Redis实现MyBatis的二级缓存

Redis实现MyBatis的二级缓存

  • 前言
  • 一、pandas是什么?
  • 二、Mybatis的二级缓存的实现
    • 1. 配置文件中开启二级缓存
    • 2.实现Mybatis的Cache接口
    • 3. 二级缓存的实用
    • 4. Redis中的存储
  • 参考文档


前言


一、pandas是什么?

通大多数ORM层框架一样,Mybatis自然也提供了对一级缓存和二级缓存的支持。一下是一级缓存和二级缓存的作用于和定义。
1、一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
2、一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写 到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存 也就不存在了。Mybatis默认开启一级缓存。
二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同 namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二 次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。
一般的我们将Mybatis和Spring整合时,mybatis-spring包会自动分装sqlSession,而Spring通过动态代理 sqlSessionProxy使用一个模板方法封装了select()等操作,每一次select()查询都会自动先执行openSession(), 执行完close()以后调用close()方法,相当于生成了一个新的session实例,所以我们无需手动的去关闭这个session(),当然也无 法使用mybatis的一级缓存,也就是说mybatis的一级缓存在spring中是没有作用的
因此我们一般在项目中实现Mybatis的二级缓存,虽然Mybatis自带二级缓存功能,但是如果实在集群环境下,使用自带的二级缓存只是针对单个的节 点,所以我们采用分布式的二级缓存功能。一般的缓存NoSql数据库如redis,Mancache等,或者EhCache都可以实现,从而更好地服务 tomcat集群中ORM的查询。

二、Mybatis的二级缓存的实现

下面主要通过Redis实现Mybatis的二级缓存功能。

1. 配置文件中开启二级缓存

mybatis:
  mapper-locations: classpath:mapping/*Mapper.xml
  type-aliases-package: com.*.traffic
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true # 开启二级缓存

2.实现Mybatis的Cache接口

Mybatis提供了第三方Cache实现的接口,我们自定义MybatisRedisCache实现Cache接口,代码如下:


import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Description Redis对Mybatis的缓存
 * @Author Jack.Hu
 */
public class MybatisRedisCache implements Cache {

    private static Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);
    // 读写锁
    private final ReadWriteLock lock = new ReentrantReadWriteLock(true);

    // 这里使用redis缓存,使用springBoot自动注入
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private String id;

    public MybatisRedisCache(final String id) {
        if (null == id) {
            throw new IllegalStateException("Cache instances require an ID");
        }
        this.id = id;
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public void putObject(Object key, Object value) {
        if (redisTemplate == null) {
            redisTemplate = (RedisTemplate<String, Object>) SpringContextHolder.getApplicationContext().getBean("redisTemplate");
        }
        if (value != null) {
            redisTemplate.opsForValue().set(key.toString(), value);
        }
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.lock;
    }

    @Override
    public Object getObject(Object key) {
        try {
            if (null != key) {
                return redisTemplate.opsForValue().get(key.toString());
            }
        } catch (Exception e) {
            log.error("缓存错误");
        }
        return null;
    }

    @Override
    public Object removeObject(Object key) {
        if (null != key) {
            return redisTemplate.delete(key.toString());
        }
        return null;
    }

    @Override
    public void clear() {
        log.debug("清空缓存");
        if (null == redisTemplate) {
            redisTemplate = (RedisTemplate<String, Object>) SpringContextHolder.getApplicationContext().getBean("redisJsonTemplate");
        }
        Set<String> keys = redisTemplate.keys("*:" + this.id + "*");
        for (String key : keys) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 获取缓存中的缓存数量
     *
     * @return
     */
    @Override
    public int getSize() { // 获取缓存中的缓存数量
        return redisTemplate.execute((RedisCallback<Long>) RedisServerCommands::dbSize).intValue();
    }


}

注意:由于RedisTemplate的注入可能在应用启动期间没有成功进行注入到缓存内中,需要在使用缓存时中进行手动注入,这是需要从应用的上下文获取RedisTemplate,需要定义一个获取应用上下文的类实现ApplicationContextAware接口,通过这个类来获取应用上下文,通过应用上下文来获取注册到应用上下文的bean!


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @ClassName SpringContextHolder
 * @Description TODO
 * @Author Jack Hu
 */
@Component
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext(){
        assertApplicationContext();
        return applicationContext;
    }

    public static <T>T getBean(String beanName){
        assertApplicationContext();
        return (T)applicationContext.getBean(beanName);
    }

    public static <T>T getBean(Class<T> requiredType){
        assertApplicationContext();
        return applicationContext.getBean(requiredType);
    }

    private static void assertApplicationContext(){
        if(null == SpringContextHolder.applicationContext){
            throw new RuntimeException("applicationContext属性为null,请检查是否注入成功!");
        }
    }

}

3. 二级缓存的实用

<cache  
    type="org.andy.shop.cache.MybatisRedisCache"  
eviction="LRU"  
flushInterval="6000000"  
size="1024"  
readOnly="false"  
/>  
  1. eviction:缓存的回收策略
  • LRU - 最近最少使用,移除最长时间不被使用的对象
  • FIFO - 先进先出,按对象进入缓存的顺序来移除它们
  • SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
  • WEAK - 弱引用,更积极地移除基于垃圾收集器和弱引用规则的对象
  • 默认的是LRU

flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值

readOnly:是否只读
true:只读:mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取数据,直接就会将数据在缓存中的引用交给用户 。不安全,速度快
false:读写(默认):mybatis觉得获取的数据可能会被修改
mybatis会利用序列化&反序列化的技术克隆一份新的数据给你。安全,速度相对慢

size:缓存存放多少个元素

type:指定自定义缓存的全类名(实现Cache接口即可)

4. Redis中的存储

redis会自动的将Sql+条件+Hash等当做key值,而将查询结果作为value,只有请求中的所有参数都符合,那么就会使用redis中的二级缓存。

Redis实现MyBatis的二级缓存_第1张图片


参考文档

Redis实现Mybatis的二级缓存
mybatis cache标签的参数
SpringBoot配置Redis对Mybatis的二级缓存进行缓存

你可能感兴趣的:(#)