MyBatis的缓存机制

MyBatis的缓存机制

一级缓存被称为Sqlssion级别的缓存,二级缓存被称为表级缓存。

  1. 一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;第二次以后是直接去缓存中取。
  • 使用一级缓存
    public static void main(String[] args) {
            // 创建连接
            try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
                // 使用动态代理,获取mapper对象
                TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
                Student student = testMapper.selectOne(1);
                Student student1 = testMapper.selectOne(1);
                System.out.println(student);
                // mybatis默认开启一级缓存
                System.out.println("student==student1??" + (student == student1));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

执行结果

我被构造了!
Student(sid=1, tid=1, sname=604, sex=女, sage=13, teacher=null)
student==student1??true
  • 不使用一级缓存

到XxxMapper.xml配置文件中添加属性:flushCache=”true”,自动刷新缓存

<mapper namespace="Mapper.TestMapper">
    <select id="selectAll" resultType="student" flushCache="true">
        select * from student
    </select>
</mapper>

public static void main(String[] args) {
        // 创建连接
        try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
            // 使用动态代理,获取mapper对象
            TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
            Student student = testMapper.selectOne(1);
            Student student1 = testMapper.selectOne(1);
            System.out.println(student);
            // mybatis默认开启一级缓存
            System.out.println("student==student1??" + (student == student1));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

执行结果

我被构造了!
我被构造了!
Student(sid=1, tid=1, sname=604, sex=女, sage=13, teacher=null)
student==student1??false
  1. Mybatis有一级缓存和二级缓存,底层都是用HashMap实现的key为CacheKey对象,value为从数据库中查出来的值。这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。
  • 开启二级缓存

通过mybatisConfig.xml配置文件开启二级缓存

给所有的sqlMapper开启二级缓存

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

<mapper namespace="Mapper.TestMapper">
    <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
</mapper>
cache:缓存开启后回收策略
eviction:缓存回收策略,默认LRU
	LRU - 最近最少使用:移除最长时间不被使用的对象
	FIFO - 先进显出:按对象进入缓存的顺序来移除他们
	SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
	WEAK - 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval:刷新间隔,单位毫秒
size:引用数目,正整数
readOnly:只读,true/false

通过XxxMapper.xml配置文件开启二级缓存

给单独指定的sqlMapper开启或关闭二级缓存

<mapper namespace="Mapper.TestMapper">
    <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
    <select id="selectOne" resultType="student" useCache="true">
        select * from student where sid=#{sid}
    </select>
</mapper>

public static void main(String[] args) {
        Student student01;
        Student student02;
        // 第一个SqlSession连接 
        try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
            TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
            student01 = testMapper.selectOne(1);
        }
        // 第二个SqlSession连接
        try (SqlSession sqlSession = MyBatisUtil.getSession(true)) {
            TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
            student02 = testMapper.selectOne(1);
        }

        System.out.println(student01 == student02);
    }

运行结果

我被构造了!
true
  • 关闭二级缓存
    <mapper namespace="Mapper.TestMapper">
        <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="false"/>
        <select id="selectOne" resultType="student" useCache="true">
            select * from student where sid=#{sid}
        </select>
    </mapper>

运行结果

我被构造了!
我被构造了!
false
  1. 缓存一致性

使用mybatis缓存会造成缓存不一致的问题

使用同一个SqlMapper查询数据时,数据保存到缓存中,有其他操作对这个数据进行修改,数据库中的数据发生改变,缓存中数据保持不变。

解决方案

使用redis实现一套mybatis二级缓存插件,将数据从内存转移到redis中,各个项目访问唯一一个redis实例(或集群),这样就保证在任意时刻,缓存的变化都会被所有项目感知,并使用最新的缓存数据;同时,redis的高性能也保证了缓存数据的高速读取。
posted @ 2022-02-11 23:50  黎白昼  阅读(108)  评论(0)    收藏  举报