数据库id转name

我们遇到了什么

在我们日常的工作过程中 设计表的时候是不是经常遇到如用户表有个组织id对应的是组织表id,但是前端再查询用户信息时确需要的是组织名称,或者用户表有个status状态在字典表里对应0-编辑中 1-发布成功 2-发布失败  数据库存的是数字但是前端确需要中文显示 这种现象及其常见
比如用户表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U3U9ItSG-1598408486970)(/uploads/it-community/images/m_7a52d6c8c76bfebddfe2cbe8f481fc30_r.png)]

传统手段解决办法

针对用户表有个组织id对应的是组织表id,但是前端再查询用户信息时确需要显示的是组织名称 常见的手段是两个表做关联查询 或者 在service里查询出部门信息时再通过id反查,当然了如果是jpa有外键关联时会自动查询出数据(懒加载 但是通常不会这样做)
针对用户表有个status状态在字典表里对应0是编辑中 1-发布成功 2-发布失败  数据库存的是数字但是前端确需要中文 常见手段要么是关联查询或者control转换 或者前端转换成中文再显示

传统手段有什么问题

问题一

要是一个表关联的太多 是不是要要关联很多表 比如用户表 我有7-8个需要关联其他表 不可能join on把 会死人的

问题二

另外分布式体系下 两张表 不在一起怎么关联 通常基础表和业务表不在一起 咋整 改代码 哈哈 改把

问题三

status状态在字典表意义要修改 比如 加了个3类型 后端改吗还是前端改 貌似都是蛋痛 都不想改 那pk把

有银蛋吗

有没有更好的方式解决呢 其实没有蛋啦 
纵观spring体系 我们和前端交互的是什么 一般是json???  对是的 如果你不是json 那就json转xml xml还不是 自己想办法
spring默认的json是什么呢??
jackson 对 为什么不是fastjson 因为fastjson问题多不安全
回归正题 对于json 我们是不是再返回给前端时统一处理呢 那我们是不是可能拦截json bean 然后反切 重新json 添加我们需要的字段呢 查阅jackson 立马斩首行动

1.开始

我们统一接口

枚举类

/**
 * 将数据库id转成name
 * <br />
 * jackson在将对象转成json时,在需要将id转成name的get属性方法上,打上该注解即可。
 * <br />
 * 注意:如果该转换对象没有存到缓存中,将导致N+1查询,请谨慎使用(此种情况建议使用数据库连接查询)
 * @author
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface IdToName {

    /** 真实的处理器,值为spring容器中的bean名称 */
    String handle();

    /** 追加输出原始的字段名称+Name*/
    String appendFieldSuffixName() default "Name";

    /** 是否全名 如果是 输出appendFieldSuffixName*/
    boolean isFullName() default  false;
}

统一接口

/**
 * 将数据库id转成name
 * @author zzy
 */
public interface IdToNameHandle {

    /**
     * id转成name
     * @param primaryKey
     * @return
     */
    String idToName(Object primaryKey);

}

2.实现类

package com.ut.approvalflow.common.jackson.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.ut.approvalflow.common.SpringContextHolder;
import com.ut.approvalflow.common.jackson.annotation.IdToName;
import com.ut.approvalflow.common.jackson.handle.IdToNameHandle;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;

/**
 * json id to name转换器
 * 使用方法:如果是数据字典转换 则在vo字段上加上
 *
 *    @JsonSerialize(using = FieldValEscJsonSerializable.class)
 *    @TypeClass(handle = "STATUS")
 *    其中STATUS表示 字典类型
 *    如果是id从另一张表转换 则在vo字段上加上
 *     @JsonSerialize(using = FieldValEscJsonSerializable.class)
 *     @IdToName(handle = "userInfoIdToNameHandle")
 *     其中userInfoIdToNameHandle表示处理器
 *    具体参考OrganizationVO
 * @author zzy
 */

public class FieldValEscJsonSerializable extends JsonSerializer<Object>  {
    
    protected Logger logger = LoggerFactory.getLogger(this.getClass());


    @Override
    public void serialize(Object value, JsonGenerator jsonGenerator,SerializerProvider serializerProvider) throws IOException {
        Object obj = jsonGenerator.getCurrentValue();
        String fieldName = jsonGenerator.getOutputContext().getCurrentName();
        Field field = null;
        Class<? extends Object> cls = obj.getClass();
        try {
            field = cls.getDeclaredField(fieldName) ;
        } catch (Exception e) {
            logger.error("", e);
        }
        if (field != null && field.isAnnotationPresent(IdToName.class)) {
            //id转换
            IdToName idToNameAnnotation = field.getAnnotation(IdToName.class);
            String handle = idToNameAnnotation.handle();
            /*
              *写转换前的属性
              * 原始的字段名称(ID原始值的字段名)
             */
            String appendFieldSuffixName = idToNameAnnotation.appendFieldSuffixName();
            boolean isFullName = idToNameAnnotation.isFullName();
            if(!isFullName){
                appendFieldSuffixName = fieldName+appendFieldSuffixName;
            }
            Map<String, IdToNameHandle> idToNameHandleMap = SpringContextHolder.getApplicationContext().getBeansOfType(IdToNameHandle.class) ;
            if (idToNameHandleMap != null) {
                IdToNameHandle idToNameService = idToNameHandleMap.get(handle);
                if (idToNameService != null) {
                    String newValue = StringUtils.trimToEmpty(idToNameService.idToName(value));
                    jsonWriteString(jsonGenerator, value, appendFieldSuffixName,newValue);
                    return;
                }
            }
        }
        jsonGenerator.writeString(value + "");
    }

    protected void jsonWriteString(JsonGenerator jsonGenerator, Object value, String apppendFieldSuffixName, String newValue) throws IOException {
        Object obj = jsonGenerator.getCurrentValue();
        Class<? extends Object> cls = obj.getClass();
        jsonGenerator.writeString(value + "");
        if(StringUtils.isNotBlank(apppendFieldSuffixName)){
            try {
                Field field = cls.getDeclaredField(apppendFieldSuffixName) ;
                if(StringUtils.isNotBlank(newValue)){
                    field.setAccessible(true);
                    field.set(obj,newValue);
                }
            } catch (Exception e) {
                jsonGenerator.writeStringField(apppendFieldSuffixName, newValue + "");
            }

        }
    }
}

3.如何使用

比如我的用户表vo

@Data
public class UserinfoMapVO  {

  
    /**
     * 所属部门id
     */
    //@NotNull(groups = {UtCreate.class}, message = "部门id为空")
    @JsonSerialize(using = FieldValEscJsonSerializable.class)
    @IdToName(handle = "departmentIdToNameHandle",isFullName=true,appendFieldSuffixName="departmentName")
    private String departmentId;

    /**
     * 所属岗位
     */
   // @NotNull(groups = {UtCreate.class}, message = "岗位id为空")
    @JsonSerialize(using = FieldValEscJsonSerializable.class)
    @IdToName(handle = "positionIdToNameHandle",isFullName=true,appendFieldSuffixName="positionName")
    private String positionId;


    /**
     * 所属上级用户
     */
    @JsonSerialize(using = FieldValEscJsonSerializable.class)
    @IdToName(handle = "userInfoIdToNameHandle",isFullName=true,appendFieldSuffixName="parentUserName")
    private String parentId;


    /**
     * 组织id
     */
    @NotNull(groups = {UtCreate.class}, message = "组织id为空")
    @JsonSerialize(using = FieldValEscJsonSerializable.class)
    @IdToName(handle = "organizationIdToNameHandle",isFullName=true,appendFieldSuffixName="organizationName")
    private String organizationId;

    /**
     * 第三方用户id
     */
    //@NotNull(groups = {UtCreate.class}, message = "第三方用户id为空")
    @Length(min=2,max=32,message="用户标识符长度范围为2-32")
    @Pattern(regexp = "^[0-9a-zA-Z]+$", message = "用户标识符格式有误,支持英文字母、数字")
    private String thirdUserId;

   // @NotNull(groups = {UtCreate.class}, message = "角色id为空")
    @JsonSerialize(using = FieldValEscJsonSerializable.class)
    @IdToName(handle = "roleIdToNameHandle",isFullName=true,appendFieldSuffixName="roleName")
    private String roleId;

    @CollectionQueryParam(name = "departmentId")
    private List<String> departmentIdList;

....其他字段
}

IdToNameHandle的各个具体实现类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ezci0tce-1598408486975)(/uploads/it-community/images/m_c2508eca92ae6b3fa7d731f2af73730a_r.png)]
ps:IdToNameHandle一定要加缓存 不管用feign还是本地查询 加了缓存 这样前端分页查询 只有第一次会做穿透 其他其实都只是内存合并 效率其高

/**
 * @program: http-uniform-sample
 * 注意方法最好加缓存
 * @description: 举例
 * @author: zzy
 * @create: 2020-05-24 14:25
 **/
@Component
public class OrganizationIdToNameHandle implements IdToNameHandle {
    @Autowired
    private OrganizationService organizationService;
    @Override
    @Cached(name="organizationId.",key="#primaryKey", expire = 600,cacheType = CacheType.LOCAL)
    public String idToName(Object primaryKey) {
        if(primaryKey==null){
            return "";
        }
        String key = (String)primaryKey;
        return organizationService.queryById(key).getOrganizationName();
    }


}

返回接口样子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I69fwQfL-1598408486980)(/uploads/it-community/images/m_3635efa86835e15ae03db77cf8b712ab_r.png)]

总结

总的来说 就是json切面统一处理这种查询 方便前后端 当然解决办法很多 就是看怎么统一

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值