Redis implements the secondary cache of Mybatis

1, Mybatis cache

Like most ORM tier frameworks, Mybatis naturally provides support for L1 and L2 caching. The following is the role and definition of L1 cache and L2 cache.

1. The first level cache is the sqlSession level cache. When operating the database, you need to construct sqlSession object, in which there is a (memory area) data structure (HashMap) to store cached data. The cached data area (HashMap) between different sqlsessions does not affect each other.

The L2 cache is a mapper level cache. Multiple sqlsessions operate the sql statement of the same mapper. When multiple sqlsessions operate the database, the data will exist in the L2 cache area. Multiple sqlsessions can share the L2 cache. The L2 cache is cross sqlsessions.  

2. The scope of the first level cache is the same sqlsession. The same sql statement is executed twice in the same sqlsession. After the first execution, the data queried in the database will be written to the cache (memory), and the second time, the data will be obtained from the cache. The data will not be queried from the database, so as to improve the query efficiency. When an sqlsession ends, the L1 cache in the sqlsession does not exist. Mybatis enables L1 cache by default.

The second level cache is shared by multiple sqlsessions. Its scope is the same namespace of mapper. Different sqlsessions execute sql statements in the same namespace twice and pass the same parameters to sql, that is, finally execute the same sql statement. After the first execution, the data queried in the database will be written to the cache (memory), and the second time, the data will be obtained from the cache, and the query will no longer be made from the database, So as to improve the query efficiency. Mybatis does not enable L2 cache by default. You need to configure enabling L2 cache in the setting global parameter.

Generally, when we integrate mybatis and spring, the mybatis spring package will automatically sub package sqlsessions. Spring uses a template method to encapsulate select() and other operations through the dynamic proxy sqlSessionProxy. Each select() query will automatically execute openSession() first and call the close() method after closing(), which is equivalent to generating a new session instance. Therefore, we do not need to manually close this session(), Of course, the first level cache of mybatis cannot be used, which means that the first level cache of mybatis has no effect in spring.

Therefore, we generally implement the secondary cache of Mybatis in the project. Although Mybatis has its own secondary cache function, in the real cluster environment, the use of its own secondary cache is only for a single node, so we use the distributed secondary cache function. General caching NoSql databases such as redis, Mancache, etc., or EhCache can be implemented to better serve the query of ORM in tomcat cluster.

2, Implementation of secondary cache of Mybatis

Next, the second level cache function of Mybatis is mainly realized through Redis.

1. Enable L2 cache in configuration file


  1. <setting name="cacheEnabled" value="true"/>  

The default L2 cache is on.

2. Implement the Cache interface of Mybatis

Mybatis provides an interface implemented by a third-party Cache. We customize MybatisRedisCache to implement the Cache interface. The code is as follows:

  1. /** 
  2.  * Creation time: 11:40 am, January 7, 2016
  3.  *  
  4.  * Mybatis L2 cache implementation class
  5.  *  
  6.  * @author andy 
  7.  * @version 2.2 
  8.  */  
  10. public class MybatisRedisCache implements Cache {  
  12.     private static final Logger LOG = Logger.getLogger(MybatisRedisCache.class);   
  14.     private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);  
  16.     private RedisTemplate<Serializable, Serializable> redisTemplate =  (RedisTemplate<Serializable, Serializable>) SpringContextHolder.getBean("redisTemplate");   
  18.     private String id;  
  20.     private JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();  
  22.     public MybatisRedisCache(final String id){  
  23.         if(id == null){  
  24.             throw new IllegalArgumentException("Cache instances require an ID");  
  25.         }  
  26."Redis Cache id " + id);  
  27. = id;  
  28.     }  
  30.     @Override  
  31.     public String getId() {  
  32.         return;  
  33.     }  
  35.     @Override  
  36.     public void putObject(Object key, Object value) {  
  37.         if(value != null){  
  38.             redisTemplate.opsForValue().set(key.toString(), jdkSerializer.serialize(value), 2, TimeUnit.DAYS);  
  39.         }  
  40.     }  
  42.     @Override  
  43.     public Object getObject(Object key) {  
  44.         try {  
  45.             if(key != null){  
  46.                 Object obj = redisTemplate.opsForValue().get(key.toString());  
  47.                 return jdkSerializer.deserialize((byte[])obj);   
  48.             }  
  49.         } catch (Exception e) {  
  50.             LOG.error("redis ");  
  51.         }  
  52.         return null;  
  53.     }  
  55.     @Override  
  56.     public Object removeObject(Object key) {  
  57.         try {  
  58.             if(key != null){  
  59.                 redisTemplate.expire(key.toString(), 1, TimeUnit.SECONDS);  
  60.             }  
  61.         } catch (Exception e) {  
  62.         }  
  63.         return null;  
  64.     }  
  66.     @Override  
  67.     public void clear() {  
  68.         //jedis nonsupport  
  69.     }  
  71.     @Override  
  72.     public int getSize() {  
  73.         Long size = redisTemplate.getMasterRedisTemplate().execute(new RedisCallback<Long>(){  
  74.             @Override  
  75.             public Long doInRedis(RedisConnection connection)  
  76.                     throws DataAccessException {  
  77.                 return connection.dbSize();  
  78.             }  
  79.         });  
  80.         return size.intValue();  
  81.     }  
  83.     @Override  
  84.     public ReadWriteLock getReadWriteLock() {  
  85.         return this.readWriteLock;  
  86.     }  
  88. }  

3. Practical application of L2 cache

We need to serialize all entity classes, and then add custom cache function in Mapper.


  1.   <cache  
  2.     type=""  
  3. eviction="LRU"  
  4. flushInterval="6000000"  
  5. size="1024"  
  6. readOnly="false"  
  7. />  

4. Storage in Redis

Redis will automatically take Sql + condition + Hash, etc. as the key value and the query result as the value. If all the parameters in the request meet, the secondary cache in redis will be used. The query results are as follows:


Reproduced in:

Tags: Java Database data structure

Posted by ladokha on Sun, 15 May 2022 22:48:08 +0300