springboot+redis+mybatis实现数据字典功能

自我学习代码地址:

​​​​​​https://gitee.com/renCaiGe/spring-boot-demo/tree/spring-boot.2.5.4/spring-boot-redis-dictionary

参考博客1

参考博客2

springboot+redis+mybatis实现数据字典功能

Hash底层存储数据的方式确实跟其他数据结构有点不同,其他数据结构几乎都是:Key-Value的存储,而Hash则是:Key – [Field-Value] 的存储,也就是说其他数据结构的Value一般是确切的值,而Hash的Value是一系列的键值对。

通常我们是这样称呼Hash的存储的:大Key为实际的Key,小Key为Field,而具体的取值为Field对应的值value
实践:系统数据字典实时触发缓存存储

2.1 开发过程中遇到的问题

2.1.1 注解@Autowired导入RedisTemplate<String, Object>导致无法创建对象,

@Autowired
private RedisTemplate<String, Object> redisTemplate;

修改为:

@Resource
private RedisTemplate<String, Object> redisTemplate;

2.1.2 redis存储乱码

问题如下图

 如下配置解决该问题

 /**
     * 设置数据存入 redis 的序列化方式
     *
     * @return redisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisTemplate redisTemplate) {
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jdkSerializationRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        //如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!
        //key采用String的序列化方式
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
        //hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);
        return redisTemplate;
    }

2.2 在数据库建立 系统字典 表 sys_dict_config

CREATE TABLE `sys_dict_config` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `module` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '模块',
    `type` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '字典类型',
    `name` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '字典名称',
    `code` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '选项编码',
    `value` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '选项取值',
    `order_by` int(11) DEFAULT '1' COMMENT '排序',
    `is_active` int(11) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
    `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`),
    KEY `idx_type_code` (`type`,`code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.3 新建实体类 SysDictConfig,SysDictConfigMapper,SysDictConfigMapper.xml,代码如下:
SysDictConfig:

BaseResponse.java

package com.bridge.common;

/**
 * @author bridge
 * @Date 2022/05/25/20:34
 */

public class BaseResponse<T> {

    private String code;
    private String msg;
    private T data;

    public BaseResponse(String code) {
        this.code = code;
    }

    public BaseResponse(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public BaseResponse(String code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}


RedisConfig.java

package com.bridge.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;

/**
 * @author bridge
 * @Date 2022/05/24/22:21
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * 设置数据存入 redis 的序列化方式,并开启事务
     *
     * @return redisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisTemplate redisTemplate) {
//        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jdkSerializationRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
//        redisTemplate.setConnectionFactory(redisConnectionFactory);//会找到它的实现类 JedisConnectionFactory
        //如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!
        //key采用String的序列化方式
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
        //hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);
        //开启事务
//        redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }
}

ErrorCodeEnum.java

package com.bridge.enums;

/**
 * @author bridge
 * @Date 2022/05/25/20:34
 */
public enum ErrorCodeEnum {
    SUCCESS("SUCCESS","成功"),
    FAIL("FAIL","失败"),
    ;

    /**
     *  编码
     */
    private String code;

    /**
     *  错误描述
     */
    private String msg;

    ErrorCodeEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

SysDictConfigMapper.java

package com.bridge.mapper;

import com.bridge.model.SysDictConfig;

import java.util.List;

/**
 * @author bridge
 * @Date 2022/05/25/20:10
 */
public interface SysDictConfigMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(SysDictConfig sysDictConfig);

    int insertSelective(SysDictConfig sysDictConfig);

    SysDictConfig selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(SysDictConfig sysDictConfig);

    int updateByPrimaryKey(SysDictConfig sysDictConfig);

    List<SysDictConfig> selectActiveConfigs();

    List<SysDictConfig> selectListByType(String type);


}

 SysDictConfig.java

package com.bridge.model;

/**
 * @author bridge
 * @Date 2022/05/25/20:10
 */
import org.hibernate.validator.constraints.NotBlank;

import java.io.Serializable;
import java.util.Date;

public class SysDictConfig implements Serializable {
    private Integer id;

    @NotBlank(message = "字典类型不能为空")
    private String type;


    @NotBlank(message = "模块不能为空")
    private String module;

    @NotBlank(message = "字典名称不能为空")
    private String name;

    @NotBlank(message = "选项编码不能为空")
    private String code;

    @NotBlank(message = "选项取值不能为空")
    private String value;

    //排序
    private Integer orderBy;

    //是否有效(1=是;0=否)
    private Byte isActive=1;

    //创建时间
    private Date createTime;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getModule() {
        return module;
    }

    public void setModule(String module) {
        this.module = module;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public Integer getOrderBy() {
        return orderBy;
    }

    public void setOrderBy(Integer orderBy) {
        this.orderBy = orderBy;
    }

    public Byte getIsActive() {
        return isActive;
    }

    public void setIsActive(Byte isActive) {
        this.isActive = isActive;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

service和impl

1 service

package com.bridge.service;

import com.bridge.model.SysDictConfig;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author bridge
 * @Date 2022/05/25/20:24
 */
public interface IRedisHashService {
    @Transactional(rollbackFor = Exception.class)
    Integer addSysDictConfig(SysDictConfig config) throws Exception;

    Map<String, List<SysDictConfig>> getSysDictConfig() throws Exception;

    List<SysDictConfig> getByType(String type) throws Exception;

    Map<String, List<SysDictConfig>> getAllCacheConfig();

    List<SysDictConfig> getCacheConfigByType(String type);

    List<SysDictConfig> update(String type);
}


// impl

package com.bridge.service.impl;

import com.bridge.mapper.SysDictConfigMapper;
import com.bridge.model.SysDictConfig;
import com.bridge.service.IRedisHashService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;


/**
 * @author bridge
 * @Date 2022/05/25/20:24
 */
@Service
public class RedisHashServiceImpl implements IRedisHashService {
    private static final Logger log = LoggerFactory.getLogger(RedisHashServiceImpl.class);

    private static final String RedisHashKey = "Redis:Hash";

    @Resource
    private SysDictConfigMapper sysDictConfigMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 添加数据字典及其对应的选项(code-value)
     *
     * @param config
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer addSysDictConfig(SysDictConfig config) throws Exception {
        int res = sysDictConfigMapper.insertSelective(config);
        if (res > 0) {
            //实时触发数据字典的hash存储
            cacheConfigMap();
        }
        return config.getId();
    }


    /**
     * 取出缓存中所有的数据字典列表
     */
    @Override
    public Map<String, List<SysDictConfig>> getSysDictConfig() throws Exception {
        return getAllCacheConfig();
    }


    /**
     * 取出缓存中特定的数据字典列表
     *
     * @param type
     * @return
     * @throws Exception
     */
    @Override
    public List<SysDictConfig> getByType(final String type) throws Exception {
        return getCacheConfigByType(type);
    }


    /**
     * 实时获取所有有效的数据字典列表-转化为map-存入hash缓存中
     */
    @Async
    public void cacheConfigMap() {
        try {
            List<SysDictConfig> sysDictConfigList = sysDictConfigMapper.selectActiveConfigs();
            if (sysDictConfigList != null && !sysDictConfigList.isEmpty()) {
                Map<String, List<SysDictConfig>> dataMap = Maps.newHashMap();
                //所有的数据字典列表遍历 -> 转化为 hash存储的map
                sysDictConfigList.forEach(sysDictConfig -> {
                    List<SysDictConfig> list = dataMap.get(sysDictConfig.getType());
                    if (CollectionUtils.isEmpty(list)) {
                        list = Lists.newLinkedList();
                    }
                    list.add(sysDictConfig);
                    dataMap.put(sysDictConfig.getType(), list);
                });

                //存储到缓存hash中
                HashOperations<String, String, List<SysDictConfig>> hashOperations = redisTemplate.opsForHash();
                hashOperations.putAll(RedisHashKey, dataMap);
            }
        } catch (Exception e) {
            log.error("实时获取所有有效的数据字典列表-转化为map-存入hash缓存中-发生异常:", e.fillInStackTrace());
        }
    }

    /**
     * 实时获取所有有效的数据字典列表-转化为map-存入hash缓存中
     */
    @Async
    public void cacheConfigMap(String type) {
        try {
            List<SysDictConfig> sysDictConfigList = sysDictConfigMapper.selectListByType(type);
            if (CollectionUtils.isEmpty(sysDictConfigList)) {
                return;
            }
            Map<String, List<SysDictConfig>> dataMap = Maps.newHashMap();
            dataMap.put(type, sysDictConfigList);
            //存储到缓存hash中
            HashOperations<String, String, List<SysDictConfig>> hashOperations = redisTemplate.opsForHash();
            hashOperations.putAll(RedisHashKey, dataMap);
        } catch (Exception e) {
            log.error("实时获取所有有效的数据字典列表-转化为map-存入hash缓存中-发生异常:", e.fillInStackTrace());
        }
    }


    /**
     * 从缓存hash中获取所有的数据字典配置map
     *
     * @return 数据字典配置map
     */
    @Override
    public Map<String, List<SysDictConfig>> getAllCacheConfig() {
        Map<String, List<SysDictConfig>> map = Maps.newHashMap();
        try {
            HashOperations<String, String, List<SysDictConfig>> hashOperations = redisTemplate.opsForHash();
            map = hashOperations.entries(RedisHashKey);
        } catch (Exception e) {
            log.error("从缓存hash中获取所有的数据字典配置map-发生异常:", e.fillInStackTrace());
        }
        return map;
    }


    /**
     * 从缓存hash中获取特定的数据字典列表
     *
     * @param type type
     * @return
     */
    @Override
    public List<SysDictConfig> getCacheConfigByType(final String type) {
        List<SysDictConfig> list = Lists.newArrayList();
        try {
            HashOperations<String, String, List<SysDictConfig>> hashOperations = redisTemplate.opsForHash();
            list = hashOperations.get(RedisHashKey, type);
        } catch (Exception e) {
            log.error("从缓存hash中获取特定的数据字典列表-发生异常:", e.fillInStackTrace());
        }
        // 缓存数据
        if (CollectionUtils.isEmpty(list)) {
            cacheConfigMap();
        }
        return list;
    }

    @Override
    public List<SysDictConfig> update(String type) {
        cacheConfigMap(type);
        return getCacheConfigByType(type);
    }


}

SysDictConfigMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.bridge.mapper.SysDictConfigMapper" >

    <resultMap id="BaseResultMap" type="com.bridge.model.SysDictConfig" >
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="module" property="module" jdbcType="VARCHAR" />
        <result column="type" property="type" jdbcType="VARCHAR" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="code" property="code" jdbcType="VARCHAR" />
        <result column="value" property="value" jdbcType="VARCHAR" />
        <result column="order_by" property="orderBy" jdbcType="INTEGER" />
        <result column="is_active" property="isActive" jdbcType="TINYINT" />
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
    </resultMap>

    <sql id="Base_Column_List" >
        id, module,type, name, code, value, order_by, is_active, create_time
    </sql>

    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
        select
        <include refid="Base_Column_List" />
        from sys_dict_config
        where id = #{id,jdbcType=INTEGER}
    </select>

    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
        delete from sys_dict_config
        where id = #{id,jdbcType=INTEGER}
    </delete>

    <insert id="insert" parameterType="com.bridge.model.SysDictConfig" >
        insert into sys_dict_config (id, module,type, name,
                                     code, value, order_by,
                                     is_active, create_time)
        values (#{id,jdbcType=INTEGER}, #{module,jdbcType=VARCHAR},#{type,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
                #{code,jdbcType=VARCHAR}, #{value,jdbcType=VARCHAR}, #{orderBy,jdbcType=INTEGER},
                #{isActive,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP})
    </insert>

    <insert id="insertSelective" keyProperty="id" useGeneratedKeys="true" parameterType="com.bridge.model.SysDictConfig" >
        insert into sys_dict_config
        <trim prefix="(" suffix=")" suffixOverrides="," >
            <if test="id != null" >
                id,
            </if>
            <if test="module != null" >
                module,
            </if>
            <if test="type != null" >
                type,
            </if>
            <if test="name != null" >
                name,
            </if>
            <if test="code != null" >
                code,
            </if>
            <if test="value != null" >
                value,
            </if>
            <if test="orderBy != null" >
                order_by,
            </if>
            <if test="isActive != null" >
                is_active,
            </if>
            <if test="createTime != null" >
                create_time,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides="," >
            <if test="id != null" >
                #{id,jdbcType=INTEGER},
            </if>
            <if test="module != null" >
                #{module,jdbcType=VARCHAR},
            </if>
            <if test="type != null" >
                #{type,jdbcType=VARCHAR},
            </if>
            <if test="name != null" >
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="code != null" >
                #{code,jdbcType=VARCHAR},
            </if>
            <if test="value != null" >
                #{value,jdbcType=VARCHAR},
            </if>
            <if test="orderBy != null" >
                #{orderBy,jdbcType=INTEGER},
            </if>
            <if test="isActive != null" >
                #{isActive,jdbcType=TINYINT},
            </if>
            <if test="createTime != null" >
                #{createTime,jdbcType=TIMESTAMP},
            </if>
        </trim>
    </insert>

    <update id="updateByPrimaryKeySelective" parameterType="com.bridge.model.SysDictConfig" >
        update sys_dict_config
        <set >
            <if test="module != null" >
                type = #{module,jdbcType=VARCHAR},
            </if>
            <if test="type != null" >
                type = #{type,jdbcType=VARCHAR},
            </if>
            <if test="name != null" >
                name = #{name,jdbcType=VARCHAR},
            </if>
            <if test="code != null" >
                code = #{code,jdbcType=VARCHAR},
            </if>
            <if test="value != null" >
                value = #{value,jdbcType=VARCHAR},
            </if>
            <if test="orderBy != null" >
                order_by = #{orderBy,jdbcType=INTEGER},
            </if>
            <if test="isActive != null" >
                is_active = #{isActive,jdbcType=TINYINT},
            </if>
            <if test="createTime != null" >
                create_time = #{createTime,jdbcType=TIMESTAMP},
            </if>
        </set>
        where id = #{id,jdbcType=INTEGER}
    </update>

    <update id="updateByPrimaryKey" parameterType="com.bridge.model.SysDictConfig" >
        update sys_dict_config
        set module = #{module,jdbcType=VARCHAR},
            type = #{type,jdbcType=VARCHAR},
            name = #{name,jdbcType=VARCHAR},
            code = #{code,jdbcType=VARCHAR},
            value = #{value,jdbcType=VARCHAR},
            order_by = #{orderBy,jdbcType=INTEGER},
            is_active = #{isActive,jdbcType=TINYINT},
            create_time = #{createTime,jdbcType=TIMESTAMP}
        where id = #{id,jdbcType=INTEGER}
    </update>


    <select id="selectActiveConfigs" resultType="com.bridge.model.SysDictConfig">
        SELECT <include refid="Base_Column_List"/>
        FROM sys_dict_config
        WHERE is_active = 1
        ORDER BY type, order_by ASC
    </select>


    <select id="selectListByType" resultType="com.bridge.model.SysDictConfig">
        SELECT <include refid="Base_Column_List"/>
        FROM sys_dict_config
        WHERE is_active = 1
        and type = #{type,jdbcType=VARCHAR}
        ORDER BY type, order_by ASC
    </select>


</mapper>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值