SpringBoot基于jackson序列化的字典表转换的注解实现

基本实现流程

启动时自动查询码表存储至缓存–>根据注解识别带转码字段–>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;
    
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值