This is the 21st day of my participation in the August More Text Challenge

Please tell me about the level 1 cache and level 2 cache of Mybatis. The following will show you how to use it. If you are not interested please look directly at the conclusion.

1. Understand the MyBatis cache

Mybatis level 1 cache and level 2 cache is Mybatis own.

Purpose: To store SQL query results in the cache and retrieve them directly from the cache rather than from the database during subsequent queries. This will speed up the query, but will result in dirty reads.

Note: Level 1 cache is automatically enabled, level 2 cache is manually enabled. Therefore, you need to be careful about enabling level 2 caching. (Using the Spring Boot environment)

2. Level 1 cache

Level 1 caching is automatically enabled. Sqlsession-level cache.

Call the same SQL twice within the same transaction. Level 1 cache is read.

Level 1 caching can be used if the following conditions are met:

  • 1. Sqlsession-level cache.
  • Use @Transactional. (Note required)

The service code is pasted here, and the rest is plain MVC query code.

@transactional Public City selectCity(int ID) {system.out.println (" first query "); redisDao.selectAnno(id); // Call dao layer query method system.out.println (" second query "); return redisDao.selectAnno(id); // Call the DAO layer query method}Copy the code

So we can see the query SQL. After testing, it was found that the same query twice will only print one SQL.

To enable SQL viewing in Spring Boot, you need to specify the package location of the DAO layer.

logging.level.com.example.redis.dao=debug
Copy the code

3. Level 2 cache

1. What is level 2 cache

Level 2 caching is mapper level and can be used whenever a mapper call is made.

2. Query sequence

The order of query in Mybatis is: Level 2 cache —- Level 1 cache ——- database.

3. Use Redis as level 2 cache

First of all, connect redis, realize the Cache interface, realize the method in the interface, value and save value from REDis. Here we use the RedisTemplate.

public class MybatisRedisCache implements Cache { private static Logger log = LoggerFactory.getLogger(MybatisRedisCache.class); private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); private RedisTemplate redisTemplate; private String id; private static final long EXPIRE_TIME_IN_MINUTES = 30; // Redis Expiration time public MybatisRedisCache(String ID) {if (id == null) {throw new IllegalArgumentException("Cache instances  require an ID"); } this.id = id; } @Override public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.execute((RedisCallback) connection -> { connection.flushDb(); return null; }); log.debug("Clear all the cached query result from redis"); } @Override public String getId() { // TODO Auto-generated method stub return this.id; } @Override public Object getObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); //ValueOperations opsForValue = redisTemplate.opsForValue(); log.debug("Get cached query result from redis"); //return opsForValue.get(key); Object obj = redisTemplate.opsForValue().get(key.toString()); return obj; } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } @Override public int getSize() { return 0; } @Override @SuppressWarnings("uncheck") public void putObject(Object key, Object value) { //RedisTemplate redisTemplate = getRedisTemplate(); //ValueOperations opsForValue = redisTemplate.opsForValue(); //opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); redisTemplate.opsForValue().set(key.toString(), value, 2, TimeUnit.DAYS); log.debug("Put query result to redis"); } @Override public Object removeObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete(key); log.debug("Remove cached query result from redis"); return null; } private RedisTemplate getRedisTemplate() { if (redisTemplate == null) { redisTemplate = ApplicationContextHolder.getBean("redisTemplate"); } return redisTemplate; }}Copy the code

However, RedisTemplate is not provided by Spring, so we cannot use Spring’s IOC for dependency injection, so we need to register RedisTemplate by hand.

@Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { applicationContext = ctx; } /** * Get application context from everywhere * * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * Get bean by class * * @param clazz * @param <T> * @return */ public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); } /** * Get bean by class name * * @param name * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) { return (T) applicationContext.getBean(name); }}Copy the code

Then turn on level 2 caching in the Mapper file.

<mapper namespace="com.example.redis.dao.RedisDao"> <! - open the second level cache - > < cache type = "com. Example. Redis. Cache. MybatisRedisCache" > < / cache > < select id = "selectAnno" resultType="com.example.redis.entity.City"> select * from city <if test="id ! = null">where id = #{id}</if> </select> <insert id="insertIn"> INSERT INTO `mysql`.`city` (`id`, `provinceId`, `cityName`, `description`) VALUES ('1', '1', 'aa', 'aa') </insert> </mapper>Copy the code

Finally, serialization is required to store garbled characters in Redis.

@Configuration public class RedisConfig { @Autowired private RedisTemplate redisTemplate; @Bean public RedisTemplate redisTemplateInit() { Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); / / set the serialization of the Key object instantiated redisTemplate. SetKeySerializer (new StringRedisSerializer ()); / / set the Value the instantiation of the object serialization redisTemplate. SetValueSerializer (new GenericJackson2JsonRedisSerializer ()); redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); return redisTemplate; }}Copy the code

We then tested that when the same SQL was executed in different SQLSessions, only the first time the SQL was printed. Redis stores the query result after executing a SQL query.

4. Conclusion

Level 1 cache: fetch the same SQL twice within the same transaction. Level 1 cache is read.

Level 2 cache: Mapper level, which can be used whenever a mapper call is made.