5.高级配置
本章目标
- 缓存(了解)
- 延迟加载(扩展)
- 映射器注解(扩展)
本章内容
一、缓存
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、分类
使用一级缓存失效的四种情况:
- 查询使用不同的 SqlSession 对象,就是不同 SqlSession 对应不同的一级缓存
- 同一个 SqlSession 但是查询条件不同,就是获取到数据不同
- 同一个 SqlSession 两次查询期间执行了任何一次增删改操作【就是在两次查询期间执行了任何一次增删改操作, MyBatis 就是将一级缓存,清空掉】
- 同一个 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);
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18832176