Mybatis之二级缓存(八)

1. 介绍

        Mybatis缓存分为一级缓存和二级缓存,在本节中我们介绍下二级缓存的使用及其特性

        MyBatis的一级缓存是在一个Session域内有效的,当Session关闭后,缓存内容也随之销毁。但是Mybatis的一级缓存在Spring中是没有作用的,而我们实际项目中也经常把Mybatis和Spring整合到一起使用。这个时候我们就需要用到二级缓存。

2. 准备

        请先完成Mybatis之一级缓存(七)章节的内容。

3. Mybatis二级缓存学习

        1) 开启二级缓存

        Mybatis是默认不开启二级缓存的,要开启二级缓存我们需要做如下准备:

        a. 调整Mybatis.xml,在<configuration>节点中增加<settings><setting name="cacheEnabled" value="true"></settings>。这里是Mybatis二级缓存的总开关。

<configuration>
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>  
    .
    .
    .
</configuration>  
    

         b. 调整Mapper文件,开启针对Mapper的二级缓存开关。

<mapper namespace="com.mybatis.dao.CacheDao">
    <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
    .
    .
    .
</mapper>

        eviction="FIFO"  是缓存的淘汰算法,可选值有"LRU"、"FIFO"、"SOFT"、"WEAK",缺省值是LRU。

        flushInterval="60000"  指缓存过期时间,单位为毫秒,缺省值为空,即只要容量足够,永不过期。  

        size="1024"  缓存容量。

        readOnly="true"  是否只读。

           如果为true, 则所有相同的sql返回的是同一个对象(有助于提高性能,但并发操作同一条数据时,可能不安全)。

           如果为false,则所有相同的sql返回的是同一个对象的副本拷贝。

        c. 调整要缓存的对象,使其实现com.io.Serializabie接口。

package com.mybatis.entity;

import java.io.Serializable;

public class CacheInfo implements Serializable{
    .
    .
    .
}

        2) 二级缓存的测试使用

package com.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.mybatis.entity.CacheInfo;

@SuppressWarnings("unused")
public class TestMain {
      
    public static void main(String[] args) {
        InputStream iStream = TestMain.class.getClassLoader().getResourceAsStream("mybatis.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(iStream);
        CacheInfo cacheInfo;

        SqlSession session1 = sessionFactory.openSession();
        cacheInfo = session1.selectOne("com.mybatis.dao.CacheDao.getCache1", 1);
        System.out.println("第1次打印  ID:"+cacheInfo.getId()+","+cacheInfo.getTxt());
        session1.close();

        SqlSession session2 = sessionFactory.openSession();
        cacheInfo = session2.selectOne("com.mybatis.dao.CacheDao.getCache1", 1);
        System.out.println("第1次打印  ID:"+cacheInfo.getId()+","+cacheInfo.getTxt());
        session2.close();
        
    }
}

        打印结果为:

2016-02-22 11:41:06.895 [DEBUG] com.mybatis.dao.CacheDao.getCache1 ==>  Preparing: SELECT * FROM tbCache1 WHERE ID=?  
2016-02-22 11:41:06.954 [DEBUG] com.mybatis.dao.CacheDao.getCache1 ==> Parameters: 1(Integer) 
2016-02-22 11:41:07.021 [DEBUG] com.mybatis.dao.CacheDao.getCache1 <==      Total: 1 
第1次打印  ID:1,111
.
.
.
2016-02-22 11:41:07.021 [DEBUG] com.mybatis.dao.CacheDao Cache Hit Ratio [com.mybatis.dao.CacheDao]: 0.5
第2次打印  ID:1,111

        从上面的结果中,可以看到“第2次打印”并没有从数据库中读取数据,而是直接读取的缓存。

4. 总结及其他

        1)Mybatis的二级缓存可以跨Session访问,但是必须在同一个Session工厂内。

        2)Mybatis的二级缓存更新是以Mapper(确切的说是namespace,因为Mybatis允许多个Mapper对用同一个namespace)为单位的,一个Mapper中的增删改查只能影响到当前Mapper所关联的缓存内容,不同Mapper之间的缓存是相互独立的。

        3)Mybatis的二级缓存的弊端也就显而易见了。假如AMapper中是对A表的增删改查操作,BMapper中是对B表的增删改查操作,CMapper中执行了一个针对A表和B表的联合查找。那么,

            首先,我们执行CMapper的联合查找,然后针对CMapper产生一个缓存对象。

            然后,我们执行AMapper中对A表进行更新操作,由于二级缓存是针对Mapper为单位的,所以此时并不会更新CMapper中的缓存对象。

            在这种情况下,我们再重新执行CMapper的联合查找(查询条件不变),就会读取CMapper中的二级缓存内容,也就会读到脏数据。

        获取有人会说,我们可以把AMapper、BMapper、CMapper合并到一个Mapper中啊?这样会带来另外一些问题:

            首先,这样会导致我们的单个的Mapper文件容量变得极大

            其次,这样会导致这个融合后的Mapper对应的缓存文件不停的被更新,导致效率低下甚至失去缓存意义。

笔者只是初学者,开此博客的初衷是为了给自己的学习过程留一个痕迹。所以您可能发现笔者措辞不严谨、逻辑不合理,甚至代码有错误、结论很偏颇等等。笔者感激各位的讨论和指正,并在此不胜感激!拜谢!欢迎加QQ群讨论:852410026
原文地址:https://www.cnblogs.com/LOVE0612/p/5210152.html