1,字典表Or枚举类?

项目里有很多标识状态的字段,比如订单状态:0-未支付,1-已支付,2-已取消。或者性别sex: 0-未知,1-男,2-女 。等等。一般这种我们都会建相应的枚举类,比如性别枚举:

public enum SexEnum {
    UNKNOWN(0,"未知"),
    MAN(1,"男"),
    WOMAN(2,"女");
    private final int code;
    private final String text;

    SexEnum(int code, String text) {
        this.code = code;
        this.text = text;
    }
    //省略getter方法
}

项目里一般都有字典表,我们写了枚举类,还需要把他们存在字典表么?



javax table注解 实体属性不映射数据库注解_java 字典类



以前我也没搞清楚,现在清楚了。存字典表里,可以用AOP进行统一字典翻译。比如你返回给前端一个分页列表,列表有sex字段,你就需要在接口文档写上:sex(0-未知,1-男,2-女)。像性别这种还好,一般不会再新增状态。但是其他类型字段,以后再增加一种类型,接口文档就要改,前端代码也要改。但是如果后端返回列表时,把这些字段都给前端翻译好,即使以后再增加类型,前端直接展示,就不用改代码了。比如返回的列表里,遇到这种类型值的,比如以前返回sex=1,现在返回俩字段:sex=1, sex_text=男,前端直接拿到sex_text做展示即可。

有了字典,还需要枚举类或者常量类么?需要的。因为你的代码里,常常是需要关于sex类型的判断的,比如:

if(SexEnum.MAN.getCode() == user.getSex()){

    //do something

}

这就需要在代码里再维护一份和字典一样的常量或枚举。至于用枚举还是常量类,无所谓。

2.AOP翻译字典表

一个注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 字典
 * create by lihaoyang on 2020/10/28
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {

    /**
     * 数据字典code
     * @return
     */
    String dictCode();
}

注解标在实体类属性上:

/**
     * 性别(0-默认未知,1-男,2-女)
     */
    @Dict(dictCode = "sex")
    private Integer sex;

一个切面类:

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nb.nbbase2.beans.Result;
import com.nb.nbbase2.constant.SysConstant;
import com.nb.nbbase2.service.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 字典aop类
 * create by lihaoyang on 2020/10/28
 */
@Slf4j
@Aspect  //标识该类是一个切面
@Component
public class DictAspect {

    @Autowired
    private SysDictService sysDictService;

    @Autowired
    private ObjectMapper objectMapper;

    //切入点
    @Pointcut("execution(public * com.nb.nbbase2.*.*Controller.*(..))")
    public void dictPointCut() {

    }


    @Around("dictPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        Object result = point.proceed();
        long start=System.currentTimeMillis();
        //解析字典
        this.parseDictText(result);
        long end=System.currentTimeMillis();
        log.debug("AOP解析字典耗时{}ms",(end-start));

        return result;
    }

    /**
     * 解析字典
     * @param result
     */
    private void parseDictText(Object result) {

        //如果control返回的是Result对象
        if(result instanceof Result){
            //如果是分页, TODO:是不是也可以处理不分页的list??或者单个对象?道理一样,这里就不写了
            if(((Result) result).getResult() instanceof IPage){
                //存放字典转换加工处理后的结果列表
                List items = new ArrayList<>();
                IPage iPage = (IPage) ((Result) result).getResult();//遍历分页列表,for(Object record : iPage.getRecords()){//解决@JsonFormat注解解析不了的问题,这个我没实验,先这样吧!//一行数据的json:如{"sex":1,"id":9,"userType":3,"email":"ttrrr@qq.com","username":"admin后台","status":0}
                    String json = null;try {
                        json = objectMapper.writeValueAsString(record);
                    } catch (JsonProcessingException e) {
                        log.error("翻译字典json解析失败:"+e.getMessage(),e);
                    }//存放加工处理后的一行数据,准备往里头加字段text用
                    JSONObject itemObj = JSONObject.parseObject(json);
                    System.err.println("对象==========> "+itemObj.toJSONString());//反射获取到所有属性for(Field field : getAllFields(record)){
                        Dict dict = field.getAnnotation(Dict.class);if(dict != null){//获取实体类成员变量上的@Dict注解的dictCode值 比如@Dict(dictCode = "sex")
                            String dictCode = dict.dictCode();//获取属性的值,比如sex=1
                            String filedValue =  String.valueOf(itemObj.get(field.getName()));//翻译字典,加_text
                            String itemText = convertDictValue2Text(dictCode,filedValue);
                            itemObj.put(field.getName()+ SysConstant.DICT_LABEL_SUFFIX,itemText);//date类型默认转换string格式化日期//if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){//    SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//    item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));//}
                        }
                    }
                    items.add(itemObj);
                }//set给分页
                iPage.setRecords(items);
            }
        }
    }/**
     * 根据字典code和字典值,转换为字典text文本
     * @param dictCode 字典表code 比如sex
     * @param dictValue 字典项value 比如男=1
     * @return
     */private String convertDictValue2Text(String dictCode,String dictValue){if(StringUtils.isEmpty(dictValue)){return null;
        }
        StringBuilder dictText = new StringBuilder();//可能一个字段存多个字典值,比如:允许性别字段(1,2)男女都允许,逗号分隔的,这里需要分隔一下,循环处理。//如果没有一个字典存多个字典的需求,直接用一次查询翻译就好了??
        String[] dictValueArr = dictValue.split(",");if(ArrayUtils.isNotEmpty(dictValueArr)){for(String dictVal : dictValueArr){
                String text = null;if(dictVal.trim().length() != 0){
                    text = sysDictService.findTextByCodeAndDictItemValue(dictCode,dictVal);
                }if(text != null){if(!"".equals(dictText.toString())){
                        dictText.append(",");
                    }
                    dictText.append(text);
                }
            }
        }return dictText.toString();
    }/**
     * 获取类的所有属性,包括父类
     *
     * @param object
     * @return
     */public static Field[] getAllFields(Object object) {
        Class> clazz = object.getClass();
        List fieldList = new ArrayList<>();while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));//对于继承的对象,父类的属性也得获取一下
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);return fields;
    }
}

查询用户列表,返回json效果:

{
    "message":"成功",
    "code":200,
    "result":{
        "records":[
            {
                "userType_text":"学生",
                "sex_text":"未知",
                "sex":0,
                "allowSex_text":"男,女",
                "id":6,
                "userType":2,
                "status_text":"启用",
                "allowSex":"1,2",
                "email":"asd@qq.com",
                "username":"张三",
                "status":1
            },
            {
                "userType_text":"学生",
                "sex_text":"男",
                "sex":1,
                "allowSex_text":"男",
                "id":7,
                "userType":2,
                "status_text":"启用",
                "allowSex":"1,",
                "email":"dsasd@qq.com",
                "username":"李四",
                "status":1
            },
            {
                "userType_text":"老师",
                "sex_text":"未知",
                "sex":0,
                "allowSex_text":"男",
                "id":8,
                "userType":1,
                "status_text":"启用",
                "allowSex":"1",
                "email":"gfdsa@qq.com",
                "username":"张老师",
                "status":1
            },
            {
                "userType_text":"管理员",
                "sex_text":"男",
                "sex":1,
                "allowSex_text":"男,女",
                "id":9,
                "userType":3,
                "status_text":"禁用",
                "allowSex":"1,2",
                "email":"ttrrr@qq.com",
                "username":"admin后台",
                "status":0
            }
        ],
        "total":4,
        "size":10,
        "current":1,
        "orders":[

        ],
        "optimizeCountSql":true,
        "hitCount":false,
        "searchCount":true,
        "pages":1
    },
    "timestamp":1605668907169
}

可以看到,性别sex、用户状态status、用户类型userType 都对应多了一个_text结尾的翻译字段。

对于字典表,可以存到redis缓存起来:



javax table注解 实体属性不映射数据库注解_java 字典类_02



mybatis-plus的通用枚举:听同事说他们以前用mybatis-plus的枚举,多么多么好用,看了一下,这么搞得:

实体类属性类型直接是你的枚举类:教师职称

/**
     * 职称(引用字典表)
     * 原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
     */
    private TeacherTitleEnum title;

教师职称枚举类:

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * 教师职称
 * @author lihaoyang
 * @date 2020/11/12
 */
public enum TeacherTitleEnum {

    ZHUJIAO(1, "助教"),
    JIANGSHI(2, "讲师"),
    FUJIAOSHOU(3, "副教授"),
    JIAOSHOU(4,"教授");

    @EnumValue//标记数据库存的值是code
    private final int code;
    private final String desc;



    TeacherTitleEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }
    @JsonValue    //标记响应json值
    public String getDesc() {
        return desc;
    }
}

测试:

@Test
    public void insert(){
//        SysTeacher teacher = new SysTeacher();
//        teacher.setTeacherName("小羊");
//        teacher.setTitle(TeacherTitleEnum.FUJIAOSHOU);
//        teacherMapper.insert(teacher);
        List list = teacherMapper.selectList(null);for (SysTeacher tea : list) {
            System.err.println(JSONObject.toJSONString(tea));
        }
    }打印:////        {"id":1,"teacherName":"牛贝","title":"JIANGSHI"}//        {"id":2,"teacherName":"西决","title":"ZHUJIAO"}//        {"id":3,"teacherName":"石竹","title":"FUJIAOSHOU"}//        {"id":4,"teacherName":"樟树鱼","title":"JIAOSHOU"}//        {"id":5,"teacherName":"小羊","title":"FUJIAOSHOU"}//        {"id":6,"teacherName":"小羊","title":"FUJIAOSHOU"}//        {"id":7,"teacherName":"小羊","title":"FUJIAOSHOU"}//        {"id":8,"teacherName":"小羊","title":"FUJIAOSHOU"}//        {"id":9,"teacherName":"小羊","title":"FUJIAOSHOU"}

他并没有把枚举的汉字描述翻译出来,而是把枚举的变量值翻译出来了。前端拿到怎么处理??