属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。既可以在典型的java属性文件中配置这些属性,也可以在properties元素的子元素中设置。
我们可以使用properties加载一个资源文件,资源文件中可以配置各种属性。
image.png
在mybatis-config.xml配置文件中引用这个文件:
image.png
image.png
tips:如果在外部的properties中配置的属性和当前的配置文件标签下的属性重复,则外部的属性值会覆盖当前配置文件中的属性值。

设置(settings)

这是MyBatis中极为重要的调整设置,它们会改变MyBatis的运行时行为。

  1. <!-- 显示mybatis执行日志 -->
  2. <settings>
  3. <setting name="logImpl" value="STDOUT_LOGGING"/>
  4. </settings>

类型别名(typeAliases)

类型别名可以为java类型设置一个缩写的名字。它仅用于xml配置,意在降低冗余的全限定类名书写。例如:

         <typeAliases>
        <!-- 配置一个别名 -->
        <typeAlias alias="teacher" type="com.quail.pojo.Teacher"/>
        <!-- 配置一个包,这个包下的所有的类都可以直接使用类的简称(不区分首字母大小写) -->
        <package name="com.quail.pojo" />
    </typeAliases>

image.png

类型处理器(typeHandlers)

MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取到的值以合适的方式转换成java类型。

对象工厂(objectFactory)

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。

ObjectFactory 接口很简单,它包含两个创建实例用的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的。 另外,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。

插件(plugins)

MyBatis允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

    环境配置(environments)

    MyBatis可以配置成适应多环境,这种机制有助于将SQL映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生成环境需要有不同的配置;或者想在具有相同Schema的多个生成数据库中使用相同的SQL映射。
    不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

    映射器(mappers)

    用于定位sql映射语句的配置文件

    <!-- 使用相对于类路径(classpath)的资源引用 --> 
    <mappers> 
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> 
    </mappers> 
    <!-- 使用完全限定资源定位符(URL) --> 
    <mappers> 
    <mapper url="file:///var/mappers/AuthorMapper.xml"/> 
    </mappers> 
    <!-- 使用映射器接口实现类的完全限定类名 --> 
    <mappers>
    <mapper class="org.mybatis.builder.AuthorMapper"/> 
    </mappers>
    <mappers> 
    <!-- 最常用的方式 --> 
    <package name="org.mybatis.builder"/> 
    </mappers>
    

    关系查询

    一对一查询

    <resultMap id="studentMapping" type="com.quail.pojo.Student">
          <id column="stuId" property="stuId" javaType="java.lang.Long"/>
          <result column="stuName" property="stuName" />
          <result column="loginPass" property="loginPass" />
          <result column="stuGender" property="stuGender" />
          <result column="stuAge" property="stuAge" />
          <result column="stuBirth" property="stuBirth" />
          <result column="stuTel" property="stuTel" />
          <result column="stuEducation" property="stuEducation" />
          <result column="status" property="status" />
          <result column="classId" property="classId" />
          <!-- 一对一关系映射,连接查询 -->
          <association column="classId" property="classInfo" javaType="com.quail.pojo.ClassInfo" >
              <id column="classId" property="classId" />
              <result column="className" property="className" />
              <result column="roomId" property="roomId" />
              <result column="teacherId" property="teacherId" />
              <result column="headmasterId" property="headmasterId" />
              <result column="startTime" property="startTime" />
              <result column="endTime" property="endTime" />
              <result column="status" property="status" />
          </association>
      </resultMap>
    
      <select id="queryStuById" parameterType="long" resultMap="studentMapping">
          select st.*,c.className,c.roomId,c.teacherId,c.headmasterId,c.startTime,c.endTime,c.status
          from student st join classinfo c on st.classId = c.classId
          where st.stuId = #{stuId}
      </select>
    
      <resultMap id="studentMapping2" type="com.quail.pojo.Student">
          <id column="stuId" property="stuId" javaType="java.lang.Long"/>
          <result column="stuName" property="stuName" />
          <result column="loginPass" property="loginPass" />
          <result column="stuGender" property="stuGender" />
          <result column="stuAge" property="stuAge" />
          <result column="stuBirth" property="stuBirth" />
          <result column="stuTel" property="stuTel" />
          <result column="stuEducation" property="stuEducation" />
          <result column="status" property="status" />
          <result column="classId" property="classId" />
          <!-- 一对一关系映射,子查询 -->
          <association column="classId" property="classInfo" select="queryByClassId" javaType="com.quail.pojo.ClassInfo" >
              <id column="classId" property="classId" />
              <result column="className" property="className" />
              <result column="roomId" property="roomId" />
              <result column="teacherId" property="teacherId" />
              <result column="headmasterId" property="headmasterId" />
              <result column="startTime" property="startTime" />
              <result column="endTime" property="endTime" />
              <result column="status" property="status" />
          </association>
      </resultMap>
      <select id="queryByStudentId" parameterType="long" resultMap="studentMapping2">
          select * from student where stuId = #{stuId}
      </select>
      <select id="queryByClassId" parameterType="long" resultType="classInfo">
          select * from classInfo where classId = #{classId}
      </select>
    

    测试:

    @Test
      public void testStuIdByClassId(){
          Student student = sqlSession.selectOne("com.quail.dao.StudentDao.queryStuById",3l);
          System.out.println(student);
      }
    
      @Test
      public void testStuIdByClassId2(){
          Student student = sqlSession.selectOne("com.quail.dao.StudentDao.queryByStudentId",3l);
          System.out.println(student);
      }
    

    一对多查询

    <!-- 一对多关系映射 -->
      <resultMap id="classAndStudentResMapping" type="classInfo">
          <id column="classId" property="classId" />
          <result column="className" property="className" />
          <result column="roomId" property="roomId" />
          <result column="teacherId" property="teacherId" />
          <result column="headmasterId" property="headmasterId" />
          <result column="startTime" property="startTime" />
          <result column="endTime" property="endTime" />
          <result column="status" property="status" />
          <!-- 配置一对多的关系映射 -->
          <!-- property:属性名 javaType:集合类型 ofType:集合中的类型 -->
          <collection property="students" column="ClassId" javaType="java.util.List" ofType="Student">
              <id column="stuId" property="stuId" javaType="java.lang.Long"/>
              <result column="stuName" property="stuName" />
              <result column="loginPass" property="loginPass" />
              <result column="stuGender" property="stuGender" />
              <result column="stuAge" property="stuAge" />
              <result column="stuBirth" property="stuBirth" />
              <result column="stuTel" property="stuTel" />
              <result column="stuEducation" property="stuEducation" />
              <result column="status" property="status" />
              <result column="classId" property="classId" />
          </collection>
      </resultMap>
      <select id="queryClassAndStuByCid" parameterType="long" resultMap="classAndStudentResMapping">
          select st.*,c.*
          from student st join classInfo c on st.classId = c.classId
          where c.classId=#{classId}
      </select>
    
      <!-- 一对多关系,子查询 -->
      <resultMap id="classAndStudentResMapping2" type="classInfo">
          <id column="classId" property="classId" />
          <result column="className" property="className" />
          <result column="roomId" property="roomId" />
          <result column="teacherId" property="teacherId" />
          <result column="headmasterId" property="headmasterId" />
          <result column="startTime" property="startTime" />
          <result column="endTime" property="endTime" />
          <result column="status" property="status" />
          <!-- 配置一对多的关系映射 -->
          <!-- property:属性名 javaType:集合类型 ofType:集合中的类型 -->
          <collection property="students" column="classId" select="studentByIdQuery3"
                      javaType="java.util.List" ofType="Student"/>
    
      </resultMap>
      <select id="studentByIdQuery3" parameterType="long" resultType="com.quail.pojo.Student">
          select * from student where classId = #{classId}
      </select>
      <select id="classByIdQuery2" parameterType="long" resultMap="classAndStudentResMapping2">
          select * from classInfo where classId = #{classId}
      </select>
    

    测试:

    @Test
      public void testClassAndStuQuery(){
          ClassInfo classInfo = sqlSession.selectOne("com.quail.dao.StudentDao.queryClassAndStuByCid", 1l);
          System.out.println(classInfo);
          for (Student student : classInfo.getStudents()) {
              System.out.println(student);
          }
      }
    
      @Test
      public void testClassAndStuQuery2(){
          ClassInfo classInfo = sqlSession.selectOne("com.quail.dao.StudentDao.classByIdQuery2", 1l);
          System.out.println(classInfo);
          for (Student student : classInfo.getStudents()) {
              System.out.println(student);
          }
      }
    

    延迟加载

    当开启延迟加载时,所有关联对象都会延迟加载。每个延迟加载属性会按需加载。

    <settings>
     <!-- 显示mybaits执行日志 --> 
     <setting name="logImpl" value="STDOUT_LOGGING"/>
     <!-- 开启延迟加载 -->
     <setting name="lazyLoadingEnabled" value="true"/>
     <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    

    MyBatis的缓存

    一级缓存

    默认开启。
    一级缓存也称之为session级别的缓存,也就是说在一次会话中执行相同的查询时,会首先查询缓存,如果缓存没有数据,则直接查询数据库,并且给缓存放一份。sqlSession的缓存,一次会话缓存,只有查询操作有。
    所有的增删改操作会清空缓存。

    测试:**

      @Test
      public void testStuIdByClassId2(){
          Student student = sqlSession.selectOne("com.quail.dao.StudentDao.queryByStudentId",3l);
          System.out.println(student);
          // 查询两次
          Student student1 = sqlSession.selectOne("com.quail.dao.StudentDao.queryByStudentId",3l);
          System.out.println(student1);
      }
    

    结果:
    image.png
    测试增删改,清空缓存:

    @Test
    public void testStuIdByClassId2(){
      Student student = sqlSession.selectOne("com.quail.dao.StudentDao.queryByStudentId",3l);
      System.out.println(student);
      // 修改
      student.setStuName("旗木卡卡西");
      sqlSession.update("com.quail.dao.StudentDao.updateStuName",student);
    
      Student student1 = sqlSession.selectOne("com.quail.dao.StudentDao.queryByStudentId",3l);
      System.out.println(student1);
    }
    

    image.png
    其他清除缓存的操作:

  1. 提交事务sqlSession.commit();
  2. 手动清除事务sqlSession.clearCache();
  3. 关闭链接,并且开启了新的连接sqlSession.close();

    二级缓存

    二级缓存又叫sessionFatory级别的缓存。可以在两个session(事务)之间共享数据。二级缓存默认不开启,需要手动开启。
    在映射文件中手动开启二级缓存:
    <!-- 开启二级缓存,readOnly:是否只读 -->
    <cache eviction="FIFO" flushInterVal="60000" size="512" readOnly="true" />
    
    image.png

    动态SQL

    if标签

    <select id="queryByNameAndGender" parameterType="com.quail.pojo.Student" resultType="com.quail.pojo.Student">
         select * from student where 1 = 1
         <if test="stuName != null and stuName != ''">
             and stuName like '%${stuName}%'
         </if>
         <if test="stuGender != null and stuGender != -1">
             and stuGender = #{stuGender}
         </if>
     </select>
    
     @Test
     public void testSql_if(){
         Student student = new Student();
         student.setStuName("q");
         student.setStuGender(0);
         List<Student> students = sqlSession.selectList("com.quail.dao.StudentDao.queryByNameAndGender", student);
         for (Student student1 : students) {
             System.out.println(student1);
         }
     }
    

    choose/when/otherwise

    <select id="queryByNameAndGender" parameterType="com.quail.pojo.Student" resultType="com.quail.pojo.Student">
    select * from student where 1 = 1
    <choose>
     <when test="stuName != null and stuName != ''">
       and stuName like '%${stuName}%'
     </when>
     <when test="stuGender!=null and stuGender!=-1">
       and stuGender = #{stuGender}
     </when>
     <otherwise>
       <!-- 其他条件 -->
     </otherwise>
    </choose>
    </select>
    

    trim/where/set

  • where

处理where语句块的, 有了where标签,我们就不用使用“where 1 =1 ”。标签会自动判断,去掉多余的and。

<select id="queryByNameAndGender2" parameterType="student" resultMap="stuResultMapping"> 
  select * from student 
  <!-- 自动添加where语句块,并且删除多余的 “and” --> 
  <where> 
    <!-- test里面判断时,直接写属性名称 --> 
    <if test="studentName!=null and studentName!=''"> 
      and stuName like '%${studentName}%' 
    </if> 
    <if test="stuGender!=null and stuGender!=-1"> 
      and stuGender = #{stuGender} 
    </if> 
  </where>
</select>
  • set

处理update语句中的set部分。添加set字样,删除多余的“,”

<!-- set标签 --> 
<update id="updateStudent" parameterType="student"> 
  update student
  <set>
    <if test="studentName!=null and studentName!=''"> 
      stuName=#{studentName}, 
    </if> 
    <if test="loginPass!=null and loginPass!=''"> 
      longinPass=#{loginPass},
    </if>
    <if test="stuGender!=null and stuGender!=-1"> 
      stuGender=#{stuGender} 
    </if> 
  </set> where stuId=#{stuId}
</update>
  • trim

和where等价的trim:

<select id="queryByNameAndGender3" parameterType="student" resultMap="stuResultMapping"> 
  select * from student 
  <!-- prefiex:最前面添加一个where字样 --> 
  <!-- prefixOverrides:取出前面多余的(and或者or)字样 --> 
  <trim prefix="where" prefixOverrides="and | or"> 
    <!-- test里面判断时,直接写属性名称 --> 
    <if test="studentName!=null and studentName!=''">
      and stuName like '%${studentName}%' 
    </if>
    <if test="stuGender!=null and stuGender!=-1">
      and stuGender = #{stuGender} 
    </if>
  </trim> 
</select>

和set等价的trim:

<update id="updateStudent1" parameterType="student"> 
  update student 
  <!-- prefix:添加set字样 suffixOverrides:删除多余的后缀(“,”) --> 
  <trim prefix="set" suffixOverrides=","> 
    <if test="studentName!=null and studentName!=''">
      stuName=#{studentName}, 
    </if> 
    <if test="loginPass!=null and loginPass!=''"> 
      longinPass=#{loginPass}, 
    </if>
    <if test="stuGender!=null and stuGender!=-1"> 
      stuGender=#{stuGender} 
    </if>
  </trim> where stuId=#{stuId}
</update>

foreach标签

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和
索引(index)变量。

<!-- foreach标签 --> 
<select id="queryStudentByClassIds" resultMap="stuResultMapping"> 
  select * from student where classId in 
  <foreach collection="list" index="index" item="cid" separator="," open=" (" close=")"> 
    #{cid} 
  </foreach>
</select>