1. 程式人生 > 實用技巧 >Redis快取資料庫實現資料字典功能

Redis快取資料庫實現資料字典功能

一、實現方式

  • ①將配置表讀取存入redis做快取
  • ②建立自定義註解定義方法返回引數需要在資料字典中翻譯的表明和列名
  • ③使用aop切面對查詢出的結果進行資料字典注入

二、測試

本測試使用springboot+mysql+redis,過程如下:

1、準備工作

mysql建立測試表useruser_data,兩張表

CREATE TABLE `user` (

 `id` int(11) NOT NULL AUTO_INCREMENT,

 `name` varchar(255) DEFAULT NULL,

 PRIMARY KEY (`id`)

);

CREATE TABLE `user_data` (

  `id` 
int(11) NOT NULL, `user_id` int(11) DEFAULT NULL, `action` varchar(10) DEFAULT NULL, `time` datetime DEFAULT NULL, PRIMARY KEY (`id`) )

表內插入資料

INSERT INTO`user`(`id`, `name`) VALUES (1, '小顧');

INSERT INTO`user`(`id`, `name`) VALUES (2, '小陳');

INSERT INTO`user`(`id`, `name`) VALUES (3
, '小王'); INSERT INTO`user_data`(`id`, `user_id`, `action`, `time`) VALUES (1, 1, '', '2020-07-30 06:10:09'); INSERT INTO`user_data`(`id`, `user_id`, `action`, `time`) VALUES (2, 1, '穿', '2020-08-01 11:10:45'); INSERT INTO`user_data`(`id`, `user_id`, `action`, `time`) VALUES (3, 1, '', '2020-08-03 11:11:10'); INSERT
INTO`user_data`(`id`, `user_id`, `action`, `time`) VALUES (4, 2, '看書', '2020-08-02 11:11:37'); INSERT INTO`user_data`(`id`, `user_id`, `action`, `time`) VALUES (5, 2, '跑步', '2020-08-03 11:11:55');

安裝redis,springboot專案引入相關依賴包並配置redis(過程省略)

RedisUtil工具類

import com.alibaba.fastjson.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    public void add(String key,String value){
        stringRedisTemplate.opsForValue().set(key,value);
    }
    public void add(String key, String value,Long time){
        if (time>0){
            stringRedisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
        }else{
            stringRedisTemplate.opsForValue().set(key,value);
        }
    }
    public void add(String key, List<Map<String, Object>> o, int time){
        stringRedisTemplate.opsForValue().set(key, JSONArray.toJSONString(o),time,TimeUnit.SECONDS);
    }
    public String get(String key){
        return key==null?"":stringRedisTemplate.opsForValue().get(key);
    }
}

2、測試介面

新增返回引數類Data.java

import java.io.Serializable;
import java.util.List;
import java.util.Map;

public class Data implements Serializable {
    private static final long serialVersionUID = 4030739808145679471L;
    private List<Map<String,Object>> result;

    public List<Map<String, Object>> getResult() {
        return result;
    }

    public void setResult(List<Map<String, Object>> result) {
        this.result = result;
    }
}

新增測試介面顯示user_data內容

@ApiOperation(value = "測試", notes = "測試")
@RequestMapping(value = "/example", method = RequestMethod.GET)
public Data example() {
    LOG.info("example");
    List<Map<String,Object>> res = mysql.nQueryForMapList("select user_id,action,date_format(time,'%Y-%m-%d %T') as time from user_data");
    Data data = new Data();
    data.setResult(res);
    return data;
}

呼叫example介面,返回引數如下:

{
  "result": [
    {
      "user_id": 1,
      "action": "吃",
      "time": "2020-07-30 06:10:09"
    },
    {
      "user_id": 1,
      "action": "穿",
      "time": "2020-08-01 11:10:45"
    },
    {
      "user_id": 1,
      "action": "玩",
      "time": "2020-08-03 11:11:10"
    },
    {
     "user_id": 2,
      "action": "看書",
      "time": "2020-08-02 11:11:37"
    },
    {
      "user_id": 2,
      "action": "跑步",
     "time": "2020-08-03 11:11:55"
    }
  ]
}

3、redis快取資料

user表的資料快取到redis

import com.github.drinkjava2.jsqlbox.SqlBoxContext;
import com.jsepc.collectmonitor.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Component
public class AppRunner implements ApplicationRunner {

    @Qualifier("mysql")
    @Autowired
    public SqlBoxContext mysql;

    @Autowired
    RedisUtil redisUtil;

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {

        //讀取mysql的資料
        String sql = "select id,name from user";
        List<Map<String,Object>> taskList = mysql.nQueryForMapList(sql);
        //拼接儲存到redis的資料
        for (Map<String,Object> map : taskList){
            redisUtil.add("user"+"&&"+map.get("id"),map.get("name").toString());
        }
    }
}

執行後使用Redis Desktop Manager工具檢視到Redis中已經新增了三條資料,key為表名+id拼接,valueid的含義。

4、新增AOP切面

新增自定義註解類OperatorRedisData.java,其中引數tablename對應要注入的表名,key對應返回引數中需要解析的欄位名。在本測試中,tablename=user,key=user_id。

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperatorRedisData {
    String tableName();
    String key();
}

新增字典AOP

import com.jsepc.collectmonitor.model.Data;
import com.jsepc.collectmonitor.util.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Aspect
@Component
public class OperatorRedisDataAspect {

    private final Logger log = LoggerFactory.getLogger(com.jsepc.collectmonitor.aop.OperatorRedisDataAspect.class);

    @Autowired
    RedisUtil redisUtil;

    @Pointcut("@annotation(com.jsepc.collectmonitor.aop.OperatorRedisData)")
    public void operatorRedis(){
    }

    @Around("operatorRedis()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long time1 = System.currentTimeMillis();
        Object result = pjp.proceed();
        long time2 = System.currentTimeMillis();
        //通過自定義註釋獲取tableName和key
        MethodSignature  signature = (MethodSignature)pjp.getSignature();
        OperatorRedisData d =signature.getMethod().getAnnotation(OperatorRedisData.class);
        log.info("獲取JSON資料 耗時:" + (time2 - time1) + "ms");
        long start = System.currentTimeMillis();
        this.parseDictText(result,d.tableName(),d.key());
        long end = System.currentTimeMillis();
        log.info("解析注入JSON資料  耗時" + (end - start) + "ms");
        return result;
    }

    //對返回值解析並注入資料字典
    private void parseDictText(Object result,String table,String key) {
        if (result instanceof Data) {
            List<Map<String,Object>> items = new ArrayList<>();
            for (Map<String, Object> item : ((Data) result).getResult()) {
                String k = item.get(key).toString();
                String res = redisUtil.get(table+"&&"+k);
                item.put(key+"_dictText",res);
                items.add(item);
            }
            ((Data) result).setResult(items);
        }
    }
}

5、應用註解後測試介面

對步驟2中的測試介面新增步驟4中的自定義註解@OperatorRedisData,如下:

@ApiOperation(value = "測試", notes = "測試")
@RequestMapping(value = "/example", method = RequestMethod.GET)
@OperatorRedisData(tableName="user",key="user_id")
public Data example() {
    LOG.info("example");
    List<Map<String,Object>> res = mysql.nQueryForMapList("select user_id,action,date_format(time,'%Y-%m-%d %T') as time from user_data");
    Data data = new Data();
    data.setResult(res);
    return data;
}

新增後執行程式再次測試,返回結果如下:

{
  "result": [
    {
      "user_id": 1,
      "action": "吃",
      "time": "2020-07-30 06:10:09",
      "user_id_dictText": "小顧"
    },
    {
      "user_id": 1,
      "action": "穿",
      "time": "2020-08-01 11:10:45",
      "user_id_dictText": "小顧"
    },
    {
      "user_id": 1,
      "action": "玩",
     "time": "2020-08-03 11:11:10",
      "user_id_dictText": "小顧"
    },
    {
      "user_id": 2,
      "action": "看書",
      "time": "2020-08-02 11:11:37",
      "user_id_dictText": "小陳"
    },
    {
      "user_id": 2,
      "action": "跑步",
      "time": "2020-08-03 11:11:55",
      "user_id_dictText": "小陳"
    }
  ]
}

AOP對返回引數注入了user_id_dictText欄位。

三、總結

通過Redis將一些使用率比較高的臺帳、配置類的表快取,進而通過AOP在返回引數中注入。這種方式可以減少資料庫的壓力,提高讀取效率。