introduce

Mybatis will store every query result in a map object. If repeated query is performed next time, it can be directly retrieved from the cache. Interaction with the database is avoided.

Problem: In distributed project, different servers use their own JVM to start the project, and each has its own myBatis level 2 cache, which leads to poor use effect of cache. Here we can choose a middleware to replace the original cache stored in Map, while here we use Redis.

Project configuration

Server. port=8090 #redis SpringData automatically creates redisTemplate and StringRedisTemplate spring.redis. Host =127.0.0.1 Port =8379 spring.redis. Password = qWER1234 # default to use the 0 library spring.redis. Database =0 ## Sentinel configuration ##master name Check the sentinel. Conf configuration inside The name # using sentinel surveillance spring. Redis. Sentinel. Master = mymaster # # even is no longer a specific redis host, Writing is multiple sentinel node # spring. Redis.. Sentinel nodes = 127.0.0.1:26379 # redis cluster operation writing all the nodes in the cluster # spring. Redis. The cluster. Nodes = 192.168.2.2:7000192168 2.2:8888192168 2.2:3564 # configuration database spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/examine? characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=123456 mybatis.mapper-locations=classpath:/mapper/*.xml mybatis.type-aliases-package=com.exit.springbootredis.entity logging.level.com.exit.springbootredis.dao=debugCopy the code

The redis connection scheme is selected based on your Redis deployment architecture

Custom Mybatis level 2 cache implementation class

package com.exit.springbootredis.cache; import com.exit.springbootredis.util.ApplicationContextUtils; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.cache.CacheException; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.util.DigestUtils; /** * @author Y2M * @createTime 2021/6/30 * @comment public class redisCache implements Cache {/** * Namespace */ private final String ID; public redisCache(String id) { System.out.println("id:"+id); this.id = id; } /** ** @return Returns the unique identifier of the cache */ @override public String getId() {return ID; @override public int getSize() {RedisTemplate RedisTemplate = getRedisTemplate(); return redisTemplate.opsForHash().size(id.toString()).intValue(); } @override public void putObject(Object key, Object value) {system.out.println ("****** to cache the Object *******"); System.out.println("key:"+key); System.out.println("value:"+value); System.out.println("****** end cache object *******"); RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.opsForHash().put(id.toString(),getStringMd5(key.toString()),value); } @override public Object getObject(Object key) {system.out.println ("****** start caching that Object *******"); System.out.println("key:"+key); System.out.println("****** end from caching that object *******"); RedisTemplate redisTemplate = getRedisTemplate(); return redisTemplate.opsForHash().get(id.toString(),getStringMd5(key.toString())); } /** * @param key * @return */ @Override public Object removeObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); return redisTemplate.opsForHash().delete(id.toString(),getStringMd5(key.toString())); } @override public void clear() {system.out.println (" empty cache "); RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete(id.toString()); } @Override public boolean equals(Object o) { if (getId() == null) { throw new CacheException("Cache instances require an ID."); } if (this == o) { return true; } if (! (o instanceof Cache)) { return false; } Cache otherCache = (Cache) o; return getId().equals(otherCache.getId()); } @Override public int hashCode() { if (getId() == null) { throw new CacheException("Cache instances require an ID."); } return getId().hashCode(); } private RedisTemplate getRedisTemplate(){ RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } /** * optimize the key length * @param source * @return 32-bit hexadecimal String */ public String getStringMd5(String source){return DigestUtils.md5DigestAsHex(source.getBytes()); }}Copy the code
package com.exit.springbootredis.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Configuration; /** * @author Y2M * @createTime 2021/6/30 * @comment */ @configuration public class ApplicationContextUtils implements ApplicationContextAware {/** * private Static ApplicationContext applicationContext; * @param applicationContext * @throws BeansException */ @override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextUtils.applicationContext = applicationContext; } /** * public static Object getBean(String beanName){return applicationContext.getBean(beanName); }}Copy the code

The ApplicationContextUtils class is used to retrieve the factory ApplicationContext created by SpringBoot. Why get ApplicationContext? RedisCache is not in the Spring IOC container because we need to customize a class that implements the ApplicationContextAware interface, retrieved in the setApplicationContext method, We then get the RedisTemplate from the ApplicationContext to manipulate redis.

The redisCache class can look at the comments, so let’s see why we have getStringMd5. The key is generated by Redis, it’s very long, and you can look at it yourself. Md5 encryption is used to reduce the length, and of course md5 is used because MD5 encrypts different objects differently. Both characters are 32-bit hexadecimal characters.

Enable mybatis level 2 cache

<? The XML version = "1.0" encoding = "utf-8"? > <! DOCTYPE mapper PUBLIC "- / / mybatis.org//DTD mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > < mapper namespace="com.exit.springbootredis.dao.MzroomDao"> <! Mybatis: PerpetualCache --> <! --<cache type="com.exit.springbootredis.cache.redisCache"/>--> <! - correlation of cache Used to put multiple relationship with query query cache in a to deal with -- -- > < cache - ref namespace = "com. Exit. Springbootredis. Dao. VoteRecordDao" / > < select id="findAll" resultType="com.exit.springbootredis.entity.Mzroom"> select * from mzroom </select> <insert id="insert" parameterType="com.exit.springbootredis.entity.Mzroom"> insert into mzroom (id,room_id,`name`,phone,relation) value (null,#{room_id},#{name},#{phone},#{relation}) </insert> </mapper>Copy the code














Simply speaking, the two DAos have duplicate operation tables. Adao. class can modify the school information, while bdao.class can modify the department information of the school. If the
tag is used, adao. class will clear not only its own level cache but also the bDAo.class cache. Use the
tag to clear only your own cache.

The test class

package com.exit.springbootredis; import com.exit.springbootredis.entity.Mzroom; import com.exit.springbootredis.service.MzroomService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; /** * @author Y2M * @createTime 2021/6/30 * @comment */ @SpringBootTest(classes = SpringbootRedisApplication.class) @RunWith(SpringRunner.class) public class TestMzroomService { @Autowired private MzroomService mzroomService; @test public void findAllNoCache(){List<Mzroom> all = mzRoomservice.findAll (); @test public void findAllNoCache(){List<Mzroom> all = mzroomService. all.forEach(mzroom -> System.out.println(mzroom)); System.out.println("================="); List<Mzroom> allNew = mzRoomservice.findAll (); allNew.forEach(mzroom -> System.out.println(mzroom)); } /** * add <cache/> * to the mapper file. If you look at the query statement in the console, it will not query the database. Instead, it will retrieve it from the cache public void findAllJvmCache(){ List<Mzroom> all = mzroomService.findAll(); all.forEach(mzroom -> System.out.println(mzroom)); System.out.println("================="); List<Mzroom> allNew = mzRoomservice.findAll (); allNew.forEach(mzroom -> System.out.println(mzroom)); } / * * * open cache mapper file < cache type = "com. Exit. Springbootredis. Cache. RedisCache" / > > * look at the console of the query He won't join database queries But taken from the cache * * How do I update the cache when the data changes? Solutions: Override RedisCache's clear method */ @test public void findAllRedisCache(){List<Mzroom> all = mzRoomservice.findAll (); all.forEach(mzroom -> System.out.println(mzroom)); System.out.println("================="); Mzroom addMzroom = new Mzroom(); AddMzroom. Elegantly-named setName (" rose "). The setPhone (" 753159 "). SetRelation (" flower village ".) setRoom_id (" 8888 "); mzroomService.insert(addMzroom); List<Mzroom> allNew = mzroomService.findAll(); allNew.forEach(mzroom -> System.out.println(mzroom)); }}Copy the code