首先来了解下mybatis 缓存,mybatis缓存分为一级缓存和二级缓存。一级缓存是默认开启的,无需其他配置操作,二级缓存则需要手动设置开启。

一级缓存原理:

Mybatis的一级缓存是指同一个SqlSession中的操作。一级缓存的作用域是一个SqlSession。
在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则SqlSession的缓存清空。

二级缓存原理:

Mybatis的二级缓存是指mapper映射文件。二级缓存是多个sqlSession共享的,其作用域是mapper下的同一个namespace。
在不同的sqlSession中,相同的namespace下,相同的查询sql语句并且参数也相同的情况下,会命中二级缓存。如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存。

了解一些基本原理后,我们开始在springboot集成mybatis的情况下,开启二级缓存。

  1. 在pom.xml文件中引入mybatis和redis的依赖
        <!--mybatis 依赖包--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--redis lettuce--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>
  1. 在application.yml文件中配置mybatis相关设置时,开启二级缓存
### mybatis相关配置
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#开启MyBatis的二级缓存cache-enabled: truemapper-locations: classpath*:mappers/*Mapper.xml### Redis 相关配置
redis:host: localhostport: 6379timeout: 10000database: 0lettuce:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0
  1. 实体类实现序列化

我们采用的redis序列化方式是默认的jdk序列化。所以数据库的查询对象(比如Student类)需要实现Serializable接口。

public class Student implements Serializable {//采用的redis序列化方式是默认的jdk序列化。所以数据库的查询对象实体需要实现Serializable接口。private static final long serialVersionUID = 1L;private int id;private String name;private int age;private String position;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", position='" + position + '\'' +'}';}
}
  1. 先看一下Redis的配置类(这里用的是lettuce)
@Configuration
public class RedisConfig {@Autowiredprivate LettuceConnectionFactory connectionFactory;@Beanpublic RedisTemplate<String,Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();initDomainRedisTemplate(redisTemplate, connectionFactory);return redisTemplate;}/*** 设置数据存入 redis 的序列化方式* @param template* @param factory*/private void initDomainRedisTemplate(RedisTemplate<String, Object> template,LettuceConnectionFactory factory) {// 定义 key 的序列化方式为 string// 需要注意这里Key使用了 StringRedisSerializer,那么Key只能是String类型的,不能为Long,Integer,否则会报错抛异常。StringRedisSerializer redisSerializer = new StringRedisSerializer();template.setKeySerializer(redisSerializer);// 定义 value 的序列化方式为 json@SuppressWarnings({"rawtypes", "unchecked"})Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setValueSerializer(jackson2JsonRedisSerializer);//hash结构的key和value序列化方式template.setHashKeySerializer(jackson2JsonRedisSerializer);template.setHashValueSerializer(jackson2JsonRedisSerializer);template.setEnableTransactionSupport(true);template.setConnectionFactory(factory);}
}
  1. 缓存配置类
public class MybatisRedisCache implements Cache {private static final Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);private String id;private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间public MybatisRedisCache(String id) {this.id = id;}private RedisTemplate<Object, Object> getRedisTemplate(){return ApplicationContextHolder.getBean("redisTemplate");}@Overridepublic String getId() {return id;}@Overridepublic void putObject(Object key, Object value) {RedisTemplate redisTemplate = getRedisTemplate();redisTemplate.boundHashOps(getId()).put(key, value);log.info("[结果放入到缓存中: " + key + "=" + value+" ]");}@Overridepublic Object getObject(Object key) {RedisTemplate redisTemplate = getRedisTemplate();Object value = redisTemplate.boundHashOps(getId()).get(key);log.info("[从缓存中获取了: " + key + "=" + value+" ]");return value;}@Overridepublic Object removeObject(Object key) {RedisTemplate redisTemplate = getRedisTemplate();Object value = redisTemplate.boundHashOps(getId()).delete(key);log.info("[从缓存删除了: " + key + "=" + value+" ]");return value;}@Overridepublic void clear() {RedisTemplate redisTemplate = getRedisTemplate();redisTemplate.delete(getId());log.info("清空缓存!!!");}@Overridepublic int getSize() {RedisTemplate redisTemplate = getRedisTemplate();Long size = redisTemplate.boundHashOps(getId()).size();return size == null ? 0 : size.intValue();}@Overridepublic ReadWriteLock getReadWriteLock() {return readWriteLock;}
}

ps:
重点部分就是重写这个mybatis的cache类,它只会对配置文件类型的映射文件起作用。
该接口共有以下五个方法:
String getId():mybatis缓存操作对象的标识符。一个mapper对应一个mybatis的缓存操作对象。
void putObject(Object key, Object value):将查询结果塞入缓存。
Object getObject(Object key):从缓存中获取被缓存的查询结果。
Object removeObject(Object key):从缓存中删除对应的key、value。只有在回滚时触发。
void clear():发生更新时,清除缓存。
int getSize():可选实现。返回缓存的数量。
ReadWriteLock getReadWriteLock():可选实现。用于实现原子性的缓存操作。

上述重写cache类中有几个关键点:
  • 自定义实现的二级缓存,必须要有一个带id的构造函数,否则会报错。
  • 此处使用Spring封装的redisTemplate来操作Redis。很多都是直接用jedis库,但是现在springboot2.x 以上对lettuce的兼容更好。RedisTemplate封装了底层的实现,使用redisTemplate会更加方便,无论是使用jedis还是使用lettuce,我们可以直接更换底层的库,无需修改上层代码。
  • 这里不能通过@Autowire的方式引用redisTemplate,因为RedisCache并不是Spring容器里的bean。所以我们需要手动地去调用容器的getBean方法来拿到这个bean,那么这样,我们就需要引入ApplicationContextHolder这个类。
  1. ApplicationContextHolder.java (我们需要通过这个类得到RedisTemplate)
@Component
public class ApplicationContextHolder implements ApplicationContextAware{private static ApplicationContext applicationContext;/*** 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.*/public void setApplicationContext(ApplicationContext applicationContext) {ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR}/*** 取得存储在静态变量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {checkApplicationContext();return applicationContext;}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(String name) {checkApplicationContext();return (T) applicationContext.getBean(name);}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(Class<T> clazz) {checkApplicationContext();return (T) applicationContext.getBeansOfType(clazz);}/*** 清除applicationContext静态变量.*/public static void cleanApplicationContext() {applicationContext = null;}private static void checkApplicationContext() {if (applicationContext == null) {throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");}}
}
  1. 然后再映射文件中开启二级缓存(使用二级缓存)
<mapper namespace="com.example.demo.dao.StudentDao"><!-- 开启基于redis的二级缓存 --><cache type="com.example.demo.redis.cache.MybatisRedisCache"/><cache/><insert id="insert" parameterType="com.example.demo.entity.Student" useGeneratedKeys="true" keyProperty="id">insert intostudents(name,age,position)values(#{name},#{age},#{position})</insert><insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">insert intostudents(name,age,position)values<foreach collection="studentList" item="item" index="index" open="" close="" separator=",">(#{item.name},#{item.age},#{item.position})</foreach></insert><delete id="delete" parameterType="java.lang.String">delete from students where name = #{name}</delete><!--并且在update语句中,设置flushCache为true,这样在更新信息时,能够自动失效缓存(本质上调用的是clear方法)--><update id="update" parameterType="com.example.demo.entity.Student" flushCache="true">update studentsset students.position = #{position}where name = #{name}</update><select id="findByName" resultMap="BaseResultMap">select *from studentswhere name = #{name}</select><select id="findAll" resultMap="BaseResultMap">select *from students</select><resultMap id="BaseResultMap" type="com.example.demo.entity.Student"><result column="name" property="name"/><result column="age" property="age"/><result column="position" property="position"/></resultMap>
</mapper>

下面是我在实现二级缓存过程中一些报错问题:

  1. 在我修改了序列化问题后,报错消失。

小白入门,在实践过程中参考了很多优秀的博客:
https://my.oschina.net/ljc94/blog/1504320
https://blog.csdn.net/qq_40008535/article/details/83446123

Springboot 集成 mybatis 开启二级缓存(redis)相关推荐

  1. mybatis开启二级缓存和懒加载,类型别名,类都简称

    SqlMapConfig.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE conf ...

  2. mybatis开启二级缓存

    二级缓存是Mapper级别的缓存,多个SqlSession去操作同一个Mapper中的SQL语句,则这些SqlSession可以共享二级缓存,即二级缓存是跨SqlSession的. 配置: 1.在ym ...

  3. 使用Redis做Mybatis的二级缓存

    文章目录 前言 一.二级缓存 二.使用步骤 1.开启二级缓存 2.编写ApplicationContextHolder 3.编写RedisCache二级缓存工具类 4.在mapper.xml文件中开启 ...

  4. Mybatis一级缓存和二级缓存 Redis缓存

    一级缓存 Mybatis的一级缓存存放在SqlSession的生命周期,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对 ...

  5. MyBatis 缓存详解-什么时候开启二级缓存?

    一级缓存默认是打开的,二级缓存需要配置才可以开启.那么我们必须思考一个问题,在什么情况下才有必要去开启二级缓存? 1.因为所有的增删改都会刷新二级缓存,导致二级缓存失效,所以适合在查询为主的应用中使用 ...

  6. Mybatis实现二级缓存

    目录 一.Mybatis实现Ehcache作为二级缓存 1.导入相关依赖 2 .修改日志配置,因为ehcache使用了Slf4j作为日志输出 3. 在Resource中添加一个ehcache.xml的 ...

  7. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)...

    SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 前言 表结构 maven配置 配置Druid 配置mybatis ...

  8. SpringBoot教程(十一) | SpringBoot集成Mybatis

    上一篇文章我们介绍了SpringBoot集成JdbcTemplate.简单体验了一下JdbcTemplate框架的用法,今天的内容比较重要,我们来介绍一下SpringBoot集成Mybatis的步骤. ...

  9. Mybatis 之 二级缓存

    1.二级缓存 二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取.但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的names ...

最新文章

  1. 防治交换机窃听技术_等保2.0建设基本要求(技术部分)解读(下)
  2. 一文读懂 K8s 持久化存储流程
  3. java生成word 框勾_Java 使用模板生成 Word 文件---基于 Freemarker 模板框架
  4. oracle数据库常用的语法与复合函数
  5. 使用各类BeanUtils的时候,切记注意这个坑!
  6. 申请Let's Encrypt永久免费SSL证书
  7. iOS开发多线程-RunLoop
  8. 洛谷 P4017 最大食物链计数
  9. php mysql存储中文为空_PHP如何解决MySQL存储数据中文乱码
  10. Ubuntu编译并安装voip服务器软件Asterisk
  11. html的介绍及常用标签,吊打面试官系列!
  12. Jexi设计 (1) Lexi研究
  13. 搭建Web服务器-迅为IMX6ULL开发板
  14. 白嫖正版《极客时间》课程的正确姿势
  15. java环境变量含义_java环境变量的配置及各环境变量的含义
  16. C++之父B.Stroustrup言论
  17. 线下+线上双管齐下,订单管理系统助力餐饮业提升销量
  18. 如何实现不识字也能打电话——关于低代码DIY“手机通讯录”,方便老年人或文盲电话通讯方案
  19. 缩进是用来格式美化python程序的_以下对 Python 程序缩进格式描述错误的选项是...
  20. 图片局部放大镜,可在大图上下左右移动并放大对应的区域

热门文章

  1. 2022年《国家职业教育智慧平台应用推广培训》课程-答案--(中职)已验证
  2. 2.1 墒编码基本原理
  3. 华为校招有多难?千军万马过独木桥!
  4. 《新概念》英语的学习方法(完整版)
  5. class-validator中文教程
  6. 宜信 、《麻省理工科技评论》共同揭晓: 35岁以前最有可能改变世界的中国科技精英丨Xtecher 前线
  7. TeraTerm-服务器访问和数据库链接写到批处理中-双击执行
  8. Github高星项目!用Python开发的一个网易云音乐
  9. bat脚本获取cfg文件内的变量值
  10. 第二十章:异步和文件I/O.(十九) 1