5.高级配置

本章目标

  1. 缓存(了解)
  2. 延迟加载(扩展)
  3. 映射器注解(扩展)

本章内容

一、缓存

Mybatis 使用到了两种缓存:本地缓存(local cache)和二级缓存(second level cache)。

1、一级缓存

每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询结果都会被保存在本地缓存中,所以,当再次执行参数相同的相同查询时,就不需要实际查询数据库了。

注意:

  • SqlSession 是只针对查询的 ,并且一级 SqlSession 是默认开启的
  • 只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存
  • 本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空

1.1、测试方法

     @Test
     public void queryByNameAndTel() {
         try (SqlSession session = MybatisUtils.getSession()) {
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             List<Employee> list = mapper.queryByNameAndTel("赵一",null);
             list.forEach(System.out::println);
             System.out.println("--------------------");
             List<Employee> list2 = mapper.queryByNameAndTel("赵一",null);
             list2.forEach(System.out::println);
         }
     }

1.2、控制台信息

在一个SqlSession中执行了两次查询操作,只执行了一次sql

 ==> Parameters: 赵一(String)
 <==    Columns: emp_name, phone, address, salary
 <==        Row: 赵一, 13892845500, 南京市江宁区菲尼克斯路, 10000
 <==      Total: 1
 Employee [Hash = 462060764, id=null, empName=null, sex=null, age=null, birthday=null, email=null, phone=13892845500, address=南京市江宁区菲尼克斯路, salary=10000, deptId=null, jobLevelId=null, posId=null, serialVersionUID=1]
 --------------------
 Employee [Hash = 462060764, id=null, empName=null, sex=null, age=null, birthday=null, email=null, phone=13892845500, address=南京市江宁区菲尼克斯路, salary=10000, deptId=null, jobLevelId=null, posId=null, serialVersionUID=1]

2、一级缓存失效

2.1、分类

使用一级缓存失效的四种情况:

  1. 查询使用不同的 SqlSession 对象,就是不同 SqlSession 对应不同的一级缓存
  2. 同一个 SqlSession 但是查询条件不同,就是获取到数据不同
  3. 同一个 SqlSession 两次查询期间执行了任何一次增删改操作【就是在两次查询期间执行了任何一次增删改操作, MyBatis 就是将一级缓存,清空掉】
  4. 同一个 SqlSession 再次查询期间手动清空缓存 【注意是只会清空一级缓存】

这里我们就不一一测试,测试一下查询条件改变和手动清空缓存

2.2、查询条件改变

查询条件里添加电话号码,这里不走缓存

     @Test
     public void queryByNameAndTel() {
         try (SqlSession session = MybatisUtils.getSession()) {
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             List<Employee> list = mapper.queryByNameAndTel("赵一",null);
             list.forEach(System.out::println);
             System.out.println("--------------------");
             List<Employee> list2 = mapper.queryByNameAndTel("赵一","13892845500");
             list2.forEach(System.out::println);
         }
     }

2.3、手动清空缓存

调用SqlSession对象中的clearCache()方法,手动清空缓存

     @Test
     public void queryByNameAndTel() {
         try (SqlSession session = MybatisUtils.getSession()) {
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             List<Employee> list = mapper.queryByNameAndTel("赵一",null);
             list.forEach(System.out::println);
             System.out.println("--------------------");
             session.clearCache();
             List<Employee> list2 = mapper.queryByNameAndTel("赵一",null);
             list2.forEach(System.out::println);
         }
     }

3、二级缓存

二级缓存是 SqlSessionFactory 级别,通过同一个 SqlSessionFactory 创建的 SqlSession 查询到的结果会被缓存,此后若再次执行相同的查询语句,结果就会从缓存中获取

3.1、二级缓存开启条件

在核心配置文件中,可以通过 全局性地开启或关闭所有映射器配置⽂件中已配置的任何缓存。

默认就是true,⽆需设置(默认二级缓存就是开启的)!

3.2、配置SqlMapper.xml

在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加一个标签:标签

3.3、实体类实现序列化

使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝

3.4、生效时机

SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中;此时⼆级缓存才可⽤。

3.5、示例

    @Test
     public void queryByNameAndTel() {
         try (SqlSession session = MybatisUtils.getSession()) {
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             List<Employee> list = mapper.queryByNameAndTel("赵一",null);
             list.forEach(System.out::println);
         }
         System.out.println("---------------");
         try (SqlSession session = MybatisUtils.getSession()) {
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             List<Employee> list = mapper.queryByNameAndTel("赵一",null);
             list.forEach(System.out::println);
         }
     }

4、二级缓存失效的情况:【只有一种情况就是 增删改 】

两次查询之间执行了任意的 增删改 ,都会使 一级 和 二级 缓存 同时失效

注意是:手动调用SqlSession 对象中的 clearCache()方法,手动清空的缓存 ,只会清空一级缓存,对二级缓存不起作用,只有增删改对二级缓存失

5.MyBatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭或提交后,一级缓存中的数据会写入二级缓存中

二、延迟加载(扩展)

注意:只对嵌套查询方式起作用

在我们查数据时,mybatis会默认把关联的数据查出来,而关联查询比单表查询慢,并且我们有时候不需要那么多的数据。所以我们需要开启懒加载,关联的数据等需要的时候再查出来。

myBatis开启懒加载,需要在mybatis主配置文件的settings标签中配置:

  • lazyLoadingEnabled=true 即开启延迟加载
  • aggressiveLazyLoading=false即按需加载

1、懒加载的概念

MyBatis中的延迟加载,也称为懒加载,是指进行关联查询时,按需执行子查询。

当程序需要获取使用关联对象时,mybatis再执行子查询,这样可以减轻数据库的压力,在一定程度上可以降低程序运行消耗、提高查询效率。

2、懒加载的适用场景:

当前业务只使用主加载对象的其他属性,或者暂时只使用主加载对象的其他属性,长时间以后才使用主加载对象的关联对象属性。

懒加载只对关联查询起作用(一对一、一对多、多对多),且只对嵌套查询方式起作用,因为嵌套结果、扩展类的方式都是一次查询所需数据,不存在子查询,也就不存在延迟加载的情况。

MyBatis的延迟加载只是延迟执行子查询,对于主加载对象的查询都是直接执行的。

3、子查询的执行时机(关联对象的加载时机)

可在全局配置文件中进行配置,也可以在映射文件中进行配置。

3.1、配置文件

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeMapper">
 
     <resultMap id="employeeResultMap" type="employee">
         <id property="id" column="id"/>
         <result property="empName" column="emp_name"/>
         <result property="phone" column="phone"/>
         <result property="address" column="address"/>
         <result property="salary" column="salary"/>
         <collection property="trains" javaType="ArrayList" column="id" ofType="employeeTrain"
                     select="com.woniuxy.hrms.mapper.EmployeeTrainMapper.queryAllByEmployee">
         </collection>
 
     </resultMap>
     <select id="selectEmployeeByPrimaryKey" resultMap="employeeResultMap">
         select e.id,
                emp_name,
                phone,
                address,
                salary
         from employee e
 
         where e.id = #{id}
     </select>

3.2、直接加载:

默认就是,执行完主加载对象的查询,马上执行子查询。什么都不用配置,当然也可以显式配置:

 <settings>
   <setting name="lazyLoadingEnabled" value="false"/>
 </settings>

测试:

查询主对象时(Emp),直接查询关联对象

    @Test
     public void selectByPrimaryKey(){
         try(SqlSession session = MybatisUtils.getSession()){
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             Employee employee = mapper.selectEmployeeByPrimaryKey(1L);
         }
     }

3.3、侵入式延迟:

先执行主加载对象的查询,后续使用主加载对象的属性时(调用getter方法)才执行子查询。只要使用主加载对象的属性,就执行子查询

 <settings>
   <!-- 使用懒加载 -->
   <setting name="lazyLoadingEnabled" value="true"/>
   <!-- 使用侵入式延迟的懒加载 -->
   <setting name="aggressiveLazyLoading" value="true"/>
 </settings>

测试:

访问主对象的属性,则查询关联对象

   @Test
     public void selectByPrimaryKey(){
         try(SqlSession session = MybatisUtils.getSession()){
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             Employee employee = mapper.selectEmployeeByPrimaryKey(1L);
             System.out.println("-------------------------------")
             System.out.println(employee.getId());
         }
     }

3.4、深度延迟

先执行主加载对象的查询,后续使用主加载对象的关联对象属性时才执行子查询。使用的属性要是主加载对象的关联对象属性时,才执行子查询

 <settings>
   <!-- 使用懒加载 -->
   <setting name="lazyLoadingEnabled" value="true"/>
   <!-- 使用深度延迟的懒加载 -->
   <setting name="aggressiveLazyLoading" value="false"/>
 </settings>

测试:

注意:不能直接打印employee对象,因为打印对象时会调用对应的部门信息

只有访问关联对象时,才对关联对象进行查询

   @Test
     public void selectByPrimaryKey(){
         try(SqlSession session = MybatisUtils.getSession()){
             EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
             Employee employee = mapper.selectEmployeeByPrimaryKey(1L);
             //System.out.println(employee.getId());不生效
            System.out.println(employee.getTrains().size());//生效
         }
     }

4、 配置fetchType属性

也可以在映射文件的中单独配置某个关联对象的加载方式,配置 fetchType 的值,有2个可选的值:

  • eager 直接加载,默认值。eager 饥渴的。
  • lazy 深度延迟。

此种方式不能指定为侵入式延迟。

在全局配置文件中进行的配置是配置所有的,在映射文件中的配置是配置单个的。

如果在mybatis全局配置文件中显式配置了懒加载,又在映射文件中配置了懒加载,冲突时以全局配置文件中的配置为准。

5、关于 Mybatis 设置懒加载无效的问题

1、mybatis 的懒加载的设置:只需要在 mybatis 的配置文件中设置两个属性就可以了:

     <settings>
         <!-- 打开延迟加载的开关 -->
         <setting name="lazyLoadingEnabled" value="true" />
         <!-- 将积极加载改为消息加载即按需加载 -->
         <setting name="aggressiveLazyLoading" value="false" />
     </settings>

如果你只配了lazyLoadingEnabled属性,那就不行了。

官方对这两个属性的解释是:

lazyLoadingEnabled 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。默认:false

aggressiveLazyLoading 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。默认:true

三、映射器注解(扩展)

MyBatis 3 构建在全面且强大的基于 Java 语言的配置 API 之上。它是 XML 和注解配置的基础。注解提供了一种简单且低成本的方式来实现简单的映射语句。

提示: 不幸的是,Java 注解的表达能力和灵活性十分有限。尽管我们花了很多时间在调查、设计和试验上,但最强大的 MyBatis 映射并不能用注解来构建——我们真没开玩笑。而 C# 属性就没有这些限制,因此 MyBatis.NET 的配置会比 XML 有更大的选择余地。虽说如此,基于 Java 注解的配置还是有它的好处的。

示例:

     @Select("select * from employee where id = #{id}")
     Employee queryById(Long id);
posted @ 2025-04-18 09:41  icui4cu  阅读(2)  评论(0)    收藏  举报