• springboot-mybatisplus-redis二级缓存


    前言

    mybatis可以自己带有二级缓存的实现,这里加上redis是想把东西缓存到redis中,而不是mybaits自带的map中。这也就构成了我们看到的springboot + mybatisplus +redis实现二级缓存的题目。

    具体步骤如下:

    首先加入需要的依赖

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-data-redis</artifactId>
    4. </dependency>
    5. <dependency>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-cache</artifactId>
    8. </dependency>
    9. <dependency>
    10. <groupId>org.projectlombok</groupId>
    11. <artifactId>lombok</artifactId>
    12. </dependency>

    第二步:在application.properties或者yml中配置redis属性和设置开启二级缓存(默认是关闭的)

    1. spring.cache.type=redis
    2. spring.redis.host=192.168.9.82
    3. spring.redis.jedis.pool.max-active=10
    4. spring.redis.jedis.pool.max-idle=10
    5. spring.redis.jedis.pool.max-wait=3000
    6. spring.redis.jedis.pool.min-idle=20
    7. spring.redis.port=6379
    8. spring.redis.timeout=3000
    9. mybatis-plus.configuration.cache-enabled=true #开启二级缓存

    第三步:编写redis缓存的配置类,大体需要如下三个类,先截图在上代码

     ApplicationContextHolder.java类主要用于在另外另个类中可以通过context获取spring容器中注入的redisTemplate来操作redis。代码如下:

    1. import org.springframework.context.ApplicationContext;
    2. //@Component
    3. public class ApplicationContextHolder {
    4. private static ApplicationContext applicationContext = null;
    5. public static void setApplicationContext(ApplicationContext applicationContext) {
    6. ApplicationContextHolder.applicationContext = applicationContext;
    7. }
    8. public static ApplicationContext getApplicationContext() {
    9. return applicationContext;
    10. }
    11. public static T getBean(String name) {
    12. return (T)applicationContext.getBean(name);
    13. }
    14. public static T getBean(Class clz) {
    15. return (T)applicationContext.getBean(clz);
    16. }
    17. }

    MybatisRedisCache.java类实现了Mybatis的Cache接口,这样才能在执行数据库操作前调用缓存。代码如下:

    1. import org.apache.ibatis.cache.Cache;
    2. import org.springframework.data.redis.core.RedisCallback;
    3. import org.springframework.data.redis.core.RedisTemplate;
    4. import org.springframework.data.redis.core.ValueOperations;
    5. import java.util.concurrent.TimeUnit;
    6. import java.util.concurrent.locks.ReadWriteLock;
    7. import java.util.concurrent.locks.ReentrantReadWriteLock;
    8. public class MybatisRedisCache implements Cache {
    9. private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    10. private final String id; // cache instance id
    11. private RedisTemplate redisTemplate;
    12. private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
    13. public MybatisRedisCache(String id) {
    14. if (id == null) {
    15. throw new IllegalArgumentException("Cache instances require an ID");
    16. }
    17. this.id = id;
    18. }
    19. @Override
    20. public String getId() {
    21. return id;
    22. }
    23. /**
    24. * Put query result to redis
    25. *
    26. * @param key
    27. * @param value
    28. */
    29. @Override
    30. @SuppressWarnings("unchecked")
    31. public void putObject(Object key, Object value) {
    32. try {
    33. RedisTemplate redisTemplate = getRedisTemplate();
    34. ValueOperations opsForValue = redisTemplate.opsForValue();
    35. opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
    36. }
    37. catch (Throwable t) {
    38. t.printStackTrace();
    39. }
    40. }
    41. /**
    42. * Get cached query result from redis
    43. *
    44. * @param key
    45. * @return
    46. */
    47. @Override
    48. public Object getObject(Object key) {
    49. try {
    50. RedisTemplate redisTemplate = getRedisTemplate();
    51. ValueOperations opsForValue = redisTemplate.opsForValue();
    52. return opsForValue.get(key);
    53. }
    54. catch (Throwable t) {
    55. return null;
    56. }
    57. }
    58. /**
    59. * Remove cached query result from redis
    60. *
    61. * @param key
    62. * @return
    63. */
    64. @Override
    65. @SuppressWarnings("unchecked")
    66. public Object removeObject(Object key) {
    67. try {
    68. RedisTemplate redisTemplate = getRedisTemplate();
    69. redisTemplate.delete(key);
    70. }
    71. catch (Throwable t) {
    72. t.getMessage();
    73. }
    74. return null;
    75. }
    76. /**
    77. * Clears this cache instance
    78. */
    79. @Override
    80. public void clear() {
    81. RedisTemplate redisTemplate = getRedisTemplate();
    82. redisTemplate.execute((RedisCallback) connection -> {
    83. connection.flushDb();
    84. return null;
    85. });
    86. }
    87. /**
    88. * This method is not used
    89. *
    90. * @return
    91. */
    92. @Override
    93. public int getSize() {
    94. return 0;
    95. }
    96. @Override
    97. public ReadWriteLock getReadWriteLock() {
    98. return readWriteLock;
    99. }
    100. private RedisTemplate getRedisTemplate() {
    101. if (redisTemplate == null) {
    102. // 这里用到了ApplicationContextHolder.java
    103. redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
    104. }
    105. return redisTemplate;
    106. }
    107. }
    RedisCacheConfig.java类主要用来配置redis的一些属性,里面这里主要配置了reids序列化。代码如下:
    1. import org.springframework.beans.factory.annotation.Value;
    2. import org.springframework.cache.CacheManager;
    3. import org.springframework.cache.annotation.CachingConfigurerSupport;
    4. import org.springframework.cache.interceptor.CacheErrorHandler;
    5. import org.springframework.cache.interceptor.CacheResolver;
    6. import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
    7. import org.springframework.cache.interceptor.SimpleCacheResolver;
    8. import org.springframework.context.annotation.Bean;
    9. import org.springframework.context.annotation.Configuration;
    10. import org.springframework.data.redis.cache.RedisCacheConfiguration;
    11. import org.springframework.data.redis.cache.RedisCacheManager;
    12. import org.springframework.data.redis.connection.RedisConnectionFactory;
    13. import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
    14. import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    15. import org.springframework.data.redis.core.RedisTemplate;
    16. import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    17. import org.springframework.data.redis.serializer.RedisSerializationContext;
    18. import org.springframework.data.redis.serializer.StringRedisSerializer;
    19. import java.time.Duration;
    20. import java.util.HashMap;
    21. import java.util.Map;
    22. /**
    23. * ignit改redis缓存
    24. * @author lhb
    25. * @since 2022/11/24
    26. */
    27. @Configuration
    28. public class RedisCacheConfig extends CachingConfigurerSupport {
    29. @Value("${spring.redis.host}")
    30. private String redisHost;
    31. @Value("${spring.redis.port}")
    32. private int redisPort;
    33. @Bean
    34. public RedisTemplate redisTemplate() {
    35. RedisTemplate redisTemplate = new RedisTemplate<>();
    36. redisTemplate.setConnectionFactory(redisConnectionFactory());
    37. GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
    38. redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
    39. redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
    40. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    41. redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
    42. return redisTemplate;
    43. }
    44. @Override
    45. public CacheResolver cacheResolver() {
    46. return new SimpleCacheResolver(cacheManager());
    47. }
    48. @Override
    49. public CacheErrorHandler errorHandler() {
    50. return new SimpleCacheErrorHandler();
    51. }
    52. @Override
    53. public CacheManager cacheManager() {
    54. Map cacheConfigurationMap = generateCacheConfigMap();
    55. RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
    56. .disableCachingNullValues()
    57. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
    58. return RedisCacheManager.builder(redisConnectionFactory())
    59. .cacheDefaults(redisCacheConfiguration)
    60. .withInitialCacheConfigurations(cacheConfigurationMap)
    61. .build();
    62. }
    63. public Map generateCacheConfigMap() {
    64. Map initialCacheConfiguration = new HashMap<>();
    65. initialCacheConfiguration.put("hourCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1)));//1小时
    66. initialCacheConfiguration.put("minCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)));
    67. initialCacheConfiguration.put("dayCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)));
    68. return initialCacheConfiguration;
    69. }
    70. public RedisConnectionFactory redisConnectionFactory() {
    71. RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
    72. configuration.setHostName(redisHost);
    73. configuration.setPort(redisPort);
    74. configuration.setDatabase(0);
    75. LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration);
    76. factory.afterPropertiesSet();
    77. return factory;
    78. }
    79. }

    第四步:在mapper.xml中添加cache标签,来告诉mybtisplus这个mapper中的内容使用二级缓存,这个cache标签放在mapper表中的什么位置都行

    1. "hisense.HiDevMng.code.hidevConfigManager.web.dao.DevBusRouterelViewDao">
    2. "hisense.HiDevMng.code.base.config.cache.MybatisRedisCache"/>

    第五步:在启动类中实现ApplicationContextAware接口,目的是拿到spring容器的context,然后给ApplicationContextHolder,这样我们的redis缓存的配置类中就可以通过applicationContext来得到容器中中的redisTemplate进行redis操作了。添加如下代码:

    1. public class StartApplication extends SpringBootServletInitializer implements ApplicationContextAware {
    2. public static void main(String[] args) {
    3. SpringApplication.run(StartApplication.class, args);
    4. }
    5. @Override
    6. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    7. //重点在此,通过这个赋值context
    8. ApplicationContextHolder.setApplicationContext(applicationContext);
    9. }
    10. }

    网上很多帖子给出的方式是自己单独写一个类来实现ApplicationContextAware接口,但是因为我的项目中用到了@PostConstruct注解,那样会导致ApplicationContrext为空,所以我选择了通过上面在启动类给ApplicationContextHolder赋值的方式。

    第六步,测试,创建测试类例如

    1. RunWith(SpringRunner.class)
    2. @SpringBootTest(classes = StartApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    3. public class test {
    4. @Autowired
    5. private MyDao dao;
    6. @Test
    7. public void testOne() {
    8. Set ids = new HashSet<>();
    9. ids.add("25191027172915794000");
    10. List busOrgList = dao.getBusOrgList(ids);
    11. System.out.println(busOrgList.size());
    12. System.out.println("====================================================");
    13. List busOrgList1 = dao.getBusOrgList(ids);
    14. System.out.println(busOrgList1.size());
    15. }
    16. }

     运行2次查询,但是只出现一次如下图的log,就说明第二次执行使用redis缓存中的数据了

     出现一次Preparing:就说明执行的是数据库查询,没走redis缓存。当我们多次执行都不在显示这个Preparing:了就说明缓存成功了。

    总结:

    这里就不把代码放git上了,直接从上面把代码都拷贝下来,就可以执行了。关于什么是二级缓存网上可以自行百度。

    代码中还存在完善的空间,比如多久后自动从缓存删除等,大家可以自行了解下。这里只是为了给初学者使用,写多了怕容易乱。谢谢支持

  • 相关阅读:
    Java学习笔记(二)
    DHTMLX To Do List 1.0 亮点功能:高级任务分配、内联编辑、便捷的键盘导航
    基于Stable Diffusion的AIGC服饰穿搭实践
    【无标题】
    五、ROS2接口及其使用
    Ruoyi-vue项目新建模块的SQL输出的配置
    从零开始之了解电机及其控制(11)实现空间矢量调制
    Servlet——文件上传
    【一】Mac 本地部署大模型
    单体JOB向分布式JOB迁移案例
  • 原文地址:https://blog.csdn.net/liuhaibo_ljf/article/details/128116625