自我学习代码地址:
https://gitee.com/renCaiGe/spring-boot-demo/tree/spring-boot.2.5.4/spring-boot-redis-dictionary
参考博客1
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>