基本实现流程
启动时自动查询码表存储至缓存–>根据注解识别带转码字段–>jackson序列化时自动去缓存中获取待转码的值。
本项目采用reids缓存
创建转码注解
/**
* json自动转码注解;
* 该注解只有添加于String类型的属性上、且码表转换后的值不为null时生效
* @Author: yousili
* @Date: 2022/4/26 10:56
* @Description:
*/
@Target({FIELD})
@Retention(RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = JacksonTranscodingSerializer.class)
public @interface JacksonTranscoding {
/**
* 组名;eg:币种、行业。。。
*
* @return 组名
*/
String group();
}
自动转码的系列化实现
代码中涉及到的Constants为项目中自定义的一些常量,自己随意定义即可
/**
* Json自动转码实现逻辑
*
* @Author: yousili
* @Date: 2022/4/26 10:07
* @Description:
*/
public class JacksonTranscodingSerializer extends StdSerializer<String> implements ContextualSerializer {
private String redisKeyPer;
public JacksonTranscodingSerializer(){
super(String.class);
}
public JacksonTranscodingSerializer(String key) {
super(String.class);
this.redisKeyPer = key;
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
if (beanProperty.getType().getRawClass().equals(String.class)) {
JacksonTranscoding annotation = beanProperty.getAnnotation(JacksonTranscoding.class);
if (annotation == null) {
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
} else {
String redisKey = Constants.DICT_PER + annotation.group();
return new JacksonTranscodingSerializer(redisKey);
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(beanProperty);
}
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//该项目中通过redis缓存码表,根据项目实现修改下面获取转码值的代码
Object value = SpringUtil.getBean(RedisService.class).get(redisKeyPer + ":" + s);
if (value == null) {
//字典不存在则使用原来值
jsonGenerator.writeString(s);
} else {
jsonGenerator.writeString((String) value);
}
}
}
自动查询数据库并将转码对应关系缓存至缓存的注解
/**
* 支持返回结果为List<obj>或obj的方法 (obj为map时则map.get()获取,为对象时则通过属性的get方法获取)
* 该注解用于查询码表的mapper接口的方法上,声明该方法查询结果需要缓存至redis
* 例如:
* @TranscodingStatement(codeField = "code",valueField = "name")
* List<Map<String,String>> amr_n042_code_cb18();
* 意思就是获取返回的map并向redis中缓存:redisKey = amr_n042_code_cb18+"-"+map.get("code");如果注解存在group参数则redisKey = group+"-"+map.get("code");
* redisValue = map.get("name");
* @Author: yousili
* @Date: 2022/5/20 14:56
* @Description:
*/
@Target({METHOD})
@Retention(RUNTIME)
@Documented
public @interface TranscodingStatement {
/**
* code对应的字段名称(map时为key的值,实体类时为字段的值)
* @return
*/
String codeField();
/**
* value对应的字段名称
* @return
*/
String valueField();
/**
* 组名;eg:币种、行业。。。
* 如果组名未声明则会默认取方法名;组名+方法名的集合中不允许存在重复否则可能会覆盖
* @return 组名
*/
String group() default "";
}
使用示例:
/**
* 查询所有码表的操作
* 在方法上添加@TranscodingStatement注解就会根据响应参数去查询码表缓存codeField字段的值和valueField字段的值于redis
* @Author: yousili
* @Date: 2022/5/20 10:07
* @Description:
*/
public interface TranscodingMapper {
@TranscodingStatement(group = "ent_status", codeField = "code",valueField = "name")
List<Map> code_ex02();
}
项目启动自动加载码表的实现逻辑
/**
* 转码相关处理组件
*
* @Author: yousili
* @Date: 2022/5/20 11:18
* @Description:
*/
@Component
@Slf4j
public class TranscodingComponet implements ApplicationRunner {
@Resource
private TranscodingMapper transcodingMapper;
@Resource
private RedisService redisService;
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("==========开始加载码表数据至redis!======");
//额 还没有加扫描所有mapper的代码,感觉写在一个mapper里就可以了,因此在这里指定了mapper的名称
//获取带有@TranscodingStatement注解的方法
List<Method> publicMethods = ClassUtil.getPublicMethods(TranscodingMapper.class, (method) -> {
TranscodingStatement annotation = method.getAnnotation(TranscodingStatement.class);
method.getReturnType();
return annotation != null;
});
//仅执行返回结果会处理list,具体内容会根据map(调用get方法)和entity(直接获取属性值)单独处理
for (Method method : publicMethods) {
TranscodingStatement annotation = method.getAnnotation(TranscodingStatement.class);
String keyPer = Constants.DICT_PER + (StrUtil.isEmpty(annotation.group()) ? method.getName() + "-" : annotation.group() + ":");
Object res = method.invoke(transcodingMapper);
if (res instanceof List) {
List list = (List) res;
for (Object obj : list) {
setDictionaryCache(obj, annotation, keyPer);
}
} else {
setDictionaryCache(res, annotation, keyPer);
}
}
log.info("==========码表数据加载至redis完成!本次操作缓存了{}张码表数据======", publicMethods.size());
}
/**
* 通过注解的属性值获取obj中的具体值然后存储值redis
* @param obj 对象
* @param annotation 注解
* @param redisKeyPer redisKey前缀
*/
private void setDictionaryCache(Object obj, TranscodingStatement annotation, String redisKeyPer) {
Object key;
Object value;
if (obj instanceof Map) {
//处理大小写的问题,oracle查询select*时map的key默认为大写
key = ((Map) obj).get(annotation.codeField());
key = key == null?((Map) obj).get(annotation.codeField().toUpperCase()):key;
value = ((Map) obj).get(annotation.valueField());
value = value == null?((Map) obj).get(annotation.valueField().toUpperCase()):value;
} else {
key = BeanUtil.getFieldValue(obj, annotation.codeField());
value = BeanUtil.getFieldValue(obj, annotation.valueField());
}
redisService.set(redisKeyPer + key, value);
}
}
使用
添加完上述代码后就完成了所有的准备工作了。
1:在mapper中添加查询的方法
使用示例:
mapper接口如下
/**
* 查询所有码表的操作
* 在方法上添加@TranscodingStatement注解就会根据响应参数去查询码表缓存codeField字段的值和valueField字段的值于redis
* @Author: yousili
* @Date: 2022/5/20 10:07
* @Description:
*/
public interface TranscodingMapper {
@TranscodingStatement(group = "ent_status", codeField = "code",valueField = "name")
List<Map> code_ex02();
}
xml如下
<select id="code_ex02" resultType="java.util.Map">
select * from CODE_EX02
</select>
2:在实体类待转码属性上添加注解即可
@ApiModel(value = "企业基础信息表")
@Data
@TableName(value = "ENT_BASE_INFO")
public class EntBaseInfo implements Serializable {
private static final long serialVersionUID = 7809446069751653511L;
/**
* 企业状态
*/
@TableField(value = "ENTSTATUS_NAME")
@ApiModelProperty(value = "企业状态")
@JacksonTranscoding(group = "ent_status")
private String entstatusName;
}