基于 SpringBoot+MybatisPlus+Jackson 优雅使用枚举值的方案

一、前言

关于如何“优雅”得使用枚举值讨论很多,但是很多文章少了对于“优雅”的定义,以致于给出的解决方案不够完整,增加了菜鸟程序员借鉴的成本。本文将在定义了明确目标的基础上,给出实施步骤。

二、技术栈:

  1. spring-boot
    版本:spring-boot-starter-parent 1.5.22.RELEASE
  2. mybatis-plus
    版本:mybatis-plus-boot-starter: 3.4.3

三、目标:

  1. 前端应用请求的某些 int 类型字段,在后端自动映射为枚举值
  2. 在数据库插值时,实体类包含的枚举值自动映射到数据库中对应的 int 类型字段
  3. 从数据库中查出特定 int 类型字段,后端自动将该字段转为实体类中的枚举值
  4. 包含枚举值的实体类在传递给前端应用时,自动将枚举值转为JSON对象,包含 code 以及 文字说明

四、实施:

  1. 在枚举类中,针对特定字段使用注解  @EnumValue ,标记数据库存的值是对应字段
  2. 在实体类中,使用属性  autoResultMap = true 修饰注解   @TableName 
  3. 在实体类中,使用属性  typeHandler = ValueEnumTypeHandler . class  修饰注解   @ TableField
  4. 在sql Mapper 中,使用属性 typeHandler ="${包名}.ValueEnumTypeHandler" 修饰  resultMap-resul t 标签
  5. 统一枚举类型的接口,以便构造类型为 Jackson2ObjectMapperBuilderCustomizer 的 Bean ,依赖 JSON 类库 Jackson 自定义枚举转换,实现统一的枚举类序列化
  6. 构造 toJsonObject 静态方法,统一将包含枚举值的实体对象转为 com.alibaba.fastjson.JSONObject


五、源码:

  1. 统一枚举接口:Enumerator.java
    public interface Enumerator {
    
        /**
         * Code integer.
         *
         * @return the integer
         */
        Integer code();
    
        /**
         * Description string.
         *
         * @return the string
         */
        String desc();
    
    }
    

     
  2. 枚举类:StatusEnum.java
    import com.baomidou.mybatisplus.annotation.EnumValue;
    import com.uiTest.enumPack.Enumerator;
    
    public enum StatusEnum implements Enumerator {
    
        INVALID(0, "无效"),
        VALID(1, "有效");
    
        StatusEnum(Integer code, String desc) {this.code = code;this.desc = desc;}
    
        @EnumValue//标记数据库存的值是code
        private final Integer code;
    
        private final String desc;
    
        @Override
        public Integer code() {
            return this.code;
        }
    
        @Override
        public String desc() {
            return this.desc;
        }
    
    }
    
  3. 实体类:EnumDemoEntity.java
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.uiTest.enumPack.enums.StatusEnum;
    import com.uiTest.util.mybatisPlus.ValueEnumTypeHandler;
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    
    @Data
    @TableName(value = "enum_demo", autoResultMap = true)
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class EnumDemoEntity implements Serializable {
    
        private static final long serialVersionUID = 1;
    
        @TableId(type = IdType.AUTO)
        private Integer id;
    
        @TableField
        private String username;
    
        @TableField(value = "status", typeHandler = ValueEnumTypeHandler.class)
        private StatusEnum status;
    
    }
  4. 数值转枚举处理器:ValueEnumTypeHandler.java
    
    import com.uiTest.enumPack.Enumerator;
    import org.apache.ibatis.type.BaseTypeHandler;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.MappedTypes;
    
    import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
     * 值枚举转换处理器
     * @param  枚举类型
     */
    @MappedTypes({Enumerator.class})
    public class ValueEnumTypeHandler & Enumerator> extends BaseTypeHandler {
    
        private Class type;
    
        public ValueEnumTypeHandler(Class type) {
            if (type == null) {
                throw new IllegalArgumentException("Type argument cannot be null");
            }
            this.type = type;
        }
    
        @Override
        public void setNonNullParameter(PreparedStatement ps, int i, Enumerator parameter, JdbcType jdbcType)
                throws SQLException {
            ps.setInt(i, parameter.code());
        }
    
        @Override
        public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
            int code = rs.getInt(columnName);
            return rs.wasNull() ? null : codeOf(code);
        }
    
        @Override
        public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            int code = rs.getInt(columnIndex);
            return rs.wasNull() ? null : codeOf(code);
        }
    
        @Override
        public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            int code = cs.getInt(columnIndex);
            return cs.wasNull() ? null : codeOf(code);
        }
    
        private E codeOf(int code){
            try {
                return codeOf(type, code);
            } catch (Exception ex) {
                throw new IllegalArgumentException("Cannot convert" + code + "to" + type.getSimpleName() + "by code value.", ex);
            }
        }
    
        private static  & Enumerator> E codeOf(Class enumClass, int code) {
            E[] enumConstants = enumClass.getEnumConstants();
            for (E e : enumConstants) {
                if (e.code() == code) {
                    return e;
                }
            }
            return null;
        }
    
    }
  5. 自定义 Jackson 枚举转换
    import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
    
    @Configuration
    
    public class CustomConfig {
    
        /**
         * Jackson枚举转换
         * @return
         */
        @Bean
        public Jackson2ObjectMapperBuilderCustomizer enumCustomizer(){
            return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Enumerator.class, new JsonSerializer() {
                @Override
                public void serialize(Enumerator value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
                    gen.writeStartObject();
                    gen.writeNumberField("code", value.code());
                    gen.writeStringField("desc", value.desc());
                    gen.writeEndObject();
                }
            });
        }
    
    }
  6. 实体对象转为 com.alibaba.fastjson.JSONObject
        public JSONObject toJsonObject(EnumDemoEntity serializable) throws JsonProcessingException {
            String enumDemoStr = objectMapper.writeValueAsString(serializable);
            JSONObject jsonObject = (JSONObject) JSON.parse(enumDemoStr);
            return jsonObject;
        }
  7. 前端请求 int 转枚举
        @ApiOperation("测试枚举")
        @PostMapping("/testEnum")
        public R testEnum(@RequestBody EnumDemoEntity enumDemoEntity) throws JsonProcessingException {
            JSONObject jsonObject = supportUtils.toJsonObject(enumDemoEntity);
            return R.ok().put("enumDemoEntity", jsonObject);
        }

    基于 SpringBoot+MybatisPlus+Jackson 优雅使用枚举值的方案_第1张图片


     
  8. 后端单元测试
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.uiTest.dao.platform.demo.EnumDemoDao;
    import com.uiTest.entity.demo.EnumDemoEntity;
    import com.uiTest.enumPack.enums.StatusEnum;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Slf4j
    public class BootApplicationTests {
    
        @Autowired
        private EnumDemoDao enumDemoDao;
    
        @Test
        public void enumDemoTest(){
            
            final String ENUM_DEMO_USERNAME = "赵四";
            QueryWrapper qryWrap = new QueryWrapper();
            qryWrap.lambda().eq(EnumDemoEntity::getUsername, ENUM_DEMO_USERNAME);
            EnumDemoEntity enumDemoEntity = enumDemoDao.selectOne(qryWrap);
            if(enumDemoEntity == null){
                StatusEnum statusEnum = StatusEnum.VALID;
                EnumDemoEntity entity = EnumDemoEntity.builder().username(ENUM_DEMO_USERNAME).status(statusEnum).build();
                int insertResult = enumDemoDao.insert(entity);
                System.out.println("insertResult: " + insertResult);
            }
            System.out.println("enumDemoEntity: " + enumDemoEntity);
    
        }
    
    }


 六、参考

  1. JSON类库Jackson优雅序列化Java枚举类
  2. MyBatis转换对象、枚举插入数据库的处理

你可能感兴趣的:(后端解决方案,技术创业,java,spring,boot,mybatis)