MyBatis 缓存之Redis简单实现

匿名 (未验证) 提交于 2019-12-03 00:44:02

MyBatis 提供的缓存机制都是基于Cache 接口而实现,因此我们也可以通过实现该接口创建自定义的缓存实现。

简单来说,在MyBatis开启二级缓存的前提下,通过使用自定义的缓存实现类,使用Redis完成对缓存信息的查询和更新。

先来看一下 maven 依赖,本文使用的是Spring boot框架,依赖信息相对简单清晰。

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency>    <groupId>org.mybatis.spring.boot</groupId>    <artifactId>mybatis-spring-boot-starter</artifactId>    <version>1.3.2</version> </dependency>

再来看一下具体的实现类,

public class RedisCache implements Cache {     private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);      private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();     private final String id;     private RedisTemplate redisTemplate;     private static final long EXPIRE_TIME_IN_MINUTES = 30;      public RedisCache(String id){         if(id == null)             throw new IllegalArgumentException("Cache instance requires an ID");          this.id = id;     }      @Override     public String getId() {         return this.id;     }      private RedisTemplate getRedisTemplate() {         if(null == this.redisTemplate){             logger.debug("set redisTemplate");             this.redisTemplate = ApplicationContextHolder.getBean("redisTemplate");         }         return this.redisTemplate;     }      public void setRedisTemplate(RedisTemplate redisTemplate) {         this.redisTemplate = redisTemplate;     }      @Override     public void putObject(Object key, Object value) {         ValueOperations opsForValue = this.getRedisTemplate().opsForValue();         opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);         logger.debug("Put query result to redis");     }      @Override     public Object getObject(Object key) {         ValueOperations opsForValue = this.getRedisTemplate().opsForValue();         logger.debug("Get cached query result from redis");         return opsForValue.get(key);     }      @Override     public Object removeObject(Object key) {         this.getRedisTemplate().delete(key);         logger.debug("Remove cached query result from redis");         return key;     }      @Override     public void clear() {         this.getRedisTemplate().execute(new RedisCallback<Object>() {             @Override             public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {                 logger.debug("flush redis");                 redisConnection.flushDb();                 return null;             }         });     }      @Override     public int getSize() {         return 0;     }      @Override     public ReadWriteLock getReadWriteLock() {         return this.readWriteLock;     } }

这里我们采用了 RedisTemplate 实现对缓存的操作,而不是采用 jedis,原因在于Jedis是Redis官方推荐的面向Java的操作Redis的客户端,而RedisTemplate是SpringDataRedis中对JedisApi的高度封装。SpringDataRedis相对于Jedis来说可以方便地更换Redis的Java客户端,比Jedis多了自动管理连接池的特性,方便与其他Spring框架进行搭配使用。

需要注意的是,此处的 redisTemplate 属性不同通过 autowired 方式获得,原因在于 RedisCache 本身就不是一个bean,因此我们考虑使用一个辅助类实线bean的获取。

public class ApplicationContextHolder implements ApplicationContextAware {      private static ApplicationContext context;      @Override     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {         context = applicationContext;     }      public static ApplicationContext getApplicationContext(){         return context;     }      public static <T> T getBean(Class<T> clazz){         return context.getBean(clazz);     }      public static <T> T getBean(String name){         return (T)context.getBean(name);     } }

最后还需要针对缓存做一些配置

<cache eviction="LRU" type="com.learning.cache.cache.RedisCache" flushInterval="30000" size="1024" readOnly="true"/>

mapper 文件内容相对简单,主要是基于主键信息的增删查改操作,此处不再赘述。

当我们使用浏览器发送下述请求时,能获得如下的相应信息,

而此时,Redis 数据库中存在如下的 key-value 信息,

当我们再次发送上述请求能够获得同样的响应信息,观察如下日志信息,

  • 第一次请求的日志信息

  • 第二次请求的日志信息

可以看出,每次查询的时候先查询缓存中是否存在相关信息,如果存在直接返回,如果不存在,则去访问数据库,并将结果信息写入缓存以供下次查询。

同样,我们可以借助 RESTClient 工具发送 post 请求,实现对数据库信息的修改,

而此时,Redis 数据库中的信息已被刷新,

这也说明了二级缓存的刷新时机,即事务的提交会刷新该namespace下的二级缓存。

本文描述了一种基于 RedisTemplate 的分布式缓存的简单实现方式,当然还有很多不足之处,比如缓存数据库崩溃等异常情况未考虑,希望能对读者在创建分布式缓存时提供一种思路。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!