微服务解决方案 -- Mybatis-Plus + Redis缓存,如何不太优雅的使用Redis缓存

   日期:2020-09-24     浏览:224    评论:0    
核心提示:如何不太优雅的使用Redis缓存我们都知道使用redis来缓存我们的数据集合,如下图所示。通常自己去缓存数据,这样的优点就是逻辑清晰,而且redis的key和value会比较规范。但是冗余代码会比较多,需要自己进行判断数据是否过期。为了简化业务代码,现在用注解的方式集成redis二级缓存,但是他的key和value就会比较不符合规范。他的key一共包含5个部分,最重要的就是sql和这个sql的参数。他的value就是这个查询的结果集。准备工作引入依赖,mybatis<dependency

如何不太优雅的使用Redis缓存

我们都知道使用redis来缓存我们的数据集合,如下图所示。

通常自己去缓存数据,这样的优点就是逻辑清晰,而且rediskeyvalue会比较规范。但是冗余代码会比较多,需要自己进行判断数据是否过期。
为了简化业务代码,现在用注解的方式集成redis二级缓存,但是他的keyvalue就会比较不符合规范。他的key一共包含5个部分,最重要的就是sql和这个sql的参数。他的value就是这个查询的结果集。

准备工作

引入依赖,mybatis

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    <exclusions>
        <!-- 排除 tomcat-jdbc 以使用 HikariCP -->
        <exclusion>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.6</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>3.0.6</version>
</dependency>

redis依赖

<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>

配置文件

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 这里使用的是 ip:3336/db_order 的数据库(一个服务一个数据库)
    url: jdbc:mysql://localhost:3306/sys-common?useUnicode=true&characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=false
    username: root
    password: 123456
    hikari:
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      pool-name: MyHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECt 1
  # redis
  redis:
    host: xxx.xxx.xxx.xxx
    port: 6379
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: -1ms
        min-idle: 0
    database: 4

配置redisTemplate

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
public class RedisConfiguration { 

    
    @Bean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) { 
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用String 序列化
// redisTemplate.setDefaultSerializer(new StringRedisSerializer());
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

注意这里我不使用String的序列化方式去序列化KeyValue

实现

实现Cache接口

package com.laoshiren.hello.redis.cache.mybatis.cache;

import com.laoshiren.hello.redis.cache.mybatis.configure.ApplicationContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


@Slf4j
public class RedisCache implements Cache { 

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final String id; // cache instance id
    private RedisTemplate redisTemplate;

    private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间

    public RedisCache(String id) { 
        if (id == null) { 
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        this.id = id;
    }

    @Override
    public String getId() { 
        return id;
    }

    
    @Override
    public void putObject(Object key, Object value) { 
        try { 
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.opsForValue().set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
            log.debug("Put query result to redis");
        } catch (Throwable t) { 
            log.error("Redis put failed", t);
        }
    }

    
    @Override
    public Object getObject(Object key) { 
        try { 
            RedisTemplate redisTemplate = getRedisTemplate();
            log.info("Get cached query result from redis");
// System.out.println("****" + opsForValue.get(key).toString());
            return redisTemplate.opsForValue().get(key);
        } catch (Throwable t) { 
            log.error("Redis get failed, fail over to db", t);
            return null;
        }
    }

    
    @Override
    @SuppressWarnings("unchecked")
    public Object removeObject(Object key) { 
        try { 
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.delete( key.toString());
            log.debug("Remove cached query result from redis");
        } catch (Throwable t) { 
            log.error("Redis remove failed", t);
        }
        return null;
    }

    
    @Override
    public void clear() { 
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.execute((RedisCallback) connection -> { 
            connection.flushDb();
            return null;
        });
        log.debug("Clear all the cached query result from redis");
    }

    
    @Override
    public int getSize() { 
        return 0;
    }

    @Override
    public ReadWriteLock getReadWriteLock() { 
        return readWriteLock;
    }

    private RedisTemplate getRedisTemplate() { 
        if (redisTemplate == null) { 
            redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
        }
        return redisTemplate;
    }
}

给指定的mapper配置缓存

@CacheNamespace(implementation = RedisCache.class)
public interface TbPostMapper extends BaseMapper<TbPost> { 
}

测试

请求一次数据库,使用Debug模式,它的key是一个CacheKey,无法使用使用StringRedisSerializer去序列化,所以redisTemplate 得使用默认的序列化,即 JdkSerializationRedisSerializer

打开RDM,看一下4号库。

发现keyvalue就不是很美观,不过不影响使用,当然您可以使用StringRedisSerializer去实现,只不过我在尝试的过程中获取sql和参数的时候,会出现一点问题。希望有大佬可以指出。

带参数的sql

特别注意! 在分页缓存的时候,Page对象的total必须自己手动查询一次,不然返回给前端的对象里第一次还有总页数,第二次由于走了缓存就不带这个total,所以必须手动查询一次。

@GetMapping("page/{pageNo}/{pageSize}")
public ResponseResult<IPage<Area>> page(@PathVariable(name = "pageNo")Integer pageNo,
                                        @PathVariable(name = "pageSize") Integer pageSize,
                                        HttpServletRequest request){ 
    IPage<Area> wherePage = new Page<>(pageNo, pageSize);
    String word = request.getParameter("wd");
    LambdaQueryWrapper<Area> queryWrapper = new LambdaQueryWrapper<>();
    if (StringUtils.isNotBlank(word)) { 
        queryWrapper.like(Area::getAreaName,word);
    }
    int count = areaService.count(queryWrapper);
    IPage<Area> page = areaService.page(wherePage,queryWrapper);
    page.setTotal((long)count);
    return new ResponseResult<>(CodeStatus.OK,"操作成功",page);
}

后续我也会继续更新这篇博客。好了,最后还是借用大佬的一句话:“不经一番寒彻骨,怎知梅花扑鼻香”。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服