springboot 配置mybatis的二级缓存机制

一级缓存 是sqlSession级别的缓存,面向单个访问用户
二级缓存 是namespace级别的缓存,同一个namespace内查询会缓存,一旦发生增删改操作则会清空该namespace缓存,二级缓存面向所有访问用户;二级缓存默认使用hashMap缓存,支持自定义缓存:mybatis支持encache和redis

mybatis 开启二级缓存的两种方式

首先配置 开启MyBatis的全局二级缓存
.properties文件或.yml 文件 增加配置
#推荐
mybatis.configuration.cache-enabled=true

或者

mybatis-config.xml文件增加配置
<settings>
      <!--显示的开启全局缓存-->
      <setting name="cacheEnabled" value="true"/>
</settings>
每个namespace只能选择其中一种方式
1.注解开启

//RedisCache.class为自定义配置类,实现org.apache.ibatis.cache.Cache接口
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
注解方式适用于持久层使用@Select、@insert等注解配置SQL语句
如果使用默认hashMap缓存,则@CacheNamespace

2.mapper文件开启

持久层mapper文件增加标签开启本mapper namespace下的二级缓存

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
属性说明
 a. eviction:清除策略为FIFO缓存,先进先出原则,默认的清除策略是 LRU
 b. flushInterval:属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量
 c. size:最多可以存储结果对象或列表的引用数
 d. readOnly:只读属性,可以被设置为 true 或 false。

mybatis开启redis二级缓存 demo代码

1.redis缓存配置类:实现org.apache.ibatis.cache.Cache接口
/**
 * @program: test
 * @description: mybatis 使用redis实现二级缓存
 * @author: 闲走天涯
 * @create: 2021-08-24 16:32
 */
public class RedisCache implements Cache {
    //读写锁
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);

    private RedisTemplate<String,Object> redisTemplate;

    //缓存实例id 名称
    private String id;

    public RedisCache(String id) {
        if(id==null){
            throw new IllegalArgumentException("为获取到缓存实例id");
        }
        this.id = id;
    }

    //返回cache的唯一名称
    public String getId() {
        return id;
    }

    /**
     * 获取redisTemplate对象
     * @return
     */
    public RedisTemplate<String, Object> getRedisTemplate() {
        return ApplicationContextHolder.getBean("redisTemplate");
    }

    /**
     * 缓存存值
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        getRedisTemplate().opsForHash().put(id,key.toString(),value);
    }

    /**
     * 缓存取值
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        return getRedisTemplate().opsForHash().get(id,key.toString());
    }

    /**
     * 保留方法
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        return null;
    }

    /**
     * 清空缓存,在增删改时会自动调动
     */
    @Override
    public void clear() {
        getRedisTemplate().delete(id);
    }

    /**
     * 获取缓存数量
     * @return
     */
    @Override
    public int getSize() {
        return getRedisTemplate().opsForHash().size(id).intValue();
    }

    /**
     * 获取读写锁对象
     * @return
     */
    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.readWriteLock;
    }
}
2.配置获取容器中redisTemplate对象:实现ApplicationContextAware 类
/**
 * @program: test
 * @description: 容器类
 * @author: 闲走天涯
 * @create: 2021-08-24 17:01
 */
@Component
public class ApplicationContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

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

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

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * @param name
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz){
        checkApplicationContext();
        return applicationContext.getBean(clazz);
    }

    /**
     * 清除applicationContext静态变量.
     */
    public static void cleanApplicationContext() {
        applicationContext = null;
    }
    /**
     * 检查是否容器中是否存在
     */
    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
        }
    }
}
3.学生信息vo类
/**
 * 描述:
 * 学生信息 vo类,需要实现序列化接口
 * @author 闲走天涯
 * @create 2021/7/19 15:33
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(value = "MerchantVo", description = "商户信息")
public class Student implements Serializable {
    @ApiModelProperty(value = "学生id,对应student表 s_id")
    private Long sId;
    @ApiModelProperty(value = "学生姓名,对应student表 s_name")
    private String sName;
    @ApiModelProperty(value = "学生生日,对应student表 s_birth")
    private String sBirth;
    @ApiModelProperty(value = "学生性别,对应student表 s_sex")
    private String sSex;
}
4.持久层 接口 注解方式
/**
 * @program: test
 * @description: 二级缓存dao
 * @author: 闲走天涯
 * @create: 2021-08-24 15:11
 *
 * @CacheNamespace 开启二级缓存 (根据命名空间namespace,例如com.cache.dao.CacheDao)
 * implementation 属性:默认是PerpetualCache类,即hashMap实现
 * 如果该命名空间namespace发生增删改操作,则会将该空间的缓存全部清空
 */
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
@Repository
public interface CacheDao {
    @Select({
            "<script>",
            "select * from student s",
            "<where>",
            "<if test='sId!=null and sId!='''>",
            "AND s.s_id = #{sId}",
            "</if>",
            "<if test='sName!=null and sName!='''>",
            "AND s.s_name = #{sName}",
            "</if>",
            "</where>",
            "</script>",
    })
    Page<Student> getStudentByPage(Student student);

    @Select("select * from student s where s.s_id=#{sid}")
    Student getStudentById(@Param("sid")Long sid);

    @Update({
            "<script>",
            "update student",
            "<set>",
            "<if test='sName!=null and sName!='''>",
            "s_name=#{sName},",
            "</if>",
            "<if test='sBirth!=null and sBirth!='''>",
            "s_birth=#{sBirth},",
            "</if>",
            "<if test='sSex!=null and sSex!='''>",
            "s_sex=#{sSex},",
            "</if>",
            "</set>",
            "<where>",
            "s_id = #{sId}",
            "</where>",
            "</script>",
    })
    int updateStudent(Student student);
}
5.service接口 和 serviceImpl 业务层实现类
/**
 * @program: test
 * @description: mybatis 二级缓存service
 * @author: 闲走天涯
 * @create: 2021-08-24 15:12
 */
public interface CacheService {
    PageInfo<Student> getStudentByPage(Student student,Integer pageNum,Integer pageSize);

    Student getStudentById(Long sid);

    int updateStudent(Student student);
}

/**
 * @program: test
 * @description: 二级缓存serviceimpl
 * @author: 闲走天涯
 * @create: 2021-08-24 15:14
 */
@Service
public class CacheServiceImpl implements CacheService {

    @Autowired
    private CacheDao cacheDao;

    /**
     * 分页查询学生
     * @param student
     * @return
     */
    @Override
    public PageInfo<Student> getStudentByPage(Student student,Integer pageNum,Integer pageSize) {
        if (pageNum == null || pageNum <= 0) pageNum = 1;
        if (pageSize == null || pageSize <= 0) pageSize = 10;
        PageHelper.startPage(pageNum, pageSize);
        Page<Student> page = cacheDao.getStudentByPage(student);
        return new PageInfo<>(page);
    }

    /**
     * 根据id获取学生信息
     * @param sid
     * @return
     */
    @Override
    public Student getStudentById(Long sid){
        return cacheDao.getStudentById(sid);
    }

    /**
     * 根据id修改学生信息
     * @param student
     * @return
     */
    @Override
    public int updateStudent(Student student){
        int i = cacheDao.updateStudent(student);
        if(i==1){
            return i;
        }else{
            throw new RuntimeException("修改异常");
        }
    }
}
6.控制层controller
/**
 * @program: test
 * @description: mybatis 开启二级缓存 (自定义缓存:mybatis支持encache和redis)
 *             一级缓存以sqlSession为主,面向单个访问用户
 *            二级缓存以namespace为标识,同一个namespace内查询会缓存,一旦发生增删改操作则会清空该namespace缓存,二级缓存面向所有访问用户
 * @author: 闲走天涯
 * @create: 2021-08-24 15:10
 */
@RestController
@RequestMapping(value = "/cache")
public class CacheController {
    @Autowired
    private CacheService cacheService;

    /**
     * 分页查询学生信息
     * @param student
     * @param pageNum
     * @param pageSize
     * @return
     */
    @RequestMapping("/getStudentByPage")
    public JsonBean getStudentByPage(Student student,Integer pageNum,Integer pageSize){
        try {
            PageInfo<Student> pageInfo = cacheService.getStudentByPage(student,pageNum,pageSize);
            return new JsonBean(true, "查询成功", 1, pageInfo);
        }catch (Exception e){
            e.printStackTrace();
        }
        return new JsonBean(false,"查询异常",0,null);
    }

    /**
     * 根据学生id查询学生信息
     * @param sid
     * @return
     */
    @RequestMapping(value = "/getStudentById")
    public JsonBean getStudentById(Long sid){
        try{
            if(!VerifyData.longIsNotNull(sid)){
                return new JsonBean(false,"参数不能为空",0,null);
            }
            Student student = cacheService.getStudentById(sid);
            return new JsonBean(true,"查询成功",1,student);
        }catch (Exception e){
            e.printStackTrace();
        }
        return new JsonBean(false,"查询异常",0,null);
    }

    /**
     * 更细学生信息
     * @param student
     * @return
     */
    @RequestMapping(value = "/updateStudent")
    public JsonBean updateStudent(Student student){
        try{
            if(!VerifyData.longIsNotNull(student.getSId())){
                return new JsonBean(false,"参数不能为空",0,null);
            }
            int i = cacheService.updateStudent(student);
            return new JsonBean(true,"修改成功",1,i);
        }catch (Exception e){
            e.printStackTrace();
        }
        return new JsonBean(false,"修改异常",0,null);
    }
}

7.测试

http://127.0.0.1:1234/cache/getStudentById?sid=5

http://127.0.0.1:1234/cache/getStudentByPage

查询redis hvals com.cache.dao.CacheDao:存在缓存数据,多次重复访问,不会再次访问数据库,从redis中获取

redis做mybatis二级缓存 springboot mybatis redis二级缓存_二级缓存

http://127.0.0.1:1234/cache/updateStudent?sId=7&sName=郑竹2

更新操作后缓存被清除

redis做mybatis二级缓存 springboot mybatis redis二级缓存_二级缓存_02


重新访问

http://127.0.0.1:1234/cache/getStudentById?sid=5

需要重新访问数据库