JavaEE--Mybatis学习笔记(六)--查询缓存

查询缓存

  查询缓存的使用,主要是为了提高查询访问速度。将用户对同一数据的重复查询过程简化,不再每次均从数据库查询获取结果数据,从而提高访问速度。

   左为执行原理示意图 右为执行流程图

       作用域:mybatis查询缓存的作用域是根据映射文件mapper的namespace划分的,相同namespace的mapper查询数据存放在同一缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。

       生命周期:但一、二级缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在。而二级缓存的生命周期会与整个应用同步,与SqlSession是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSession)间共        享数据,而二级缓存是在不同线程(不同的SqlSession)间共享数据。 不同的SqlSession共享数据也是在同一个namespace下。

  1.一级缓存

  • mybatis默认一级缓存是开启状态,且不能关闭。
  • 证明一级缓存的存在

 

  证明一级缓存是存在的 只执行了一次sq
  • 从缓存中查找数据的依据

  缓存的底层实现是一个Map,Map的value是查询结果
  Map的key,即查询依据,使用的ORM框架不同,查询依据是不同的
  Mybatis 查询的依据是:Sql的id + SQL语句 + 哈希值 这里的用是select 的 id(selectStudentByid 和 selectStudentByid2) 和 sql语句不同传的值为 2 或者 3
  Hibernate 的查询依据是:查询结果对象的id
  • 增删改对一级缓存的影响

   

  增删改操作都会清空一级缓存,无论是否提交
  为什么要把缓存清空了?
  比如要查询年龄大于20的,下次查询就要刷新了,不然数据会出错。

  2.内置二级缓存

    由于MyBatis从缓存中读取数据的依据与SQL的id相关,而非查询出的对象。所以,使用二级缓存的目的,不是在多个查询间共享查询结果(所有查询中只要查询结果中存在该对象的,就直接从缓存中读取,这是对查询结果的共   享,Hibernate中的缓存就是为了在多个查询间共享查询结果,但MyBatis的不是),而是为了防止同一查询(相同的Sql id、相同的Sql语句)的反复执行

    Hibernate只要查询了AllStudents 那么在下次查询某个学生就可以从缓存中找了。

  • 二级缓存的开启:在映射文件里加入<cache/> 同时 将实体类序列化
  • 证明内置二级缓存的存在

  只要把一级缓存的数据清空,查询的时候没有通过数据库来就可以证明了

  方案一.使用增删改就可以清空一级缓存 ,但是不能确定是否对二级缓存也有影响

  方案二.一级缓存的生命周期和SqlSession是一致的所以在一次查询完后关闭SqlSession

  

  • 增删改对二级缓存的影响

  

  

本例说明以下几点内容:
1.说明增删改同样也会清空二级缓存
2.对于二级缓存的清空实质上是对所查找key对应的value置为null,而并非将<key,value>对,即Entry对象删除
3.从DB中进行select查询的条件是:
    1)缓存中根本就不存在这个key
    2)缓存中存在该key所对应的Entry对象,但其value为null
4.命中率是在缓存中根据key来查的,因为key存在所以命中率为0.5,但是所对应的value为null,所以需要再次去数据库中查找,也就有了第二次sql
5.如果不想让增删改对二级缓存产生影响 就在<insert flushCache="false"即可,但是对于一级缓存这个是没用的,无论如何都会清空
  • 二级缓存的配置

    eviction:逐出策略。当二级缓存中的对象达到最大值时,就需要通过逐出策略将缓存中的对象移出缓存。默认为LRU。还有FIFO。

    flushInterval:刷新缓存的时间间隔,单位毫秒。这里的刷新即清空缓存。一般不指定,即当执行增删改时刷新缓存。

    readOnly:设置缓存中数据是否只读。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。但读写的缓存会返回缓存对象的拷贝。这会慢一些,但是安全,因此默认是false。

    size:二级缓存中可以存放的最多对象个数。默认为1024个。

  • 二级缓存的关闭

    在主配置文件中的<settings>标签下加入

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

      二级缓存的局部关闭 在select下加入useCache="false"

    <select id="selectStudentById" useCache="false" resultType="Student">
        select id,name,age,score from student2 where id = #{id}
    </select>
  • 二级缓存的使用原则
    • 多个namespace不要操作同一张表

        由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。若某个用户在某个namespace下对表执行了增删改操作,该操作只会引发当前namespace下的二级缓存的刷新,而对其他namespace下的二级缓存没有影响。这样的话,其他二级缓存中的数据依然是为更新的数据,也就出现了多个namespace中的数据不一样的现象。

    • 不要在关联关系表上执行增删改操作

        一个namespace一般是对同一个表进行操作,若表间存在关联关系,也就意味着同一个表可能就会出现在多个namespace中。这样就存在一个风险,若某一个namespace中对一个表进行增删改操作时影响到了其关联表的数据,而这个关联表的数据的修改只会刷新当前namespace下的二级缓存,而对另一namespace下的二级缓存数据没有影响。这也是多个namespace下对同一张表的操作。

    • 查询多于修改时使用二级缓存

          在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。

  3.ehcache二级缓存

     mybatis的特长是SQL操作,缓存数据管理不是其特长,为了提高缓存的性能,mybatis允许使用第三方缓存产品,ehCache就是其中一种。

     注意:使用ehcache二级缓存,实体类无需实现序列化接口。

  • ehcache二级缓存的启动
    • 导入包

      

    • 映射文件cache 类型改成ehcache
      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    • 加入ehcache.xml文件
    在ehcache-core-2.6.8.jar 包下有一个 ehcache-failsafe.xml 将它复制出来改成ehcache.xml 同时放在src目录下
若是直接将实体类的序列化接口去掉会报错
使用缓存时出现java.io.NotSerializableException:xxx.xxx.xxx.Bean解决办法
因为ehcache想将实体类写入磁盘但是这必须通过实体类的序列化才可以
解决方法:
1.开发过程中如果想缓存某个JavaBean,请确保它所引用的对象都implents Serializable,如果某个对象不需要被cache,可以加上transient关键字,否则Ehcache每次都通过引用查找的方法去保存所有实例数据到磁盘.最终会失败.
2.ehcache.xml中
    <defaultCache
            overflowToDisk="false"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
ehcache在不同mapper中的个性化设置 就是在cache里的property标签下 加name 和 value的值

猜你喜欢

转载自www.cnblogs.com/windbag7/p/9382916.html