• SpringBoot整合 MP 通过Redis 实现二级缓存


    一级缓存与二级缓存

    • 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 一级缓存是默认开启的不用配置。
    • 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的开启(实体类必须序列化接口)

    因为在Spring与Mybatis整合后,Mybatis的一级缓存是不能使用的,所以我们一般实现Mybatis的二级缓存,而在集群环境下,Mybatis的二级缓存只能实现单个节点的缓存,所以我们采用分布式的二级缓存,这里使用的是Redis的实现

    配置文件

    1. spring:
    2. redis:
    3. host: 127.0.0.1
    4. port: 6379
    5. database: 0
    6. #配置redis
    7. datasource:
    8. driver-class-name: com.mysql.cj.jdbc.Driver
    9. type: com.alibaba.druid.pool.DruidDataSource
    10. username: root
    11. password: root
    12. url: jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8
    13. #配置Mybatis-Plus
    14. mybatis-plus:
    15. mapper-locations: classpath:mybatis/mapper/*.xml
    16. configuration:
    17. cache-enabled: true

    在启动类上添加@EnableCaching注解

    EnableCaching:启动缓存功能
    开启缓存功能,配置类中需要加上这个注解,有了这个注解以后,spring才知道你需要使用缓存的功能,其他的和缓存相关的注解才会有效,spring中主要是通过aop实现的,通过aop来拦截需要使用缓存的方法,实现缓存的功能

    设置RedisTemplate

    1. import org.springframework.context.annotation.Bean;
    2. import org.springframework.context.annotation.Configuration;
    3. import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    4. import org.springframework.data.redis.core.RedisTemplate;
    5. @Configuration
    6. public class RedisConfiguration {
    7. /**
    8. * 设置redisTemplate
    9. */
    10. @Bean(name = "redisTemplate")
    11. public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
    12. RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
    13. redisTemplate.setConnectionFactory(redisConnectionFactory);
    14. redisTemplate.afterPropertiesSet();
    15. return redisTemplate;
    16. }
    17. }

     创建MybatisRedisCache类重写Mybatis二级缓存的Cache接口的实现

    1. import lombok.extern.slf4j.Slf4j;
    2. import org.apache.ibatis.cache.Cache;
    3. import org.springframework.data.redis.connection.RedisServerCommands;
    4. import org.springframework.data.redis.core.RedisCallback;
    5. import org.springframework.data.redis.core.RedisTemplate;
    6. import org.springframework.util.CollectionUtils;
    7. import javax.annotation.Resource;
    8. import java.util.Set;
    9. import java.util.concurrent.locks.ReadWriteLock;
    10. import java.util.concurrent.locks.ReentrantReadWriteLock;
    11. /**
    12. *
    13. * 使用redis实现Mybatis Plus二级缓存
    14. *
    15. */
    16. @Slf4j
    17. public class MybatisRedisCache implements Cache {
    18. // 读写锁
    19. private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    20. private RedisTemplate redisTemplate;
    21. private RedisTemplate getRedisTemplate(){
    22. //通过ApplicationContextHolder工具类获取RedisTemplate
    23. if (redisTemplate == null) {
    24. redisTemplate = (RedisTemplate) ApplicationContextHolder.getBeanByName("redisTemplate");
    25. }
    26. return redisTemplate;
    27. }
    28. private final String id;
    29. public MybatisRedisCache(String id) {
    30. if (id == null) {
    31. throw new IllegalArgumentException("Cache instances require an ID");
    32. }
    33. this.id = id;
    34. }
    35. @Override
    36. public String getId() {
    37. return this.id;
    38. }
    39. @Override
    40. public void putObject(Object key, Object value) {
    41. //使用redis的Hash类型进行存储
    42. getRedisTemplate().opsForHash().put(id,key.toString(),value);
    43. }
    44. @Override
    45. public Object getObject(Object key) {
    46. try {
    47. //根据key从redis中获取数据
    48. return getRedisTemplate().opsForHash().get(id,key.toString());
    49. } catch (Exception e) {
    50. e.printStackTrace();
    51. log.error("缓存出错 ");
    52. }
    53. return null;
    54. }
    55. @Override
    56. public Object removeObject(Object key) {
    57. if (key != null) {
    58. getRedisTemplate().delete(key.toString());
    59. }
    60. return null;
    61. }
    62. @Override
    63. public void clear() {
    64. log.debug("清空缓存");
    65. Set<String> keys = getRedisTemplate().keys("*:" + this.id + "*");
    66. if (!CollectionUtils.isEmpty(keys)) {
    67. getRedisTemplate().delete(keys);
    68. }
    69. }
    70. @Override
    71. public int getSize() {
    72. Long size = (Long) getRedisTemplate().execute((RedisCallback<Long>) RedisServerCommands::dbSize);
    73. return size.intValue();
    74. }
    75. @Override
    76. public ReadWriteLock getReadWriteLock() {
    77. return this.readWriteLock;
    78. }
    79. }

    因为RedisTemplate的实例化需要使用Spring的工厂进行创建,而我们创建的MybatisRedisCache类实现的是Mybatis的Cache接口,所以这个类不是由工厂进行管理的,所以我们不能直接在该类中直接使用注解注入RedisTemplate,所以我们创建一个获取Spring Boot创建好的工厂的ApplicationContextHolder工具类,用于获取RedisTemplate

    1. import org.springframework.beans.BeansException;
    2. import org.springframework.context.ApplicationContext;
    3. import org.springframework.context.ApplicationContextAware;
    4. import org.springframework.stereotype.Component;
    5. @Component
    6. public class ApplicationContextHolder implements ApplicationContextAware {
    7. private static ApplicationContext applicationContext;
    8. @Override
    9. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    10. ApplicationContextHolder.applicationContext = applicationContext;
    11. }
    12. //根据bean name 获取实例
    13. public static Object getBeanByName(String beanName) {
    14. if (beanName == null || applicationContext == null) {
    15. return null;
    16. }
    17. return applicationContext.getBean(beanName);
    18. }
    19. //只适合一个class只被定义一次的bean(也就是说,根据class不能匹配出多个该class的实例)
    20. public static Object getBeanByType(Class clazz) {
    21. if (clazz == null || applicationContext == null) {
    22. return null;
    23. }
    24. return applicationContext.getBean(clazz);
    25. }
    26. public static String[] getBeanDefinitionNames() {
    27. return applicationContext.getBeanDefinitionNames();
    28. }
    29. }

    实现ApplicationContextAware接口后,在Spring Boot启动创建工厂后,就会自动调用这个接口的setApplicationContext方法,将创建的工厂以参数的形式传递给这个类,在这个方法中我们就可以把工厂给保存下来。

    最后我们只需要在Mapper接口上添加@CacheNamespace注解,就完成了

    1. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    2. import com.chenyx.config.MybatisRedisCache;
    3. import com.chenyx.entity.WeChatUser;
    4. import org.apache.ibatis.annotations.CacheNamespace;
    5. @CacheNamespace(implementation= MybatisRedisCache.class,eviction=MybatisRedisCache.class)
    6. public interface WeChatUserListMapper extends BaseMapper<WeChatUser>{
    7. }
  • 相关阅读:
    3.3 C++高级编程_函数模板_引入
    关于重装系统后,Anaconda和Pycharm无法使用的问题及解决方案
    Sublime上插件的安装与使用
    displaty:none与visibility:hidden的区别
    NX二次开发-VS使用NXOpen向导创建项目失败,再次弹出创建向导对话框,解决办法
    golang - 控制协程并发数的3种方法
    Multiply and Rotate (BFS/队列)
    常见数据库优化面试题
    node实战——搭建带swagger接口文档的后端koa项目(node后端就业储备知识)
    MySQL之创建高性能的索引(十二)
  • 原文地址:https://blog.csdn.net/m0_56750901/article/details/125513482