- 订单表 与 会员表 是存在 一对多的关系,为了尽可能减少 join查询,进行了 分阶段查询,即先查询出 订单表,在根据 member_id字段 查询出 会员表,最后进行数据整合。
- 如果 订单表中 存在重复的 member_id,就会出现很多没必要的重复查询。
- 针对这种情况,MyBatis 通过 一级缓存 来实现,在同一次查询会话中,如果出现相同的语句及参数,就会从缓存中取出而不再查询数据库。
- 一级缓存,只能作用于查询会话中,所以也叫做 会话缓存。
- 必须是相同的 SQL 和 参数。
- 必须是相同的 会话。
- 必须是相同的 namespace,即同一个 Mapper接口。
- 必须是相同的 statement,即同一个 Mapper接口中的同一个方法。
- 查询语句中间,没有执行 session.clearCache()方法。
- 查询语句中间,没有执行 insert、update、delete方法(无论变动记录是否 与 缓存数据有无关系)。
/**
* 一级缓存。
*/
@Test
public void testL1Cache() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("1、------------------------------");
User user = userMapper.getUserById(1);
System.out.println("2、------------------------------");
// 1、同一个Mapper。
User user2 = userMapper.getUserById(1);
System.out.println("3、------------------------------");
// 2、同一个Session。
User user3 = sqlSession.getMapper(UserMapper.class).getUserById(1);
System.out.println("4、------------------------------");
// 3、不同的Session。
User user4 = sqlSessionFactory.openSession().getMapper(UserMapper.class).getUserById(1);
System.out.println("------------------------------");
System.out.println(user == user2);
System.out.println(user == user3);
System.out.println(user == user4);
/*
1、------------------------------
SELECT * FROM `user` WHERE id = ?
2、------------------------------
3、------------------------------
4、------------------------------
SELECT * FROM `user` WHERE id = ?
------------------------------
true
true
false
*/
}
userMapper.getUserById(1)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(…) // 145org.apache.ibatis.executor.CachingExecutor.query(…) // 93
org.apache.ibatis.executor.BaseExecutor.query(…) // 141
org.apache.ibatis.cache.impl.PerpetualCache.getObject(.) // 54
userMapper.getUserById(1)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(…) // 145org.apache.ibatis.executor.CachingExecutor.query(…) // 93
org.apache.ibatis.executor.BaseExecutor.query(…) // 141
org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(…) // 320
org.apache.ibatis.cache.impl.PerpetualCache.putObject(…) // 49
clearCache作为入口,追踪一级缓存的实现PerpetualCache。
org.apache.ibatis.session.defaults.DefaultSqlSession.clearCache() // 305org.apache.ibatis.executor.CachingExecutor.clearLocalCache() // 160
org.apache.ibatis.executor.BaseExecutor.clearLocalCache() // 263
org.apache.ibatis.cache.impl.PerpetualCache.clear() // 64
- 业务系统中存在很多的 静态数据,如:字典表、菜单表、权限表等,这些数据的特性是不会轻易修改,但又是查询的热点数据。
- 一级缓存 针对的是 同一个会话 当中 相同SQL,并不适合这种 热点数据的缓存 场景。
- 为了解决这个问题,引入了二级缓存,它脱离于会话之外。
- 当会话 提交 或 关闭 之后,才会填充二级缓存。
- 必须是 在同一个命名空间 之下。
- 必须是 相同的statement,即同一个Mapper接口中的同一个方法。
- 必须是 相同的SQL语句 和 参数。
- 如果 readWrite = true,实体对像 必须 实现 Serializable接口。
- xml文件中 配置的update 不能清空 @CacheNamespace中的缓存数据。
- 只有 修改会话 提交之后,才会执行清空操作。
- 任何一种 增删改操作,都会清空整个 namespace中的缓存。
@CacheNamespace
//@CacheNamespace(
// implementation = PerpetualCache.class, // 缓存实现Cache接口的实现类。
// eviction = LruCache.class, // 缓存算法。
// flushInterval = 60000, // 刷新间隔时间(毫秒)。
// size = 1024, // 最大缓存引用对象。
// readWrite = true, // 是否可写。
// blocking = false // 是否阻塞。
//)
public interface UserMapper {
}
<mapper namespace="com.qs.mybatisfast.mapper.UserMapper">
<!-- 开启二级缓存。-->
<cache/>
<select id="getUserById" parameterType="int" resultType="com.qs.mybatisfast.pojo.User">
SELECT *
FROM `user`
WHERE id = #{id}
</select>
</mapper>
/**
* 二级缓存。
*/
@Test
public void testL2Cache() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("1、------------------------------");
User user = userMapper.selectByTable("user", 1);
System.out.println("2、------------------------------");
// 1、同一个Mapper。
User user2 = userMapper.selectByTable("user", 1);
System.out.println("3、------------------------------");
// 2、同一个Session。
User user3 = sqlSession.getMapper(UserMapper.class).selectByTable("user", 1);
System.out.println("4、------------------------------");
// 3、不同的Session。
User user4 = sqlSessionFactory.openSession().getMapper(UserMapper.class).selectByTable("user", 1);
System.out.println("------------------------------");
System.out.println(user == user2);
System.out.println(user == user3);
System.out.println(user == user4);
/*
1、------------------------------
SELECT * FROM user WHERE id = ?
2、------------------------------
3、------------------------------
4、------------------------------
------------------------------
true
true
false
*/
}
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(…) // 145
org.apache.ibatis.executor.CachingExecutor.query(…) // 93
org.apache.ibatis.executor.CachingExecutor.flushCacheIfRequired(.) // 164
org.apache.ibatis.cache.TransactionalCacheManager.getObject(…) // 34
org.apache.ibatis.cache.decorators.TransactionalCache.getObject(.) // 66
org.apache.ibatis.cache.decorators.SynchronizedCache.getObject(.) // 49
org.apache.ibatis.cache.decorators.LoggingCache.getObject(.) // 55
org.apache.ibatis.cache.decorators.SerializedCache.getObject(.) // 63
org.apache.ibatis.cache.decorators.ScheduledCache.getObject(.) // 59
org.apache.ibatis.cache.decorators.LruCache.getObject(.) // 72
org.apache.ibatis.cache.impl.PerpetualCache.getObject(.) // 54
org.apache.ibatis.executor.CachingExecutor.close(.) // 55
org.apache.ibatis.cache.TransactionalCacheManager.commit() // 42
org.apache.ibatis.cache.decorators.TransactionalCache.commit() // 101
org.apache.ibatis.cache.decorators.TransactionalCache.flushPendingEntries() // 120
org.apache.ibatis.cache.decorators.SynchronizedCache.putObject(…) // 44
org.apache.ibatis.cache.decorators.LoggingCache.putObject(…) // 50
org.apache.ibatis.cache.decorators.SerializedCache.putObject(…) // 54
org.apache.ibatis.cache.decorators.ScheduledCache.putObject(…) // 53
org.apache.ibatis.cache.decorators.LruCache.putObject(…) // 66
org.apache.ibatis.cache.impl.PerpetualCache.putObject(…) // 49