Level 1 cache test

1) Same conversation

Turn off level 2 caching <! -- Control global cache (level 2 cache), Default true- > <setting name="cacheEnabled" value="false"/> @test public void testCache() throws IOException {String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); try { BlogMapper mapper0 = session1.getMapper(BlogMapper.class); BlogMapper mapper1 = session1.getMapper(BlogMapper.class); First query System. Out. Println (" ~ ~ ~ ~ ~ is same session session1 ~ ~ ~ ~ ~ ~ ~ ~ "); Blog blog = mapper0.selectBlogById(1); System.out.println(blog); System. Out. Println (" the second query ~ ~ ~ ~ ~ is same session session1 ~ ~ ~ ~ ~ "); System.out.println(mapper1.selectBlogById(1)); } finally { session1.close(); }}Copy the code

The first time the SQL execution process is printed, the second time it is not printed, it is fetched from the cache

2) Different conversations

@Test public void testCache() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); try { BlogMapper mapper0 = session1.getMapper(BlogMapper.class); First query System. Out. Println (" ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ different session session1 and session2 ~ ~ ~ ~ ~ ~ ~ "); Blog blog = mapper0.selectBlogById(1); System.out.println(blog); System. The out. Println (" the second query ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ different session session1 and session2 ~ ~ ~ ~ ~ ~ ~ "); BlogMapper mapper1 = session2.getMapper(BlogMapper.class); System.out.println(mapper1.selectBlogById(1)); } finally { session1.close(); }}Copy the code

3) After the update, level 1 cache is cleared

BlogMapper = session.getmapper (blogmapper.class); System.out.println(mapper.selectBlogById(1)); Blog blog = new Blog(); blog.setBid(1); blog.setName("after modified 666"); mapper.updateByPrimaryKey(blog); session.commit(); // If the same session is updated, is the cache cleared? System.out.println(" Is cache hit after update operation [same session]?" ); System.out.println(mapper.selectBlogById(1));Copy the code

Results:

4) After being updated by other sessions, level 1 cache reads stale data

BlogMapper mapper1 = session1.getMapper(BlogMapper.class); System.out.println(" first query ~~~~~~~~~~~~~"); System.out.println(mapper1.selectBlogById(1)); // Session 2 updates data, session 2's level 1 cache updates Blog Blog = new Blog(); blog.setBid(1); blog.setName("after modified 555555555555"); BlogMapper mapper2 = session2.getMapper(BlogMapper.class); mapper2.updateByPrimaryKey(blog); session2.commit(); // Other sessions update data. Is level 1 cache still available for this session? System.out.println(" Did session 1 find the latest data after being updated by session 2? ); System.out.println(mapper1.selectBlogById(1));Copy the code

So the level 1 cache works too small and will read stale data from the cache.

The second level cache

1) configuration

Scope: Namespace scope is in an interface Mapper < Mapper namespace = "com. Gupaoedu. Mapper. BlogMapper" > master switch in mybatis - config. The XML configuration, the default is open, you can manually close <! <setting name="cacheEnabled" value="true"/> The <cache> tag is also required in mapper.xml. Even if the level 2 cache master switch is on, Don't go the second level cache. < cache type = "org. Apache. Ibatis. Cache. Impl. PerpetualCache" size = "1024" eviction = "LRU flushInterval" = "120000" ReadOnly ="false"/> If level-2 caching is enabled and level-2 caching is not used for a certain method, you can configure useCache="false" :  <select id="selectBlogWithAuthorQuery" resultMap="BlogWithAuthorQueryMap" useCache="false" > ... </select> Note: Whether to enable level 2 caching for useCache; FlushCache ="true" flushCache: the default value for adding, deleting, and changing statements is true. The default value for query statements is false. FlushCache ="true"  <select id="selectBlogWithAuthorResult" resultMap="BlogWithAuthorResultMap" flushCache="true" >Copy the code

2) test

@Test public void testCache() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); try { BlogMapper mapper1 = session1.getMapper(BlogMapper.class); System.out.println(" first query ~~~~~~~"); System.out.println(mapper1.selectBlogById(1)); // Does level 2 cache write if the transaction is not committed? session1.commit(); BlogMapper mapper2 = session2.getMapper(BlogMapper.class); System.out.println(" second query ~~~~~~~"); System.out.println(mapper2.selectBlogById(1)); } finally { session1.close(); }} Note: if session1.com MIT (); The result of the query is not written to the cache, because the result of the query is not written to the cache. Level 2 caching is tied to transactions.Copy the code

Level 2 caches are associated with transactions

In CachingExecutor: Private Final Executor Delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); Where is the cache object created? XMLMapperBuilder class xmlconfigurationElement() // The <cache> tag determines if (cache! = null) {// flushCache="true" flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); / / get the second level cache / / cache by TransactionalCacheManager, TransactionalCache management @ SuppressWarnings (" unchecked ") List < E > List = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); Tcm. putObject(cache, key, list); // issue #578 and #116 } return list; }} / / go SimpleExecutor | ReuseExecutor | BatchExecutor return delegate. The query (ms, parameterObject rowBounds, resultHandler, key, boundSql); } public class TransactionalCacheManager { private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>(); . } public class TransactionalCache implements Cache { public void commit() { if (clearOnCommit) { delegate.clear(); } // write flushPendingEntries(); reset(); }} * Why does level 2 cache bind transactions together? If the database does not have a value, but there is a value in the cache, then there is a problem. * Why isn't level 1 caching tied to transactions? Because if something goes wrong in the level 1 cache, the transaction rolls back, the session ends, the cache itself is gone, there is no cross-session problem.Copy the code

4) The level 2 cache will be cleared if an update operation is performed in another session

@Test public void testCacheInvalid() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); SqlSession session3 = sqlSessionFactory.openSession(); try { BlogMapper mapper1 = session1.getMapper(BlogMapper.class); BlogMapper mapper2 = session2.getMapper(BlogMapper.class); BlogMapper mapper3 = session3.getMapper(BlogMapper.class); System.out.println(" first query ~~~~~~~"); System.out.println(mapper1.selectBlogById(1)); //session1 session1.commit(); System.out.println(" second query ~~~~~~~"); System.out.println(mapper2.selectBlogById(1)); //session2 Blog blog = new Blog(); blog.setBid(1); Blog.setname (" January 6, 2019 15:03:38"); mapper3.updateByPrimaryKey(blog); session3.commit(); //session3 system.out. println(" third query ~~~~~~~"); // Was the level 2 cache cleared when an update was performed in another session? System.out.println(mapper2.selectBlogById(1)); //session2 } finally { session1.close(); session2.close(); session3.close(); }}Copy the code