MyBatis提供了一级缓存和二级缓存机制,用于提高数据库查询性能。一级缓存是SqlSession级别的缓存,默认开启;二级缓存是Mapper级别的缓存,需要手动配置开启。
一级缓存实现原理
一级缓存基于PerpetualCache实现,缓存存储在BaseExecutor的localCache属性中。当执行查询时,MyBatis会先从缓存中查找,若未命中才查询数据库。
// 示例:一级缓存验证
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.selectById(1); // 第一次查询,访问数据库
User user2 = mapper.selectById(1); // 第二次查询,从缓存获取
System.out.println(user1 == user2); // 输出true
} finally {
session.close();
}
一级缓存失效场景
执行增删改操作会清空一级缓存,SqlSession关闭后缓存失效,调用clearCache方法主动清除缓存。
// 示例:缓存失效演示
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.selectById(1); // 第一次查询
mapper.updateUser(new User(1, "newName")); // 更新操作
User user2 = mapper.selectById(1); // 重新查询数据库
System.out.println(user1 == user2); // 输出false
} finally {
session.close();
}
二级缓存配置方式
在MyBatis配置文件中启用二级缓存,并在Mapper XML中添加<cache/>标签。
<!-- mybatis-config.xml -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
</mapper>
二级缓存工作流程
二级缓存跨SqlSession共享数据,执行查询时先查二级缓存,再查一级缓存,最后访问数据库。提交事务时数据才会存入二级缓存。
// 示例:二级缓存验证
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper mapper1 = session1.getMapper(UserMapper.class);
User user1 = mapper1.selectById(1); // 第一次查询数据库
session1.close(); // 提交到二级缓存
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user2 = mapper2.selectById(1); // 从二级缓存获取
session2.close();
缓存自定义实现
可以通过实现Cache接口创建自定义缓存,支持Redis等分布式缓存。
public class RedisCache implements Cache {
private final String id;
private JedisPool jedisPool;
public RedisCache(String id) {
this.id = id;
}
// 实现所有Cache接口方法
// ...
}
<!-- 使用自定义缓存 -->
<cache type="com.example.RedisCache">
<property name="host" value="redis.example.com"/>
</cache>
缓存相关注意事项
实体类必须实现Serializable接口,二级缓存不建议在高并发更新场景使用。可以通过useCache="false"关闭单个语句的缓存。
<select id="selectById" resultType="User" useCache="false">
select * from user where id = #{id}
</select>
缓存刷新策略
设置flushCache属性控制语句执行时是否刷新缓存,默认为增删改语句刷新,查询语句不刷新。
<insert id="insertUser" parameterType="User" flushCache="true">
insert into user values(#{id}, #{name})
</insert>
通过合理配置MyBatis缓存机制,可以显著减少数据库访问次数,但需要注意缓存一致性问题,根据业务场景选择合适的缓存策略。