更新、提交等操作情况缓存;
@SuppressWarnings("unchecked") @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } // 查询的时候一般不清楚缓存,但是可以通过 xml配置或者注解强制清除,queryStack == 0 是为了防止递归调用 if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; // 首先查看一级缓存 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 没有查到的时候直接到数据库查找 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { // 延迟加载队列 for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // 一级缓存本身不能关闭,但是可以设置作用范围 STATEMENT,每次都清除缓存 clearLocalCache(); } } return list; } 三、二级缓存mybatis 二级缓存要稍微复杂一点,中间多了一步事务缓存:
首先无论是查询还是更新,都会按要求清空缓存 flushCacheIfRequired,默认更新清空,查询不清空,也可以在 xml 或者注解中指定;
查询的时候,先查缓存,命中返回,未命中查一级缓存、数据库,然后回填事务缓存,注意这里不是直接填充到缓存中;此时的事务缓存对任何的 SqlSession 都是不可见的,因为自己查询的时候也是直接查询的目标缓存;
更新就直接委托给目标 Executor 执行;
最后 SqlSession 执行commit 的时候,将事务缓存刷新到目标缓存中;
1. 事务缓存测试设置二级缓存:
<setting value="true"/> <mapper namespace="***"> <cache/> </mapper> @Test public void test02() { SqlSessionFactory sqlSessionFactory = DBUtils.getSessionFactory(); try ( SqlSession sqlSession1 = sqlSessionFactory.openSession(true); SqlSession sqlSession2 = sqlSessionFactory.openSession(true); ) { UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); User u1 = userMapper1.getUser(1L); System.out.println("---get u1: " + u1); User u2 = userMapper2.getUser(1L); System.out.println("---get u2: " + u2); User u3 = userMapper1.getUser(1L); System.out.println("---get u3: " + u3); } }打印:
DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.0 DEBUG [main] - ==> Preparing: select * from user where id = ? DEBUG [main] - ==> Parameters: 1(Long) DEBUG [main] - <== Total: 1 ---get u1: User{id=1, user_name='sanzao', password='123456', address='TT'} DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.0 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 1613095350. DEBUG [main] - ==> Preparing: select * from user where id = ? DEBUG [main] - ==> Parameters: 1(Long) DEBUG [main] - <== Total: 1 ---get u2: User{id=1, user_name='sanzao', password='123456', address='TT'} DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.0 ---get u3: User{id=1, user_name='sanzao', password='123456', address='TT'}可以看到:
SqlSession1 为提交事务缓存,所以 SqlSession2 又从数据库中查了一次;
当SqlSession1 再次查询的时候,二级缓存未命中 Cache Hit Ratio 为 0,但是命中了一级缓存,所以并未再查数据库;
2. 二级缓存测试这次我们提交缓存看看是否命中:
@Test public void test03() { SqlSessionFactory sqlSessionFactory = DBUtils.getSessionFactory(); try ( SqlSession sqlSession1 = sqlSessionFactory.openSession(true); SqlSession sqlSession2 = sqlSessionFactory.openSession(true); ) { UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); User u1 = userMapper1.getUser(1L); System.out.println("---get u1: " + u1); sqlSession1.commit(); User u2 = userMapper2.getUser(1L); System.out.println("---get u2: " + u2); int i = userMapper1.setNameById(1L, "LiSi"); System.out.println("---update user: " + i); sqlSession1.commit(); User u3 = userMapper1.getUser(1L); System.out.println("---get u3: " + u3); sqlSession1.commit(); User u4 = userMapper2.getUser(1L); System.out.println("---get u4: " + u4); } }打印:
DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.0 DEBUG [main] - ==> Preparing: select * from user where id = ? DEBUG [main] - ==> Parameters: 1(Long) DEBUG [main] - <== Total: 1 ---get u1: User{id=1, user_name='sanzao', password='123456', address='TT'} DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.5 ---get u2: User{id=1, user_name='sanzao', password='123456', address='TT'} DEBUG [main] - ==> Preparing: update user set username = ? where id = ? DEBUG [main] - ==> Parameters: LiSi(String), 1(Long) DEBUG [main] - <== Updates: 1 ---update user: 1 DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.3333333333333333 DEBUG [main] - ==> Preparing: select * from user where id = ? DEBUG [main] - ==> Parameters: 1(Long) DEBUG [main] - <== Total: 1 ---get u3: User{id=1, user_name='LiSi', password='123456', address='TT'} DEBUG [main] - Cache Hit Ratio [sanzao.db.UserMapper]: 0.5 ---get u4: User{id=1, user_name='LiSi', password='123456', address='TT'}这次就能看到当 SqlSession1 提交事务缓存后,SqlSession2 就能看到了;
3. 缓存配置测试此外还可以配置各种二级缓存策略,比如大小,刷新间隔时间,淘汰策略等,这里主要就是使用了 Cache 接口的装饰者模式: