1、N+1问题
N+1问题主要是针对分步查询,分步查询就是使用association或collection标签中的select属性来执行另外一个 SQL 映射语句来返回预期的复杂类型,例如:
在前面的代码中(分步查询),我们所有的级联都已经成功了,但是这样会引发性能问题,就是我们查询数据时,级联的数据也会跟着全部查询出来。但是如果我们暂时只需要部门的信息,而不需要级联对象中的信息,这就会使数据库多执行几条毫无意义的SQL,导致数据库资源的损耗和系统性能的下降。而如果有多重级联的话则会更加明显,假如现在有N个级联,本来我们只要查询主数据,只要一次查询就可以了,但是由于级联的关系,其级联的数据也会跟着查询出来,后面的SQL语句还会执行N次,这样就造成了N+1问题。
以前面员工和部门的例子举例:我们本来要的是部门数据,根据部门id查询部门信息,但是由于一个部门可以有多个员工,级联数据会跟着查询出来,假如现在有N个员工,那么查询的语句如下:
本来我们只需要部门的一条数据即可,但是查询了N+1次,这是不合理的。那么为了应对N+1问题,Mybatis提供了延迟加载功能。
2、什么是延迟加载
延迟加载也叫做懒加载、惰性加载。就是我们希望一次性把常用的级联数据通过SQL直接查询出来,对于那些不想要的级联数据则不要取出,而是等待要用的时候才取出来。在mybatis中,resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
3、如何开启延迟加载
开启延迟加载有两种方式:
①、全局配置延迟加载:这种方式就是给所有的级联配置延迟加载。Mybatis默认是不开启延迟加载的,需要我们去全局配置文件中打开延迟加载。配置项
作用
配置值
默认值
lazyLoadingEnabled
全局的延迟加载开关,开启时,所有级联对象都会延迟加载
true | false
false
aggressiveLazyLoading
当启用时,有延迟加载属性的对象在被调用时将会加载该对象的所有属性。 否则,每个属性会按需加载
true | false
false 版本3.4.1之前为true
注意:这个aggressiveLazyLoading有点不好理解,下面会有介绍。
全局配置如下:
②、fetchType属性为局部配置延迟加载:这种方式需要用到fetchType属性,主要解决全局配置的缺点,因为不是所有的地方都要使用到延迟加载,fetchType出现在级联元素association和collection中,它存在两个值:eager:立即加载对应的数据。
lazy:延迟加载对应的数据。
4、全局配置实现延迟加载
以前面部门Department和员工Employee为例,一个部门可以包含多名员工的一对多关系。
①、创建员工和部门实体类:
员工实体类:
部门实体类:
②、创建员工和部门Mapper接口
EmployeeMapper:
DepartmentMapper:
③、创建员工和部门SQL映射文件
EmployeeMapper.xml
DepartmentMapper.xml
④、在Mybatis的配置文件中开启全局延迟加载
⑤、数据库连接和日志文件
db.properties:
log4j.properties:
⑥、测试代码
⑦、测试结果
(1)、只使用部门数据,这时候不需要查询员工信息
可以发现只查询了部门的信息。
(2)、需要使用员工数据,这时候因为需要获取员工数据所以要查询员工信息。
我们发现部门和员工数据都查询了,先是查询了部门的信息,然后我们又要获取员工的信息,所有后面再去查询员工的信息。
而我们在关闭延迟加载的查询时这样的,它直接给我们的数据全部都查询出来了,而不是等到我想要的时候才去查询。
5、fecthLazy实现局部延迟加载
fecthLazy实现局部延迟加载的方式配置非常简单,如下:
①、我们把Mybatis的全局配置文件中的开启延迟加载的配置删除或者注释
②、然后将我们需要设置为延迟加载的地方设置fecthLazy=lazy即可
③、执行的结果和前面的测试结果是一样的,所以我们一般推荐使用这种方式,这种方式的好处就是我哪里想要延迟加载就设置哪里即可。
6、lazyLoadingEnabled和aggressiveLazyLoading的使用lazyLoadingEnabled:主要控制延迟加载的开关。为true时表示开启延迟加载,false关闭。
aggressiveLazyLoading:true表示如果有延迟加载属性的对象在被调用时将会加载该对象的所有属性,false则表示每个属性会按需加载。
我们主要来学习一下aggressiveLazyLoading属性
(1)、aggressiveLazyLoading的属性为false,即每种属性按需加载,不调用就不加载。
运行测试方法:
运行结果:
可以看到只执行department的sql语句,并且只加载了调用的属性。
(2)、aggressiveLazyLoading设置为true:只要对这个类的任意操作将完整加载整个类的所有属性,即执行级联的SQL语句。运行同样的测试方法:
我们只调用了department对象中的getXxx方法,而并没有调用employee对象中的getXxx方法,但是mybatis却调用了查询员工SQL语句。