mybatis 自定义redis做二级缓存

前言

如果关注功能实现,可以直接看功能实现部分

何时使用二级缓存

一个宗旨---不常变的稳定而常用的

  • 一级是默认开启的sqlsession级别的。
  • 只在单表中使用,且所有的操作都是一个namespace下
  • 查询多 增删改少的情况下
  • 缓存并不全是优点,缺点很明显,缓存有时不是最新的数据。

二级缓存参数说明

这是一个跨Sqlsession级虽的缓存,是mapper级别的,也就是可以多个sqlsession访问同一个mapper时生效

关键字 解读
eviction 缓存回收策略
flushInterval 刷新时间间隔,单位毫秒
size 引用数目,代表缓存最多可以存多少对象
readOnly 是否只读默认false 如果True所有的sql返回的是一个对象,性能高并发安全性底,如果false返回的是序列化后的副本,安全高效率底

常用的回收策略(与操作系统内存回收策略差不多)

  • LRU 默认,最近最少使用
  • FIFO 先进先出
  • SOFT 软引用 移除基于垃圾回收器状态和软引用规则的对象
  • WEAK 弱引用 更积极的移除移除基于垃圾回收器状态和弱引用规则的对象

sql中控制是否使用缓存及刷新缓存

<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<select id="" resultMap="" useCache="false">
<update id="" parameterType="" flushCache="false" />

缓存何时会失效(意思就是构成key的因素一样,但是没有命中value)

一级失效

  • 不在同一个Sqlsession中,例如未开启事务,mybatis每次查询都会关闭旧的创建新的。
  • 增删改操作程序会clear缓存
  • 手动执行sqlsession.clearCache()

二级失效

  • insert ,update,delete语句的执行
  • 超时
  • 被回收策略回收

功能实现

添加依赖

<!-- 集成了lettuce的连接方式也可以用jedis方式看自己建议用集成的说明稳定些 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 序列化 -->
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- 自定义获取Bean -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- 断言判断,正式环境中可以使用 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

启动二级缓存

  • 可以配置在调用mapper的项目中,方便以后维护
spring:
redis:
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
#集群的方式
# sentinel:
# master: mymaster
# nodes: 192.168.15.154:6379
database: 0
host: 192.168.15.154
port: 6379
# MyBatis Config properties
mybatis:
type-aliases-package:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
cache-enabled: true

自定义相关代码

  • 确保所有POJO都实现了序列化并声明了序列号
  private static final long serialVersionUID =-1L;
  • 自定义实现缓存接口
/**
* @author lyy
* @description
* @date 2019/9/2
*/
public class RedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class); private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// cache instance id
private final String id;
private RedisTemplate redisTemplate;
// redis过期时间
private static final long EXPIRE_TIME_IN_MINUTES = 30; public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
} @Override
public String getId() {
return id;
} /**
* Put query result to redis
*
* @param key
* @param value
*/
@Override
public void putObject(Object key, Object value) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
logger.debug("Put query result to redis");
} catch (Throwable t) {
logger.error("Redis put failed", t);
}
} /**
* Get cached query result from redis
*
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
logger.debug("Get cached query result from redis");
return opsForValue.get(key);
} catch (Throwable t) {
logger.error("Redis get failed, fail over to db", t);
return null;
}
} /**
* Remove cached query result from redis
*
* @param key
* @return
*/
@Override
@SuppressWarnings("unchecked")
public Object removeObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
logger.debug("Remove cached query result from redis");
} catch (Throwable t) {
logger.error("Redis remove failed", t);
}
return null;
} /**
* Clears this cache instance
*/
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.execute((RedisCallback) connection -> {
connection.flushDb();
return null;
});
logger.debug("Clear all the cached query result from redis");
} /**
* This method is not used
*
* @return
*/
@Override
public int getSize() {
return 0;
} @Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
} private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
}
  • 定义ApplicationContextHolder来实现Bean的注入和获取

/**
* @author lyy
* @description
* @date 2019/9/2
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(ApplicationContextHolder.class); private static ApplicationContext applicationContext; public static ApplicationContext getApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException(
"'applicationContext' property is null,ApplicationContextHolder not yet init.");
}
return applicationContext;
} /**
*      * 根据bean的id来查找对象
*      * @param id
*      * @return
*      
*/
public static Object getBeanById(String id) {
checkApplicationContext();
return applicationContext.getBean(id);
} /**
*      * 通过名称获取bean
*      * @param name
*      * @return
*      
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
Object object = applicationContext.getBean(name);
return (T) object;
} /**
*      * 根据bean的class来查找对象
*      * @param c
*      * @return
*      
*/
@SuppressWarnings("all")
public static <T> T getBeanByClass(Class<T> c) {
checkApplicationContext();
return (T) applicationContext.getBean(c);
} /**
*      * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
* 如果有多个Bean符合Class, 取出第一个.
*      * @param cluss
*      * @return
*      
*/
public static <T> T getBean(Class<T> cluss) {
checkApplicationContext();
return (T) applicationContext.getBean(cluss);
} /**
*      * 名称和所需的类型获取bean
*      * @param name
*      * @param cluss
*      * @return
*      
*/
public static <T> T getBean(String name, Class<T> cluss) {
checkApplicationContext();
return (T) applicationContext.getBean(name, cluss);
} public static <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
checkApplicationContext();
return applicationContext.getBeansOfType(type);
} /**
* 检查ApplicationContext不为空.
*/
private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义ApplicationContextHolderGm");
}
} @Override
public void destroy() throws Exception {
checkApplicationContext();
} /**
* 清除applicationContext静态变量
*/
public static void cleanApplicationContext() {
applicationContext = null;
} @Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
//checkApplicationContext();
applicationContext = context;
logger.info("holded applicationContext,显示名称:" + applicationContext.getDisplayName());
}
}

启用缓存

  • 注解的方式,注解在mapper接口上
@CacheNamespace(implementation = RedisCache.class)
  • xml的方式
 <cache type="***.RedisCache">
<property name="eviction" value="LRU"/>
<property name="flushInterval" value="60000000"/>
<property name="size" value="2048"/>
<property name="readOnly" value="false"/>
</cache>

为什么使用redis?除了redis还有其它什么?

  1. redis是一个高性能nosql库,配上集群稳定高效,因为默认的二级缓存是在内存中的。这样可以把缓存独立出来,也是所有第三方map结构做二级缓存的优点
  2. 可以更好的适应分布式
  3. 还有ehcache,还有理论上的所有nosql库应该都可以

注意事项

缓存不生效(或者叫缓存穿透)

  • 注解的sql使用注解开启,xml配置的sql要在xml中开启,混用不生效
  • windows版的redis要配置一下允许外网连接,即使把redis.windows.conf中的bind 127.0.0.1注释掉了也不行
# Examples:
#
# bind 192.168.1.100 10.0.0.1
bind 0.0.0.0
  • intellij idea 生成序列号,下面选中后放光标放在类是,不是接口上按alt+enter
File -> Settings -> Inspections -> Serialization issues -> Serialization class without ‘serialVersionUID’

mybatis 使用redis实现二级缓存(spring boot)的更多相关文章

  1. spring boot:使用caffeine+redis做二级缓存(spring boot 2.3.1)

    一,为什么要使用二级缓存? 我们通常会使用caffeine做本地缓存(或者叫做进程内缓存), 它的优点是速度快,操作方便,缺点是不方便管理,不方便扩展 而通常会使用redis作为分布式缓存, 它的优点 ...

  2. SpringMVC +Spring + MyBatis + Mysql + Redis(作为二级缓存) 配置

    转载:http://blog.csdn.net/xiadi934/article/details/50786293 项目环境: 在SpringMVC +Spring + MyBatis + MySQL ...

  3. SpringMVC + MyBatis + Mysql + Redis(作为二级缓存) 配置

    2016年03月03日 10:37:47 标签: mysql / redis / mybatis / spring mvc / spring 33805 项目环境: 在SpringMVC + MyBa ...

  4. Mybatis整合Redis实现二级缓存

    Mybatis集成ehcache . 为什么需要缓存 拉高程序的性能 . 什么样的数据需要缓存 很少被修改或根本不改的数据 业务场景比如:耗时较高的统计分析sql.电话账单查询sql等 . ehcac ...

  5. springboot+mybatis 用redis作二级缓存

    1.加入相关依赖包: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=" ...

  6. mybatis结合redis实战二级缓存(六)

    之前的文章中我们意见分析了一级缓存.二级缓存的相关源码和基本原理,今天我们来分享下了mybatis二级缓存和redis的结合,当然mybatis二级缓存也可以和ehcache.memcache.OSC ...

  7. SpringBank 开发日志 Mybatis 使用redis 作为二级缓存时,无法通过cacheEnabled=false 将其关闭

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC ...

  8. mybatis结合redis实战二级缓存

    之前的文章中我们意见分析了一级缓存.二级缓存的相关源码和基本原理,今天我们来分享下了mybatis二级缓存和redis的结合,当然mybatis二级缓存也可以和ehcache.memcache.OSC ...

  9. SSM+redis整合(mybatis整合redis做二级缓存)

    SSM:是Spring+Struts+Mybatis ,另外还使用了PageHelper 前言: 这里主要是利用redis去做mybatis的二级缓存,mybaits映射文件中所有的select都会刷 ...

随机推荐

  1. [LeetCode] 由 “中缀表达式 --> 后缀表达式" 所想

    如何利用栈解决问题. Ref: 如何在程序中将中缀表达式转换为后缀表达式? 本文的引申:如何手写语法分析器 实现调度场算法 “9+(3-1)*3+10/2” --> “9 3 1-3*+ 10 ...

  2. XStream实现javabean和xml、json转化

    xStream转换XML.Json数据 xStream可以轻易的将javaBean对象和xml相互转换,修改某个特定的属性和节点名称,而且也支持json的转换. maven依赖: 1 <depe ...

  3. Moonraker 靶机渗透

    0x01 简介 攻击Moonraker系统并且找出存在最大的威胁漏洞,通过最大威胁漏洞攻击目标靶机系统并进行提权获取系统中root目录下的flag信息. Moonraker: 1镜像下载地址: htt ...

  4. 【linux】【FastDFS】FastDFS上传返回的url直接下载和下载文件的文件名问题

    FastDFS安装及其他问题参考:https://www.cnblogs.com/jxd283465/p/11556263.html直接调用FastDFS返回的url,浏览器访问后默认打开方式./us ...

  5. java数据结构——递归(Recursion)例题持续更新中

    继续学习数据结构递归,什么是递归呢?字面理解就是先递出去,然后回归,递归核心思想就是直接或间接调用本身,好比从前有座山,山里有位老和尚,在给小和尚讲故事,讲的是从前有座山,山里有位老和尚,在给小和尚讲 ...

  6. 转:怎么用Sql语句获取一个数据库中的所有表的名字

    用sql获取数据库中所有的表名的方法:1.oracle下:select table_name from all_tables;2.MySQL下:select table_name from infor ...

  7. Spring 梳理-传递模型数据到视图

    传递model,返回string,不指定返回key package com.jt; import com.myOrg.DeptUserDomain; @Controller @RequestMappi ...

  8. python 处理excel踩过的坑——data_only,公式全部丢失

    用openpyxl读取excel的load_workbook有个data_only参数. yb_wb = load_workbook(u"D:\\Desktop\\xxx.xlsx" ...

  9. Ionic2优于Ionic1的6个理由

    经历了一个从0到有的app的开发,我已经很熟悉Ionic1了,在此期间我曾发现过Ionic1的一些bug,和一些不合理的地方(根基版本 不同,后续我会陆续发表这些文章),我甚至在此期间对Ionic1进 ...

  10. 确认自己所用的python版本

    总结: 目前有两个版本的python处于活跃状态:python2,python3 有多种流行的python运行环境:cpython(应用最广泛的python解释器,如无对解释器有要求,一般用这个,默认 ...