一、二级缓存核心特性

MyBatis 二级缓存存储的不是 Java 对象本身,而是对象的序列化数据。这意味着从二级缓存中获取的对象,与原对象不是同一个实例(用==比较返回false),但对象内容一致。

代码示例:验证二级缓存存储特性


public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
   
}

// 测试方法
@Test
public void testSecondLevelCache() {
    // 第一个SqlSession查询并提交(触发一级缓存写入二级缓存)
    User user1 = null;
    try (SqlSession session1 = MyBatisUtil.getSqlSession()) {
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
        user1 = mapper1.selectById(1);
        session1.commit();
    }

    // 第二个SqlSession从二级缓存查询
    try (SqlSession session2 = MyBatisUtil.getSqlSession()) {
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
        User user2 = mapper2.selectById(1);
        System.out.println("对象实例是否相同:" + (user1 == user2)); // false
        System.out.println("对象内容是否相同:" + user1.equals(user2)); // true
    }
}

二、二级缓存关键配置

在 Mapper.xml 中通过<cache>标签配置二级缓存,核心参数如下:

参数作用默认值
eviction回收策略:LRU(最近最少使用)/FIFO(先进先出)LRU
size缓存最大存储条数1024
flushInterval缓存自动刷新间隔(毫秒),0 表示不自动刷新0
readOnly读写模式:true(只读,性能高)/false(读写,安全)false

配置示例(UserMapper.xml)

<!-- 开启并配置二级缓存 -->
<cache
  eviction="LRU"        <!-- 回收策略:移除最久未使用的缓存 -->
  size="512"            <!-- 最多存512条数据 -->
  flushInterval="60000" <!-- 60秒自动刷新缓存 -->
  readOnly="false"/>    <!-- 读写模式,返回对象拷贝 -->

<!-- 查询语句需开启缓存(useCache="true") -->
<select id="selectById" resultType="User" useCache="true">
    SELECT id, username, password FROM user WHERE id = #{id}
</select>

全局开关(mybatis-config.xml)

二级缓存需先开启全局配置(默认开启,可显式配置):

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

三、MyBatis 缓存查询顺序

MyBatis 查询遵循 “先二级、再一级、最后数据库” 的逻辑:

  1. 发起查询时,先检查二级缓存(跨 SqlSession 生效);
  2. 二级缓存未命中,检查一级缓存(当前 SqlSession 内生效);
  3. 一级缓存也未命中,执行 SQL 查询数据库;
  4. 查询结果写入一级缓存,当 SqlSession 关闭 / 提交时,一级缓存数据写入二级缓存。

代码示例:验证查询顺序

@Test
public void testCacheQueryOrder() {
    // 第一次查询:无缓存,查数据库,结果写入一级缓存
    try (SqlSession session1 = MyBatisUtil.getSqlSession()) {
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
        System.out.println("第一次查询:" + mapper1.selectById(1)); // 查数据库
        session1.commit(); // 一级缓存写入二级缓存
    }

    // 第二次查询:命中二级缓存,不查数据库
    try (SqlSession session2 = MyBatisUtil.getSqlSession()) {
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
        System.out.println("第二次查询:" + mapper2.selectById(1)); // 查二级缓存
    }

    // 同一Session内查询:命中一级缓存
    try (SqlSession session3 = MyBatisUtil.getSqlSession()) {
        UserMapper mapper3 = session3.getMapper(UserMapper.class);
        User u1 = mapper3.selectById(1);
        User u2 = mapper3.selectById(1);
        System.out.println("同一Session内对象是否相同:" + (u1 == u2)); // true
    }
}

总结

  1. MyBatis 二级缓存存储序列化数据,跨会话获取的对象实例不同但内容一致;
  2. 二级缓存可通过evictionsize等参数自定义,默认采用 LRU 回收策略、读写模式;
  3. 缓存查询顺序为:二级缓存 → 一级缓存 → 数据库,SqlSession 提交 / 关闭时一级缓存写入二级缓存。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐