1、延迟加载(lazyLoadingEnable)
(1)什么是延迟加载
MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询的时候,按照设 置延迟加载规则推迟对关联对象的select检索。延迟加载可以有效的减少数据库 的压力。
注意:MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象 都是直接执行查询语句的。
(2)关联对象的加载时机
1.直接加载
2.侵入式延迟加载(aggressiveLazyLoading) 也可看做立即加载
3.深度延迟加载
直接加载:即执行对象的select语句,完成对主加载马上执行对关联对象的select查询。
侵入式延迟加载:执行对主加载对象的查询时,不会执行对关联对象的查询。但是当要访问主加载对象的详情时马上执行对关联对象的select查询。即对关联对象的执行查询,
侵入到了主加载对象的访问详情中。也可理解为:将关联对象的详情侵入到主加载对象的详情中去,即将关联对象的详情作为主加载对象的一部分出现了!
深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,
才会执行对关联对象的select查询。
注意的问题:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。因为,多表连接查询,
实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
一对多延迟加载代码:
01.实体类代码:
package cn.pb.bean; import java.util.Set; /** * 国家的实体类 */ public class Country { private Integer cId;//国家的编号 private String cName;//国家的名称 //关联省会的属性 private Set<Provincial> provincials; public Integer getcId() { return cId; } public void setcId(Integer cId) { this.cId = cId; } public String getcName() { return cName; } public void setcName(String cName) { this.cName = cName; } public Set<Provincial> getProvincials() { return provincials; } public void setProvincials(Set<Provincial> provincials) { this.provincials = provincials; } } package cn.pb.bean; /** * 省会对应的实体类 */ public class Provincial { private Integer pId; //省会的编号 private String pName; //省会名称 public Integer getpId() { return pId; } public void setpId(Integer pId) { this.pId = pId; } public String getpName() { return pName; } public void setpName(String pName) { this.pName = pName; } }
02.dao层代码:
public interface CountryDao { /** * 根据国家id 查询国家的信息 以及国家下面的省会 */ Country selectCountryById(Integer id); }
03.mapper.xml代码:
<mapper namespace="cn.pb.dao.CountryDao"> <!--01.先根据id查询出国家信息 多条sql的查询 可以使用延迟加载策略--> <select id="selectCountryById" resultMap="countryMap"> select cid,cname from country where cid=#{xxx} </select> <!--03.根据国家id 查询出省份信息 --> <select id="selectProvincialByCountryId" resultType="Provincial"> select pid,pname from provincial where countryid=#{xxx} <!--#{xxx} 对应的就是resultMap中 collection节点下面的column --> </select> <!--02.国家的映射信息--> <resultMap id="countryMap" type="Country"> <id property="cId" column="cid"/> <result property="cName" column="cname"/> <!--设置关联的集合属性 select:需要关联的查询语句 column: select关联语句中需要的参数 --> <collection property="provincials" ofType="Provincials" select="selectProvincialByCountryId" column="cid"/> </resultMap> </mapper>
04.测试代码:
public class CountryTest { CountryDao dao=null; SqlSession session=null; Logger log= Logger.getLogger(CountryTest.class); @Before public void before(){ //获取session session= SessionFactoryUtil.getSession(); //获取执行的类对象 dao=session.getMapper(CountryDao.class); } /** * 在所有的test测试方法执行之后 都要执行的操作 */ @After public void after(){ if(session!=null){ session.close(); } } <!--只输出主加载对象 只会有一条sql语句--> @Test public void testSelectCountryById(){ Country country=dao.selectCountryById(1); log.debug("根据id查询国家信息"+country); } <!--输出关联对象的信息 会有两条sql语句--> @Test public void testSelectCountryById(){ Country country=dao.selectCountryById(1); log.debug("根据id查询国家信息"+country.getProvincials()); } }
4.配置延迟加载:
在mybatis.xml文件中配置:
<settings> <!-- 全局性地启用或禁用延迟加载。当禁用时,所有关联的配置都会立即加载。 --> <setting name="lazyLoadingEnabled" value="true"/> <!--当启用后,一个有延迟加载属性的对象的任何一个延迟属性被加载时,该对象 的所有的属性都会被加载。否则,所有属性都是按需加载。 侵入式延迟 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>
二、缓存机制
缓存的作用是什么?
mybatis提供查询缓存,如果缓存中有数据就不用从数据库中获取,用于减轻数据压力,提高系统性能,
mybatis中分两个缓存,一级缓存和二级缓存,现在来讲讲这两个缓存,也同样很easy。
2.1、一级缓存
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的
过程分析
第一次查询id为1 的用户,此时先去一级缓存查找,如果查找不到,则去数据库查询,把查询后的 结果存储到一级缓存中
第二次查询id为1 的用户,此时先去一级缓存查找,如果查找到,则直接从一级缓存中把数据取出,不去查询数据库
只要中间发生增删改操作,那么一级缓存就清空
默认开启一级缓存
注意:第二步中,修改,添加,删除是对缓存中已经有的记录进行这三个操作才会把一级缓存全部清空,如果是操作的缓存中没有的数据,那么就不会清空缓存。
测试的话就不测试了,也不需要配置什么东西开启,默认开启的,如果感兴趣,那么就对其进行测试。
2.2、二级缓存
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
过程分析
第一次查询id为1 的用户,此时先去二级缓存查找,如果查找不到,则去数据库查询,把查询后的 结果存储到二级缓存中
第二次查询id为1 的用户,此时先去二级缓存查找,如果查找到,则直接从二级缓存中把数据取出,不去查询数据库
只要中间发生增删改操作,那么二级缓存就清空
二级缓存默认不开启,需要手动开启。
跟一级缓存差不多,只是二级缓存是mapper级别的,也就是每个sqlSession共享该缓存,而不是每个sqlSession独享。
一级缓存和二级缓存的作用范围图
关系:当开启了二级缓存时,那么一级缓存就失效了,大家都共享二级缓存,相当于没有一级缓存,不管干什么都是对二级缓存进行操作。这里跟hibernate的缓存有区别,不要搞混淆了。
使用mybatis自带的二级缓存
5.2.1、开启二级缓存
第一步:在全局配置文件中开启
第二步:在映射文件中开启二级缓存的开关
因为使用的是自带的,所以直接写cache即可,如果使用的是第三方缓存框架,那么这里就需要写东西了,后面会讲解到。
5.2.2、序列化对象
二级缓存中的数据,可以存储到磁盘中,因为缓存中存储的是对象,所以需要对对象进行一个序列化
不开启序列化,则会报下面的错误
5.2.3、这样二级缓存就开启了,下次在查询或者别的操作,就会使用二级缓存。
5.2.4、刷新缓存
在映射文件的statement标签,可以设置是否刷新缓存。这个要注意,
注意,这里先不看userCache这个标签。在下面会讲解到
该statement中设置flushCache=true可以刷新当前的二级缓存,默认情况下
select语句:flushCache是false,也就是默认情况下,select语句是不会刷新缓存的。
如果设置成true,那么每次查询都市去数据库查询,意味着查询的二级缓存失效
insert、update、delete语句:flushCache是true,也就是默认情况下,增删改是会刷新缓存的。
如果增删改设置为false,即使用二级缓存,那么如果在数据库中修改了数据,而缓存数据还是原来的,这个时候就会出现问题。
所以一般不用手动设置,使用默认的即可。
5.2.5、禁用缓存
该statement中设置userCache=false,可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认情况下是true,即该statement使用二级缓存