一 延迟操作
多表关联查询
1.什么是延迟操作
延迟操作只用与多变关联查询(且必须是将几次查询分开的),实现的效果是:当我们访问一个该bean对象的一般属性时,并没有涉及到第二次查询,则只会执行第一次查询,而当我们需要访问到第二次查询得到的信息时,才会执行第二次查询
2.区别所在:
普通查询时,当你执行时,就会将两次查询的全部执行,并将信息保存到bean对象中;而延迟查询只会执行第一次查询,只有等你访问第二次查询得到的信息时才会执行第二次查询
3.作用:
会减少一些不必要的查询,或者我可以把它叫做虚拟查询(自己恶趣的称谓,只是觉得与虚拟内存相似,别当真)。
4.设置
在MyBatis中,延迟加载是默认关闭的,需要在配置文件的Setting中开启就可以完成延迟加载
5.执行
执行结果
二 一级缓存
1.什么是缓存
当我们执行一个查询语句时,会得到一个查询结果,而这个结果会先进入缓存中,在 将结果返回出去,但当我们下次再次使用这个方法时(且sql语句也相同),我们可以直接在缓存中获取(当缓存中依旧有这个查询结果时),而不必再次执行SQL语句。
2.缓存的好处
可以减小服务器压力,减少一些SQL语句查询,同样也避免产生大量的查询结果冗余
3.缓存的约束
(1)必须是同一个Mapper配置文件下同一个方法,即使是SQL语句相同但是方法名不同的一样不可以
(2)缓存存在生命周期(当很长时间不使用缓存中数据时,会被清空),所以必须要在缓存的声明周期内才可以使用缓存。
4.一级缓存的特点
(1)一级缓存属于SqlSession的对象,对象存在于BaseSqlSession类中(有兴趣的可以去查看下),对象名为PerpetualCache(永久缓存),这里的永久并非指实际是的永久,而是这个缓存不会被随着时间被清除掉,页不能关闭一级缓存, 只会随着clearCache方法清除掉或者被close方法给置为null值。
(2)总结下第一点:同一个SqlSession对象共享一个一级缓存;一级缓存无法手动关闭也不随时间而被清除;commit和close都会清除掉缓存但是性质不一样(Commit是清除缓存内容,但并不会讲缓存这个对象给清除掉,实际是是将缓存刷新到二级缓存中(前提是开启二级缓存时),而Close方法则是直接将缓存对象置为null,因为SqlSession被关闭后需要释放掉所有的内部对象,便于被JVM的GC给清除掉,而commit的目的在于将事务提交,原因在于事务中的一些数据实际上存在伪数据,而提交完成后的数据才是真实数据,这些伪数据被其他事务访问时会产生一些错误)。
5.一级缓存的表现:
(1)查询方法:
(2)不使用commit时:
执行结果:(会直接去一级缓存中寻找查询结果,不会执行SQL语句)
(3)使用commit提交事务后:
执行结果:(会再次执行SQL语句)
6.一级缓存的源码分析
地址:-------------------------------------还没写好
三 二级缓存
1.二级缓存与一级缓存区别
(1)二级缓存之所以与一级缓存共存是因为二级缓存与一级缓存的作用域不同,一级缓存针对于同一个SqlSession,而二级缓存则是针对于同一个Mapper映射文件存在,所以二级缓存的作用域更加广泛,不过很多情况下二级缓存并不需要(也就是作用域太大的原因导致的),重点:这里同一个Mapper不仅仅指代同一个Mapper文件产生的,还必须是同一个SqlSessionFactory创建出来的!所以必修使用单例模式来确保SqlSessionFactory是相同的。
(2)一级缓存与二级缓存都是默认开启的,不过二级缓存可以在配置文件中关闭(cacheEnabled),而且二级缓存需要再在Mapper中再开启一次,才能在当前Mapper中使用,另外当当前Mapper开启二级缓存后,某个方法不希望使用时可以使用在方法内使用useCache=“false”来关闭当前方法的二级缓存。
(3)当执行器执行需要访问缓存时,会优先访问一级缓存在访问二级缓存。所以我们需要实验二级缓存时,需要使用close或者commit清空掉一级缓存,另外当一级缓存被清空后会将缓存信息提交给二级缓存。
(4)二级缓存不同于一级缓存,可以做很多设置比如readOnly,flushInterval来设置刷新间隔等设置。
2.二级缓存的属性信息
(1)readOnly:
readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过发序列化)。这会慢一些,但是安全,因此默认是false。
(2)size:
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024.
(3)flushInterval:
flushInterval(刷新间隔)可以被设置为任意的正整数(60*60*1000这种形式是不允许的),而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
(4)eviction(回收算法)
【默认】LRU——最近最少使用的:移除最长时间不被使用的对象
FIFO——先进先出的:按对象进入缓存的顺序来移除他们
SOFT——软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK——弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
3.二级缓存的测试:
(1)默认一级缓存,不清楚一级缓存,无法看到二级缓存:
执行结果:Cache Hit Ratio:二级缓存使用率一值为0
(2)当我们使用commit提交方法清除一级缓存后,就可以看到二级缓存了:
执行结果:二级缓存使用率为0.5 二分之一
(3)前两个是使用同一个SqlSession,所以无法测试关闭,这里看看单例模式的SqlSessionFactory:
(4)当我们使用同一个SqlSessionFactory针对同一个Mapper文件创建不同的Session,且不执行commit或者close操作时:
执行结果:
到这里我们可以得到两个结论:
【1】不同的SqlSession不会共享一级缓存
【2】二级缓存并不会直接产生,通过后面的例子可以知道二级缓存只会由一级缓存调用commit或者close后由一级缓存刷新到二级缓存去
(3)使用Commit提交第一个session后:
执行结果:
可以得到的结论是:
【1】二级缓存通用语同一个Factory针对同一个Mapper生成的不同的SqlSession。注:一会儿会演示不同工厂时。
【2】证明了上面的,二级缓存不能直接产生,可以通过Commit由一级缓存刷新到二级缓存
(4)使用close关闭第一个session后:
执行结果:
可以得出的结论:
【1】证明了上面的,二级缓存不能直接产生,可以通过close由一级缓存刷新到二级缓存
(5)不同Factory针对相同Mapper创建不同SqlSession时:
执行方法:
执行结果:
可以得出结论:
【1】不同工厂针对于同一Mapper得到的不同的SqlSession是不会贡献二级缓存的。