问题难点:
- 公司的字典参数是保存在表里面的,所以增删改都需要保持一致。
- 这个字典释义是给前端展示给用户用的,我后台写接口时不用做转换工作。
- 如何代码改动最小...
code.........
开启缓存
使用springboot 自带的缓存;在项目代码中添加 @EnableCaching
代码语言:javascript复制java 代码解读复制代码@EnableCaching
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
创建一个字典缓存类
看了springboot 自带的缓存框架只需要实现Cache就可以啦;由于我这里为了方便,你可以直接继承ConcurrentMapCache就可以啦;为了防止缓存被更改我这里都是拷贝副本。
代码语言:javascript复制java 代码解读复制代码/**
* 字典缓存
* <br/>
* date: 2024/5/24<br/>
* version 0.1
*
* @author ls<br />
*/
@Component
public class DicCache implements Cache {
/**
* cache name
*/
public static final String name = "DIC_CACHE";
private final ConcurrentMap<String, CacheValue> store;
public DicCache() {
store = new ConcurrentHashMap<>();
}
public void put(String key, EntityDicAO value) {
if (null == key || null == value) return;
EntityDicAO newValue = (EntityDicAO) copy(value);
this.store.put(key, new CacheValue(newValue));
}
public CacheValue putIfAbsent(String key, EntityDicAO value) {
if (null == key || null == value) return null;
EntityDicAO newValue = (EntityDicAO) copy(value);
return this.store.putIfAbsent(key, new CacheValue(newValue));
}
/**
* 从缓存中删除
*
* @param key index
*/
public void evict(String key) {
this.store.remove(key);
}
/**
* 从缓存中删除
*
* @param key index
*/
public void remove(String key) {
this.evict(key);
}
/**
* Return the cache name.
*/
@Override
public String getName() {
return name;
}
/**
* Return the underlying native cache provider.
*/
@Override
public Object getNativeCache() {
return this.store;
}
public EntityDicAO get(String key) {
CacheValue cacheValue = this.store.get(key);
return (EntityDicAO) copy(cacheValue.getValue());
}
@Override
public ValueWrapper get(Object key) {
if (!this.store.containsKey(key)) {
return null;
}
CacheValue cacheValue = this.store.get(key);
return (ValueWrapper) copy(cacheValue);
}
@Override
public <T> T get(Object key, Class<T> type) {
return (T) this.get((String) key);
}
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
CacheValue value = this.store.computeIfAbsent((String) key, r -> {
try {
T loaderValue = valueLoader.call();
if ((loaderValue instanceof EntityDicAO)) {
EntityDicAO loaderValueDic = (EntityDicAO) loaderValue;
CacheValue cacheValue = new CacheValue((EntityDicAO) this.copy(loaderValueDic));
return cacheValue;
}
return null;
} catch (Exception ex) {
throw new ValueRetrievalException(key, valueLoader, ex);
}
});
return (T) Optional.ofNullable(value).map(CacheValue::getValue).orElse(null);
}
@Override
public void put(Object key, Object value) {
if (value instanceof EntityDicAO) {
this.put((String) key, (EntityDicAO) value);
return;
}
if (value instanceof ServiceResult) {
ServiceResult valueSt = (ServiceResult) value;
Object data = valueSt.getData();
if (data instanceof EntityDicAO) {
this.put((String) key, (EntityDicAO) data);
}
}
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
CacheValue cacheValue = this.putIfAbsent((String) key, (EntityDicAO) value);
return new SimpleValueWrapper(cacheValue.getValue());
}
@Override
public void evict(Object key) {
this.evict((String) key);
}
@PreDestroy
public void clear() {
this.store.clear();
}
public EntityDicAO findTypeWithCode(String type, String code) {
Optional<CacheValue> value = this.store.values().stream().filter(e -> e.getKey().equals(CacheValue.generateKey(type, code))).findFirst();
return (EntityDicAO) value.map(CacheValue::getValue).map(this::copy).orElse(null);
}
/**
* 缓存包装
*/
static class CacheValue implements ValueWrapper, Serializable {
private final String key;
private final EntityDicAO value;
CacheValue(EntityDicAO value) {
this.key = generateKey(value);
this.value = value;
}
public String getKey() {
return key;
}
public EntityDicAO getValue() {
return value;
}
private static String generateKey(EntityDicAO value) {
// 确保使用用户的唯一标识作为缓存键的一部分
return generateKey(value.getType(), value.getCode());
}
protected static String generateKey(String type, String code) {
// 确保使用用户的唯一标识作为缓存键的一部分
return MessageFormat.format("{0}@{1}", type, code);
}
/**
* Return the actual value in the cache.
*/
@Override
public Object get() {
return this.getValue();
}
}
/**
* 复制字典
*
* @param value 字典
* @return 新的字典
*/
private <T extends Serializable> Serializable copy(T value) {
return SerializationUtils.clone(value);
}
}
添加一个 cache 配置类
把 DicCache 添加到cacheManager中
代码语言:javascript复制java 代码解读复制代码@Configuration
public class DicCacheConfig {
@Autowired
private DicCache dicCache;
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList(dicCache));
return cacheManager;
}
}
字典服务改造
现在只需要在字典服务中以最小的改动进行修改,添加@CachePut、@CacheEvict 注解就可以实现增删改一致啦
代码语言:javascript复制java 代码解读复制代码public class DicService extends ServiceImpl<EntityDicGeneratedMapper, EntityDicAO> {
/**
* 保存参数信息
*
* @param dicAO 参数信息
* @return 是否成功
*/
@CachePut(value = DicCache.name,key = "#dicAO.id",condition = "#result.succeed == true ")
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public ServiceResult<EntityDicAO> createOrUpdateDic(EntityDicAO dicAO) {
//......业务逻辑代码
}
/**
* 删除参数信息
*
* @param id 参数信息ID
* @return 是否成功
*/
@CacheEvict(value = DicCache.name,key = "#id",condition = "#result.succeed == true ")
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public ServiceResult<Boolean> deleteDic(String id) {
//......业务逻辑代码
}
}
实现字典转译
字典释义是给前端展示用的,那是不是数据序列化的时候再做字典转译就可以啦;springboot 默认使用Jackson来做序列化,实现一个jackson 自定义序列化
实现一个自定义注解
代码语言:javascript复制java 代码解读复制代码package x.x.x;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典序列化
* @author ls
* 2024/5/27
* @version 1.0
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DictSerializerHandel.class) //这里绑定处理类
public @interface DicSerializer {
/** 字典类型 */
String type();
/**
* 字典释义后的json 字段名
* 默认是原始字段拼接一个Name
*/
String name() default "";
/**
* 值类型
*/
Type valueType() default Type.SINGLE;
/**
*多值时的分隔符
*/
String separator() default ",";
enum Type{
/**
*单值
*/
SINGLE,
/**
*多值
*/
MULTI_VALUE
}
}
实现一个基于注解的jackson 自定义序列化,这里需要注意一下不要导错包 com.fasterxml.jackson.databind 这个包下的。
代码语言:javascript复制java 代码解读复制代码package x.x.x;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 字典序列化处理
* @author ls
* 2024/5/27
* @version 1.0
*/
@JacksonStdImpl
public class DictSerializerHandel extends StdSerializer<String> implements ContextualSerializer {
private String type;
private String name;
private DicSerializer.Type valueType;
private String separator;
public DictSerializerHandel() {
super(String.class);
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (Objects.isNull(value)) {
gen.writeObject(null);
return;
}
String currentName = gen.getOutputContext().getCurrentName();
if(StringUtils.isEmpty(value) || StringUtils.isEmpty(currentName)){
gen.writeString("");
return;
}
gen.writeString(value);
// 通过数据字典类型和value获取name
DicService dicService = SpringUtils.getBean(DicService.class);
String translatedValue = "";
switch (valueType){
case SINGLE:
EntityDicAO dic = dicService.getDicByTypeWithCode(type, value).getData();
translatedValue= Optional.ofNullable(dic).map(EntityDicAO::getName).orElse("");
break;
case MULTI_VALUE:
translatedValue = Arrays.stream(value.split(separator)).map(e->dicService.getDicByTypeWithCode(type, e).getData()).filter(Objects::nonNull).map(EntityDicAO::getName).filter(StringUtils::isNotBlank)
.distinct().collect(Collectors.joining(separator));
break;
default:
}
gen.writeStringField(
StringUtils.isBlank(name) ? currentName.concat("Name") : name,
translatedValue
);
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty beanProperty) {
if (beanProperty != null && beanProperty.getType().getRawClass().equals(String.class)) {
// 获取注解的参数
DicSerializer annotation = beanProperty.getAnnotation(DicSerializer.class);
if (null != annotation) {
type = annotation.type();
name = annotation.name();
valueType = annotation.valueType();
separator = annotation.separator();
return this;
}
}
return new ToStringSerializer();
}
}
到此就大致可以1啦..................
使用
代码语言:javascript复制java 代码解读复制代码public class EntityEvaluation {
// ............
/**
* 评估类型
*/
@DicSerializer(type = "assessment")
private String type;
/**
* 评估分析-长期目标 多选 多个用‘,‘号分割
*/
@DicSerializer(type = "PATIENT_CONCERNS",valueType = DicSerializer.Type.MULTI_VALUE)
private String patientConcerns;
// ............
}