Mybatis基础学习笔记

1.基础方式的增删改查CRUD:

mybatis约定:

输入参数parameterType 和输出参数resultTyp,在形式都只能有一个,可以用集合,数组来表示

如果输入/输出参数 :是简单类型(8个基本数据类型)是可以使用任何占位符,

​ 如果是对象类型,则必须是对象的属性#{属性名}

输出参数 :如果返回值 是一个对象,则无论返回一个,还是多个

​ 在resultTyp都写成com.itt.entity.Student

​ 即 resultType="com.itt.entity.Student"

注意事项:

​ 如果使用的事务方式为jdbc, 则需要手工commit提交,即session.commit();

​ 所有的标签<select><update>等,都必须有sql语句,但是sql参数值可选

​ select * from student where stuno = #{xx}

​ sql 有参数:session.insert(statement,student);

​ sql没有参数:session.insert(statment);

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="db.properties"/>

    <!--全局参数-->
    <settings>
        <setting name="cacheEnabled" value="false"/>
    </settings>

    <!--设置单个/多个别名-->
    <typeAliases>
        <!--单个别名(别名 忽略大小写)-->
        <!--<typeAlias type="com.itt.entity.Student" alias="student"/>-->
        <!--批量定义别名,以下会自动将该包中的所有类 批量定义别名:别名就是类名(不带包名的类名)-->
        <package name="com.itt.entity"/>
    </typeAliases>

    <typeHandlers>
        <typeHandler handler="com.itt.converter.BooleanAndIntConverter"
                     javaType="boolean" jdbcType="INTEGER"/>
    </typeHandlers>

    <environments default="development">
        <environment id="development">

            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>
  • 当<transactionManager type="JDBC"/>事务的类型为JDBC时,在进行增、删、改时(也包括存储过程)必须手动的提交事务:

    session.commit();//提交事务
    

2.mapper动态代理方式的CRUD(MyBatis接口开发):

原则:约定优于配置:

  • 配置范式():

    • abc.xml
      • <name>myProject</name>
  • 硬编码方式:

    • abc.java
      • Configuration conf = new Configuration();
      • con.setName("myProject")
  • 具体实现步骤

    1. 基础环境
    2. 不同之处,
      1. 约定 目标:省略掉statement,根据约定,直接可以定位出SQL语句

    a.接口

    ​ 1.方法名和mapper.xml文件中标签的id值相同

    ​ 2.方法的输入参数和Mapper.xml文件中标签的paramerType类型一致

    ​ 3.方法的返回值和mapper.xml文件中标签的resultType一样

    除了以上约定,要实现接口中的方法和Mapper.xml中SQL标签一一对应:

    1 namespace的值 ,就是接口的全类名(接口-mapper.xml一一对应)

  • 匹配的过程:

    1.根据接口名找到mapper.xml文件(根据的是namespace==接口全类名)

    2.根据接口的方法名找到mapper.xml文件中的SQL标签(方法名=SQL标签Id)

​ 习惯:SQL映射文件(mapper.xml)和接口放在同一个包中

  • 优化:

    1. 可以将配置信息,单独放入db.properties文件中,然后再动态引入

      db.properties :

      ​ k = v

      ​<configuration>

      ​ <properties resoure="db.propertios"/>

      ​引入之后,使用${key}

    2. MyBatis全局参数

      <properties resoure="db.propertios"/>
      
      //全局参数的配置位置
      <settings>
         <setting name="" value=""/>
         <setting name="" value=""/>
      </setting>
      
      <environments default="development"
      
      
    3. 定义别名 conf.xml

      a.设置单个别名

      b.批量设置别名

      <properties resource="db.properties"/>
      
          <!--全局参数-->
          <settings>
              <setting name="cacheEnabled" value="false"/>
          </settings>
      
          <!--设置单个/多个别名-->
          <typeAliases>
              <!--单个别名(别名 忽略大小写)-->
              <!--<typeAlias type="com.itt.entity.Student" alias="student"/>-->
              <!--批量定义别名,以下会自动将该包中的所有类 批量定义别名:别名就是类名(不带包名的类名)-->
              <package name="com.itt.entity"/>
          </typeAliases>
      
  • 除了自定义别名外,MyBatis还内置了一些 常见类的别名。

3.类型处理器(类型转换器)

  1. MyBatis自带一些常见的类型处理器

    ​ int - number

  2. 自定义MyBatis类型处理器

    ​ java - 数据库(jdbc类型)

示例:

实体类Student: boolean stuSex

​ true:男

​ false:女

表student: number stuSex

​ 1:男

​ 0:女

  • 自定义类型转换器(boolean - number):步骤:

    1. 创建转换器:实现TypeHandle接口
    • 通过阅读源码发现,此接口有一个实现类BaseTypeHandler,因此,要实现转换器有两种方式:
      1. 实现TypeHandle接口
      2. 继承BaseTypeHandler类
    1. 配置conf.xml

    需要注意的问题:INTEGER

     <!--带转换器的增加-->
        <insert id="addStudentWithConverter" parameterType="student">
            insert into student(stuno,stuname,stuage,graname,stusex)
            values(#{stuNo},#{stuName},#{stuAge},#{graName},#{stuSex,javaType=boolean,jdbcType=INTEGER})
        </insert>
    //注意,values中的字段要与java类中的getter方法相对应,否则就会发生错误!!
    
  • resultMap可以实现两个功能:

    • 类型转换
    • 属性——字段的映射关系
      <resultMap id="studentMapping" type="student">
            <id property="id" column="stuno"/>
            <id property="stuName" column="stuname"/>
            <id property="stuAge" column="stuage"/>
            <id property="graName" column="graname"/>
            <id property="stuSex" column="stusex" javaType="boolean" jdbcType="INTEGER" />
        </resultMap>
    
    

4.输入参数:parameterType

(1)类型为:简单类型(8个基本类型+String)

a.#{} ${} 不同之处:

  • {任意值}

 <select id="queryStudentByStuno" parameterType="int" resultMap="studentMapping">
        select * from student where stuno = #{stuno}
 </select>
  • ${value}:标识符只能是value
 <select id="queryStudentByStuno" parameterType="int" resultMap="studentMapping">
        select * from student where stuno = ${value }
 </select> 

b.#{}:自动给String类型加上 ' ' 【自动类型轮换】

<select id="queryStudentByStuname" parameterType="string" resultMap="studentMapping">
        select * from student where stuname =  #{stuName }
    </select>
 ${}:原样输出,**但是适合于动态排序(动态字段)**
<select id="queryStudentByStuname" parameterType="string" resultMap="studentMapping">
        select * from student where stuname = '${value }'
  </select>
  • 动态排序

    select * from  student order by ${value }  desc
    

    c.#{}可以防止SQL注入

    ${}不可以

(2) 对象类型

  • {属性名}

  • ${属性名}

${}、#{}相同之处:

a.都可以 获取对象的值( 嵌套类型对象 )

模糊查询的两种方式:

i:获取对象的值

方式一、

//Mapper.xml
select * from  student where stuage = #{stuAge} or stuname like #{stuName}
//传值
Student student = new Student();
student.setStuAge(23);
student.setStuName("%z%");
List<Student>  students= studentMapper.queryStudentBystuageOrstuName(student);

方式二、

//Mapper.xml
<select id="queryStudentBystuageOrstuName" parameterType="student" resultType="student">
        select * from  student where stuage = #{stuAge} or stuname like '%${stuName}%'
</select>
//传值
Student student = new Student();
student.setStuAge(23);
student.setStuName("z");
List<Student>  students= studentMapper.queryStudentBystuageOrstuName(student);

ii:嵌套类型对象

  • Mapper.xml
//Mapper.xml
<!--输入参数为级联属性-->
    <select id="queryStudentByaddress" parameterType="student" resultType="student">
        select stuno,stuname,stuage from student where homeaddress = #{address.homeAddress} or schooladdress = '${address.schoolAddress}'
 </select>
  • Student
package com.itt.entity;

public class Student {

    private int stuNo;
    private String stuName;
    private int stuAge;
    private String graName;
    private boolean stuSex;
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

    public boolean isStuSex() {
        return stuSex;
    }

    public void setStuSex(boolean stuSex) {
        this.stuSex = stuSex;
    }


    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }

    public int getStuAge() {
        return stuAge;
    }

    public void setStuAge(int stuAge) {
        this.stuAge = stuAge;
    }

    public String getGraName() {
        return graName;
    }

    public void setGraName(String graName) {
        this.graName = graName;
    }

    public Student() {

    }

    public Student(int stuNo, String stuName, int stuAge, String graName) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.stuAge = stuAge;
        this.graName = graName;
    }

    public Student(int stuNo, String stuName, int stuAge, String graName, boolean stuSex) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.stuAge = stuAge;
        this.graName = graName;
        this.stuSex = stuSex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "stuNo=" + stuNo + ", stuName='" + stuName + '\'' +", stuAge=" + stuAge +", graName='" + graName + '\'' +
                ", stuSex=" + stuSex +
                '}'+"\n";
    }
}
  • Address
package com.itt.entity;

public class Address {

    private String homeAddress;
    private String schoolAddress;


    public String getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(String homeAddress) {
        this.homeAddress = homeAddress;
    }

    public String getSchoolAddress() {
        return schoolAddress;
    }

    public void setSchoolAddress(String schoolAddress) {
        this.schoolAddress = schoolAddress;
    }

    public Address(String homeAddress, String schoolAddress) {
        this.homeAddress = homeAddress;
        this.schoolAddress = schoolAddress;
    }

    public Address() {
    }

    @Override
    public String toString() {
        return this.homeAddress +","+this.schoolAddress;
    }
}
  • StudentMapper
public interface StudentMapper {
  ...
  List<Student> queryStudentByaddress(Student address);
  ....
}
  • Test
public void Test{
    //根据地址查学生
    public static void queryStudentByaddress() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        Student student = new Student();
        Address address = new Address();
        address.setHomeAddress("xa");
        address.setSchoolAddress("x");
        //学生里面包含着地址
        student.setAddress(address);

        List<Student>  students= studentMapper.queryStudentByaddress(student);

        System.out.println(students);
        session.close();
    }
}
  • 结果
[Student{stuNo=1, stuName='zs', stuAge=23, graName='null', stuSex=false}
, Student{stuNo=2, stuName='kk', stuAge=23, graName='null', stuSex=false}
, Student{stuNo=3, stuName='cv', stuAge=10, graName='null', stuSex=false}
, Student{stuNo=22, stuName='jz', stuAge=15, graName='null', stuSex=false}
]

输入对象为HashMap:

where stuage = #{stuAge}

用map中key的值匹配 占位符#{stuAge},如果匹配成功,应用map的value替换占位符。

  • studentMapper.xml
 <select id="queryStudentBystuageOrstuNameWithHashMap" parameterType="HashMap" resultType="student">
        select * from  student where stuage = #{stuAge} or stuname like '%${stuName}%'
    </select>
  • StudentMapper
public interface StudentMapper{
    ...
    List<Student> queryStudentBystuageOrstuNameWithHashMap(Map<Object,Object> map);
    ...
}
  • Test
//查询全部学生,根据年龄或姓名(输入参数为:HashMap)
    public static void queryStudentBystuageOrstuNameWithHashMap() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        Map<Object, Object> studentMap = new HashMap<>();
        studentMap.put("stuAge",23);
        studentMap.put("stuName","jz");
        List<Student>  students= studentMapper.queryStudentBystuageOrstuNameWithHashMap(studentMap);

        System.out.println(students);
        session.close();
    }

5.mybatis调用存储过程:

mapper.xml ->mapper接口 ->测试方法

  • 存储过程:queryCountByGradeWithProcesure
DELIMITER $$

CREATE
    PROCEDURE `test`.`queryCountByGradeWithProcesure`(IN gName VARCHAR,OUT scount INT)
    BEGIN
    SELECT COUNT(1) INTO scount FROM student WHERE graname = gName;
    END$$

DELIMITER ;

CALL queryCountByGradeWithProcesure('g1');

  • studentMapper.xml
<!--通过调用存储过程实现查询
    CALLABLE:存储过程
    存储过程的输入参数,在mybatis用Map来传递(HashMap)
-->
<select id="queryCountByGradeWithProcesure" statementType="CALLABLE" parameterType="HashMap">
  {
    CALL queryCountByGradeWithProcesure(
        #{gName,jdbcType=VARCHAR,mode=IN},
        #{sCount,jdbcType=INTEGER,mode=OUT}
    )
  }
</select>
  • StudentMapper【接口】
//根据存储过程返回某个年级的学生总数
void queryCountByGradeWithProcesure(Map<Object,Object> params);
  • Test
//根据存储过程返回某个年级的学生总数
    public static void queryCountByGradeWithProcesure() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        Map<Object,Object> params = new HashMap<>();
        params.put("gName","s2");//指定存储过程中gName的值为s1

        //调用存储过程,并传入输入参数
        studentMapper.queryCountByGradeWithProcesure(params);

        //获取存储过程的输出参数
        Object count = params.get("sCount");

        System.out.println(count);
        session.close();
    }

在使用时,通过HashMap的put方法传入输入参数的值;通过HashMap的get方法获取输出参数的值。要注意Jar包问题。

  • 结果
"D:\Program Files\Java\jdk1.8.0_131\bin\java.exe" .....
3

Process finished with exit code 0

如果报错:No enum constant org.apache.ibatis.type.JdbcType.xxx

则说明mybatis不支持这个类型,需要查表:Mybatis中javaType和jdbcType对应关系表

注:存储过程中无论输入参数是什么值,语法上都需要用Map来传递该值;

6.输出参数:resultType

1.简单类型(8个基本类型+String)

2.输出参数为实体对象类型

3.输出参数为实体对象类型的集合:虽然输出类型为集合,但是【注意】

  • 在映射文件中:resultType依然写集合的元素类型不是集合类型(resultType="Student")
  • 在接口中仍然写的是List<Student>

4.输出类型为HashMap

​ ------HashMap本身就是一个集合,可以存放多个元素,但是根据 提示返回值为HashMap时,查询的结果只能是1学生(no,name );

--->结论:一个HashMap对应一个学生的多个元素(多个属性)【一个map一个学生】

//StudentMapper.xml
<!--通过别名指定Map的key-->
  <select id="queryStudentOutByHashMap" resultType="HashMap">
      select stuno "no",stuname "name" from student where stuno = 2
  </select>
//StudentMapper【interface】
HashMap<Object,Object> queryStudentOutByHashMap();
//Test
//查询一个学生,结果为map类型
public static void queryStudentOutByHashMap() throws Exception{
    //Connection - SqlSession操作myBatis

    //config.xml  -> reader
    Reader reader = Resources.getResourceAsReader("conf.xml");

    //reader  ->SqlSession
    SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
    SqlSession session = sessionFactory.openSession();


    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
    HashMap<Object, Object> studentMap = studentMapper.queryStudentOutByHashMap();

    System.out.println(studentMap);
    session.close();
}

可以将Map放入到List中,然后再打印就可以得到所有的记录了

//StudentMapper.xml
<select id="queryAllStudentsOutByHashMap" resultType="HashMap">
    select stuno "no",stuname "name" from student
</select>
//StudentMapper【interface】
List<HashMap<Object,Object>> queryAllStudentsOutByHashMap();
//Test
//查询多个学生,结果为map类型
    public static void queryAllStudentsOutByHashMap() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<HashMap<Object, Object>> studentMap = studentMapper.queryAllStudentsOutByHashMap();

        System.out.println(studentMap);
        session.close();
    }

resultType:

resultMap:实体为的属性、数据表的字段:类型、名字不同时

注意:当属性名 和字段名 不一致时,除了使用resultMap以外,还可以使用resultType+HashMap

a.resultMap

<select id="queryStudentById" parameterType="int" resultMap="queryStudentByIdMap">
    select id,name from student where id = #{id}
</select>
<resultMap id="queryStudentByIdMap" type="student">
    <!--指定类中的属性和表中的字段对应关系-->
    <id property="stuNo" column="id" />
    <result property="stuName" column="name" />
</resultMap>

b.resultType+HashMap

select  表的字段名 "类的属性名"  from.....来指定字段名和属性名的对应关系
<select id="queryStudentByIdWithHashMap" parameterType="int" resultType="student">
    select id "stuNo",name "StuName" from student where id = #{id}
</select>

注意:如果有10个字段,但发现某一个字段结果始终为默认值(0,0.0,null),则可能是表的字段和类的属性名写错了。

7.用mybatis实现动态SQL

mybatis提供了<if>、<where>、<foreach>等标签来实现SQL语句的动态拼接。

  • 动态SQL的写法(第一种)
<select id="queryStuByNOrAWithSQLTag" parameterType="student" resultType="student">
    select stuno,stuname,stuage from student where 1=1  //永远成立
    <!-- <if test="student有stuname属性且不为空">-->
    <if test="stuName != null and stuName != '' ">
        and stuname = #{stuName}
    </if>
    <if test="stuAge != null and stuAge != 0">
        and stuage = #{stuAge}
    </if>
</select>   
  • 第二种(<where>标签会自动处理第一个<if>标签的and 语句,但不会处理之后<if>中的and)
<select id="queryStuByNOrAWithSQLTag" parameterType="student" resultType="student">
    select stuno,stuname,stuage from student
    <where>
        <!-- <if test="student有stuname属性且不为空">-->
        <if test="stuName != null and stuName != '' ">
            and stuname = #{stuName}
        </if>
        <if test="stuAge != null and stuAge != 0">
            and stuage = #{stuAge}
        </if>
    </where>
</select>

<foreach>

8.查询学号为1、3、5的学生信息

select stuno,stuname from student where stuno in (1,3,5);

<foreach>迭代的类型:数组、对象数组、集合、属性(Grade类:List<Integer> ids)

1.属性(Grade类:List<Integer> ids)

<!--将多个元素放入到对象的属性中-->
<select id="queryStudentsWithNOsInGrade" parameterType="grade" resultType="student">
    select * from student
    <!-- 
    collection="stuNos":指的是grade中的元素
    open=" and stuno in (":指的是语句的左半部分的语句
    close=")":为语句的右半部分的语句
    item="stuNo"为指定的别名,指的是集合中的每个元素
    separator=",":指定的是in(1,2,4)之间是用","进行分隔的
        -->
    <where>
      <if test="stuNos != null and stuNos.size > 0">
          <foreach collection="stuNos" open=" and stuno in (" close=")" item="stuNo" separator=",">
              #{stuNo}
          </foreach>
      </if>
    </where>
</select>
//Grade
public class Grade {
    //学号
    private List<Integer> stuNos;

    public List<Integer> getStuNos() {
        return stuNos;
    }

    public void setStuNos(List<Integer> stuNos) {
        this.stuNos = stuNos;
    }
}
//StudentMapper
List<Student> queryStudentsWithNOsInGrade(Grade grade);
//查询指定学号对应的学生
public static void queryStudentsWithNOsInGrade() throws Exception{
    //Connection - SqlSession操作myBatis

    //config.xml  -> reader
    Reader reader = Resources.getResourceAsReader("conf.xml");

    //reader  ->SqlSession
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession session = sessionFactory.openSession();


    StudentMapper mapper = session.getMapper(StudentMapper.class);
    
    Grade grade = new Grade();

    List<Integer> stuNos = new ArrayList<>();
    stuNos.add(1);
    stuNos.add(3);
    stuNos.add(5);

    grade.setStuNos(stuNos);

    List<Student> student = mapper.queryStudentsWithNOsInGrade(grade);
    System.out.println(student);

    session.close();
}
//结果为
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=5, stuName='ww', stuAge=20, graName='null', stuSex=false}
]

2.简单类型的数组

  • 无论编写代码时,传递的是什么参数名(stuNos),在Mapper.xml中,必须用array代替该数组。
//StudentMapper.xml
<!--将多个元素放入到数组中 int[] stuNos -->
    <select id="queryStudentsWithArray" parameterType="int[]" resultType="student">
        select * from student
        <where>
            <!--数组中传过来的参数不论名称是什么,统一默认为array-->
            <if test="array != null and array.length ">
                <foreach collection="array" open=" and stuno in(" close=")" item="stuNO" separator=",">
                    #{stuNO}
                </foreach>
            </if>
        </where>

    </select>
//查询指定学号对应的学生使用数组
public static void queryStudentsWithArray() throws Exception{
  //Connection - SqlSession操作myBatis

  //config.xml  -> reader
  Reader reader = Resources.getResourceAsReader("conf.xml");

  //reader  ->SqlSession
  SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
  SqlSession session = sessionFactory.openSession();


  StudentMapper mapper = session.getMapper(StudentMapper.class);

  //传入一个int类型的数组
  int[] stuNos = {1,3,5};
  List<Student> student = mapper.queryStudentsWithArray(stuNos);
  System.out.println(student);

  session.close();
}
//StudentMapper
List<Student> queryStudentsWithArray(int[] stuNos);
//运行结果
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=5, stuName='ww', stuAge=20, graName='null', stuSex=false}
]

3.集合

无论编写代码时,传递的是什么参数名(stuNos),在Mapper.xml中,必须用list代替该数组。

<!--将多个元素放入到集合中 list<Integer> stuNos = {1,3,5}-->
<select id="queryStudentWithList" parameterType="list" resultType="student">
    select * from student
    <where>
        <if test="list != null and list.size > 0">
            <foreach collection="list" open=" and stuno in(" close=")" item="stuNo" separator=",">
                #{stuNo}
            </foreach>
        </if>
    </where>
</select>
List<Student> queryStudentWithList(List<Integer> stuNOs);
//查询指定学号对应的学生使用List集合
public static void queryStudentWithList() throws Exception{
    //Connection - SqlSession操作myBatis

    //config.xml  -> reader
    Reader reader = Resources.getResourceAsReader("conf.xml");

    //reader  ->SqlSession
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession session = sessionFactory.openSession();

    StudentMapper mapper = session.getMapper(StudentMapper.class);

    List<Integer> stuNos = new ArrayList<>();
    stuNos.add(1);
    stuNos.add(3);
    stuNos.add(5);
    List<Student> student = mapper.queryStudentWithList(stuNos);
    System.out.println(student);

    session.close();
//运行结果
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=5, stuName='ww', stuAge=20, graName='null', stuSex=false}
]

4.对象数组

Student[ ] students = {student0,student1,student2}; 每个student包含一个学号属性

  • 注意的几点 :parameterType="Object[]"
<!--将多个元素放入到对象数组中 Student[ ]  students = {student0,student1,student2}-->
<select id="queryStudentWithObjectArray" parameterType="Object[]" resultType="student">
    select * from student
    <where>
        <if test="array != null and array.length > 0">
            <foreach collection="array" open=" and stuno in (" close=")" item="student"             separator=",">
                #{student.stuNo}
            </foreach>
        </if>
    </where>
</select>
List<Student> queryStudentWithObjectArray(Student[] students);
//查询指定学号对应的学生使用对象数组
public static void queryStudentWithObjectArray() throws Exception{
    //Connection - SqlSession操作myBatis

    //config.xml  -> reader
    Reader reader = Resources.getResourceAsReader("conf.xml");

    //reader  ->SqlSession
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession session = sessionFactory.openSession();


    StudentMapper mapper = session.getMapper(StudentMapper.class);

    Student student1 = new Student();
    student1.setStuNo(1);
    Student student2 = new Student();
    student2.setStuNo(2);
    Student student3 = new Student();
    student3.setStuNo(3);

    Student[] stus = new Student[]{student1,student2,student3};
    List<Student> student = mapper.queryStudentWithObjectArray(stus);
    System.out.println(student);

    session.close();
}
//运行结果为
[Student{stuNo=1, stuName='lxs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=2, stuName='zs', stuAge=0, graName='null', stuSex=false}
, Student{stuNo=3, stuName='ls', stuAge=0, graName='null', stuSex=false}
]

9.SQL片段:

  • java:方法

  • 数据库:存储过程、存储函数

  • Mybatis:SQL片段

a.提取相同的代码

b.引用

<sql id="ObjectArrayStunos">
        <where>
            <if test="array != null and array.length > 0">
                <foreach collection="array" open=" and stuno in (" close=")" item="student" separator=",">
                    #{student.stuNo}
                </foreach>
            </if>
        </where>
</sql>
              
<!--将多个元素放入到对象数组中 Student[ ]  students = {student0,student1,student2}-->
<select id="queryStudentWithObjectArray" parameterType="Object[]" resultType="student">
    select * from student
    <!-- 如果sql片段和引用处不在同一个文件中,则需要在refid 引用时加上namespace:namespace.id
    <include refid="com.itt.mapper.abcMapper.ObjectArrayStunos"></include>
    -->
    <include refid="ObjectArrayStunos"></include>
</select>

10.关联查询

10.1 一对一:

a.业务扩展类

​ * 核心:用resultType指定类的属性包含 多表查询的所有字段
* 思路:


代码:

//StudentMapper.xml  
<select id="queryStudentByNoWithOO" parameterType="int" resultType="StudentBusiness">
        select s.*,c.* from student s inner join studentcard c
        on s.cardid = c.cardid
        where s.stuno = #{stuNo}
    </select>
//StudentMapper
StudentBusiness queryStudentByNoWithOO(int stuno);
//Test
public static void queryStudentByNoWithOO() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        StudentBusiness studentBusiness = studentMapper.queryStudentByNoWithOO(2);

        System.out.println(studentBusiness);
        session.close();
    }
  • 结果


b.resultMap

  • 1通过 属性成员 将2个类建立起联系
  • 2
//StudentMapper.xml
<select id="queryStudentByNoWithOO2" parameterType="int" resultMap="student_card_map">
        select s.*,c.* from student s inner join studentcard c
        on s.cardid = c.cardid
        where s.stuno = #{stuNo}
    </select>

    <resultMap id="student_card_map" type="student">
        <!--学生的信息-->
        <id property="stuNo" column="stuNo"/>
        <result property="stuName" column="stuName"/>
        <result property="stuAge" column="stuAge"/>
        <!-- 一对一,对象成员使用association映射
            javaType指定该属性是什么类型的
        -->
        <association property="card" javaType="StudentCard">
            <id property="cardId" column="cardId"/>
            <result property="cardInfo" column="cardInfo"/>
        </association>
    </resultMap>
  //StudentMapper
  Student queryStudentByNoWithOO2(int stuno);
  //Test
  //一对一映射使用resultMap方式实现
    public static void queryStudentByNoWithOO2() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        Student student = studentMapper.queryStudentByNoWithOO2(1);

        System.out.println(student);
        session.close();
    }

注意:
一对一:association
一对多:collection

  • 结果:


10.2 一对多:

  • 表:student studentclass(关联:classid)

  • 类:student studentClass(关联:List<Student> students)
    注意:
    在描述students属性类型:javaType=" "

    在描述该属性的元素类型为:ofType=" "

    //一对多
    <collection property="students" ofType="student">
    
    </collection>
    
    List<Student> students; 
    
  • 代码实现过程

package com.itt.entity;

public class Student {

    //只用这三个字段
    private int stuNo;
    private String stuName;
    private int stuAge;
    
    
    private String graName;
    private boolean stuSex;
    private StudentCard card;

    public StudentCard getCard() {
        return card;
    }

    public void setCard(StudentCard card) {
        this.card = card;
    }
    //    private Address address;

//    public Address getAddress() {
//        return address;
//    }

//    public void setAddress(Address address) {
//        this.address = address;
//    }

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

    public boolean isStuSex() {
        return stuSex;
    }

    public void setStuSex(boolean stuSex) {
        this.stuSex = stuSex;
    }


    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }

    public int getStuAge() {
        return stuAge;
    }

    public void setStuAge(int stuAge) {
        this.stuAge = stuAge;
    }

    public String getGraName() {
        return graName;
    }

    public void setGraName(String graName) {
        this.graName = graName;
    }

    public Student() {

    }

    public Student(int stuNo, String stuName, int stuAge, String graName) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.stuAge = stuAge;
        this.graName = graName;
    }

    public Student(int stuNo, String stuName, int stuAge, String graName, boolean stuSex) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.stuAge = stuAge;
        this.graName = graName;
        this.stuSex = stuSex;
    }

    @Override
    public String toString() {
        return "stuNo"+"="+this.stuNo+","+"stuName"+"="+this.stuName+","+"stuAge"+"="+
                this.stuAge+","+"CardId"+"="+this.card.getCardId()+","+"CardInfo"+
                "="+this.card.getCardInfo();
    }
}

package com.itt.entity;

import java.util.List;

public class StudentClass {
    private int classId;
    private String className;

    //增加学生属性(通过该字段让Student类和StudentClass类建立起关联)
    List<Student> students;

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public int getClassId() {
        return classId;
    }

    public void setClassId(int classId) {
        this.classId = classId;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }
}

//StudentMapper.xml
<!--建立一对多联系-->
    <select id="queryClassAndStudents" parameterType="int" resultMap="class_student_map" >
      <!--查询g1班的班级信息,和g1班的所有学生信息-->
      select c.*,s.* from student s
        inner join studentclass c
        on s.classid = c.classid
        where c.classid = #{classId}
    </select>
    <!--类-表的对应关系-->
    <resultMap id="class_student_map" type="StudentClass">
        <!--因为type的主类是班级,因此先配置班级的信息-->
        <id property="classId" column="classId"/>
        <result property="className" column="className"/>
        <!--配置成员属性学生,一对多-->
        <collection property="students" ofType="student">
            <id property="stuNo" column="stuNo"/>
            <result property="stuName" column="stuName"/>
            <result property="stuAge" column="stuAge"/>
        </collection>
    </resultMap>
//studentMapper
StudentClass queryClassAndStudents(int ClassId);
    //一对一映射使用resultMap方式实现
    public static void queryClassAndStudents() throws Exception{
        //Connection - SqlSession操作myBatis

        //config.xml  -> reader
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //reader  ->SqlSession
        SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();


        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        //班级
        StudentClass studentClass = studentMapper.queryClassAndStudents(1);
        System.out.println("ClassId="+studentClass.getClassId()+","+"ClassName="+studentClass.getClassName());

        List<Student> students = studentClass.getStudents();
        for (Student student : students) {
            System.out.println(student.getStuNo()+","+student.getStuName()+
                    ","+student.getStuAge());
        }

        session.close();
    }

11.Log4j: 使用Maven

  1. 添加依赖:
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 开启日志,conf.xml
<settings>
    <!--开启日志,并指定使用的具体日志-->
    <setting name="LogImpl" value="LOG4J"/>
 </settings>

如果不指定,Mybatis就会根据以下顺序寻找日志

  1. 编写配置日志输出文件

    lgo4j.properties

    • 内容
log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender 
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

日志级别:

DEBUG < INFO < WARN < ERROR

如果设置为info,则只显示info 及以上级别的信息;

建议:在开发时设置debug,在运行时设置为info或以上。

可以通过日志信息,详细的阅读mybatis执行情况(观察mybatis实际执行sql的语句以及参数和结果)

12.延迟加载

12.1 一对一:学生,学生证

  • mybatis中使用延迟加载,需要先配置:
<settings>
    <!--开启日志,并指定使用的具体日志-->
    <setting name="logImpl" value="LOG4J"/>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--关闭立即加载-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

注意:
如果添加了mapper.xml文件则我们需要在主配置文件conf.xml中添加新增的mapper.xml文件。

  • 通过debug可以发现,如果程序只需要学生,则只向数据库发送了查询学生的SQL;
  • 当我们后续 需要用到学生证的时候,再第二次发送 查询学生证的SQL

12.2 一对多

  • 和一对一的延迟加载配置相同

延迟加载的步骤:先查班级,按需查询学生

  1. 开启延迟加载conf.xml配置settings

  2. 配置mapper.xml

​ 2.1 写2个Mappper文件:

  • 班级mapper.xml
<!--建立一对多联系:带延迟加载-->
    <select id="queryClassAll" resultMap="class_student_LazyLoad"               resultType="StudentClass" >
        <!--查询g1班的班级信息,和g1班的所有学生信息-->
        select * from studentclass
    </select>
    <!--类-表的对应关系-->
<resultMap id="class_student_LazyLoad" type="studentClass">
    <!--因为type的主类是班级,因此先配置班级的信息-->
    <id property="classId" column="classId"/>
    <result property="className" column="className"/>
    <!--配置成员属性学生,一对多-->
    <collection property="students" select="com.itt.mapper.StudentCardMapper.queryCardByClassId"
                column="classid">
        <!--<id property="stuNo" column="stuNo"/>-->
        <!--<result property="stuName" column="stuName"/>-->
        <!--<result property="stuAge" column="stuAge"/>-->
    </collection>
</resultMap 
  • 即查询学生的sql是通过select 属性指定,并且通过column指定外键

  • 学生mapper.xml

<!--再根据班级查询学生-->
<select id="queryCardByClassId" parameterType="int" resultType="student">
    select * from student where classid = #{classid}
    <!-- com.itt.mapper.StudentCardMapper.queryCardByClassId-->
</select>

13.查询缓存

13.1一级缓存:同一个SqlSession对象

  • MyBatis默认开启一级缓存,如果用同样的SqlSession查询相同的数据,则只会在第一次查询时向数据库发送sql语句,并将查询的结果放入到SqlSession(作为缓存存在);后续再次查询该同样的对象时,则直接从缓存中查询该对象即可(即省略了数据库的访问)。

13.2二级缓存

  • MyBatis默认情况下没有开启二级缓存,需要手工打开。
    1. config.xml

      <setting name="cacheEnabled" value="true"/>
      
    2. 在具体的mapper.xml中声明开启(studentMappper.xml)

      <mapper namespace="com.itt.mapper.StudentMapper">
          <!--声明此namespace开启二级缓存-->
          <cache/>
      

   根据 异常提示:NotSerializableException可知,MyBatis是将对象放入到硬盘文件中。
       ------->序列化:内存----->硬盘

       ------->反序列化:硬盘 ----->内存
  • 准备缓存的对象,必须实现了序列化接口(如果开启了缓存 namespace="com.itt.mapper.StudentMapper"),可知序列化对象为Student,因此要将Student对象序列化(序列化Stuent类,以及Student的级联属性和父类)

注意:触发将对象写入二级缓存的时机:SqlSession对象的close()方法。

  • Mybatis自带二级缓存:【同一个namespace】生成的mapper对象

    • 回顾:namespace的值,就是接口的全类名(包名.类名),通过接口可以生成动态代理对象(studentMapper对象)

      • ---------->namespace决定了studentMapper对象的产生

        结论:只要产生的xxxMapper对象来自于同一个namespace,则这些对象共享二级缓存

    • 注意:二级缓存的范围是同一个namespace,如果有多个xxxMapper.xml的namespace值相同,则通过这些xxxMapper.xml产生的xxxMapper对象,仍然共享二级缓存。

  • 禁用:select标签中userCache="false"

  • 清理:

    1. 执行commit();一级二级一样都使用这个(执行增删改时,会清理缓存;设计的原因是为了防止脏数据的产生)

      在二级缓存 中,commit()不能是查询自身的commit。

      commit会清理一级和二级缓存:但是清理二级缓存时,不能是查询自身的commit;

    2. 在select标签中增加属性:flushCache="true"

  • 第三方提供的二级缓存:

    echcache、memcache

    • 要想整合三方提供的二级缓存(或者自定义缓存),必须实现Cache接口,该接口的默认实现类为PerpetualCache

    • 整合echcache二级缓存:

    1. 导入Maven依赖:

    <!--mybatis使用ehacche-->
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.0.3</version>
    </dependency>
    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
      <version>2.6.10</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>
    

    2. 编写ehcache配置文件Ehcache.xml

     ```java
     <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
      <diskStore path="F:\Ehcache"/>
      <defaultCache
       maxElementsInMemory="1000"
       maxElementsOnDisk="1000000"
       eternal="false"
       overflowToDisk="false"
       timeToIdleSeconds="100"
       timeToLiveSeconds="100"
       diskExpiryThreadIntervalSeconds="120"
       memoryStoreEvictionPolicy="LRU">
      </defaultCache>
     </ehcache>
    

    3. 开启EhCache二级缓存

     在xxxMapper.xml中开启
     ```java
     <cache type="org.mybatis.caches.ehcache.EhcacheCache">
             <!--自定义Ehcache中的全局参数-->
             <property name="" value=""/>
         </cache>
     ```
    

14.逆向工程

  • 表、类、接口、mapper.xml四者密切相关,因此,当知道一个的时候,其他三个应该可以自动生成。

    表-------->其他三个

  • 实现步骤:

    1. 添加Maven依赖
 <dependency>
        <groupId>org.mybatis.generator</groupId>
         <artifactId>mybatis-generator-core</artifactId>
         <version>1.3.2</version>
     </dependency>
    <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.4.6</version>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.6</version>
        </dependency>
        <dependency>
          <groupId>c3p0</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 前言 MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL...
    AI乔治阅读 635评论 0 5
  • 1、什么是Mybatis? (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只...
    vannesspeng阅读 417评论 0 0
  • 1、什么是Mybatis? (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只...
    月哥说了算阅读 453评论 0 0
  • 1.什么是Mybatis? 1、Mybatis 是一个半 ORM对象关系映射框架,内部封装了JDBC,开发时只需要...
    奇点一氪阅读 390评论 0 6
  • 人啊!有很多时候是在忙碌中行走奔波着。在某一天突然发现,自己行走的踪迹时那样的模糊又毫无意义。无所适从的茫...
    偷闲躲静阅读 195评论 0 0