java开发 - 枚举类型自定义转换 - 自定义序列化/反序列化,mybatis/jpa字段转换

550 阅读4分钟

项目牵扯到了jpa和mybatis,枚举映射的值类型有字符串,数值,自定义信息,所以统一了一下规则,方便开发,反正我觉得很好用

//举例
enum Weekday {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY 
}

字符串可以使用 
@Enumerated(EnumType.STRING)
这个是按照枚举的枚举值的名称进行存储
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY 

数值可以使用
@Enumerated(EnumType.ORDINAL)
这个是按照枚举的枚举值的次序进行存储
0,1,2,3,4,5,6
如果我想存储的时候SUNDAY代表的是0, 那么就要改变顺序,如果已经上线了,就可能带来很大的风险

实战

//枚举类字段的使用
/**
 * 星期 1.星期一 2.星期二 3.星期三 4.星期四 5.星期五 6.星期六 7.星期日
 */
@ApiModelProperty("星期 1.星期一 2.星期二 3.星期三 4.星期四 5.星期五 6.星期六 7.星期日")
//mybatis-plus字段映射 ,@Result字段映射
@TableField(value = "weekday", typeHandler = Weekday.WeekdayConverter.class)
//反序列化的场景是前端传值时可以自定义使用
@JsonDeserialize(using = Weekday.WeekdayJsonDeserializer.class)
//序列化为枚举继承的泛型类型值, 例如:redis缓存,json相应数据等
@JsonSerialize(using = EnumConstant.EnumJsonSerializer.class)
//jpa框架字段映射
@Convert(converter = Weekday.WeekdayConverter.class)
//jpa字段映射信息
@Column(name = "weekday", columnDefinition = "tinyint NOT NULL DEFAULT 1  COMMENT '星期 1.星期一 2.星期二 3.星期三 4.星期四 5.星期五 6.星期六 7.星期日'")
private Weekday weekday;
//枚举类
public enum Weekday implements EnumConstant<Integer> {

   //星期一
   MONDAY(1, 1, "星期一", "悲催的周一"),
   //星期二
   TUESDAY(2, 2, "星期二", "周二我爱摸鱼"),
   //星期三
   WEDNESDAY(3, 3, "星期三", "调休去看电影"),
   //星期四
   THURSDAY(4, 4, "星期四", "期待中度过"),
   //星期五
   FRIDAY(5, 5, "星期五", "哼着欢快的小曲"),
   //星期六
   SATURDAY(6, 6, "星期六", "加班吐血中"),
   //星期日, 
   //注意这里的类型值是0
   SUNDAY(0, 7, "星期日", "吸血鬼继续压榨");

   Weekday(Integer typeValue, Integer value, String name, String showInfo) {
      this.typeValue = typeValue;
      this.value = value;
      this.name = name;
      this.showInfo = showInfo;
   }

   /**
    * 类型值,没想好怎么用,保留
    */
   private final Integer typeValue;

   /**
    * 枚举值
    */
   private final Integer value;

   /**
    * 枚举名称
    */
   private final String name;

   /**
    * 展示信息
    */
   private final String showInfo;

   /**
    * 获取默认值
    * 如果需要就返回,不需要就不返回
    *
    * @return 这里返回的默认的周一
    */
   @Override
   public Integer getDefaultValue() {
      return MONDAY.getValue();
   }

   /**
    * 获取枚举值
    *
    * @return 枚举值
    */
   @Override
   public Integer getValue() {
      return this.value;
   }

   /**
    * 获取枚举名称
    *
    * @return 名称
    */
   @Override
   public String getName() {
      return this.name;
   }

   /**
    * 获取枚举展示信息
    * 可以在导出报表的时候作为直接展示使用,或者是返回给前端直接使用
    * 这个方法复写了父类
    *
    * @return 展示信息
    */
   @Override
   public String getExportShowValue() {
      return this.showInfo;
   }


   /**
    * mybatis与jpa框架数据库枚举字段转换
    * 如果没有使用到相应的框架, 可以把相应的继承或实现的代码删除
    * 如果不使用jpa框架,@Converter可以不写
    */
   @Converter
   public static class WeekdayConverter extends AbstractEnumConverter<Weekday, Integer> {

   }

   /**
    * 自定义json反序列化
    */
   public static class WeekdayJsonDeserializer extends EnumJsonDeserializer<EnumConstant<Integer>, Integer> {

      @Override
      public Weekday deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
         ObjectCodec codec = p.getCodec();
         String value = codec.readValue(p, String.class);
         Weekday[] enumConstants = Weekday.values();
         for (Weekday enumConstant : enumConstants) {

            //这里使用显示信息反序列化枚举
            //例如前端传值为"哼着欢快的小曲",则为星期五,枚举值为FRIDAY
            if (enumConstant.getExportShowValue().equals(value)) {
               return enumConstant;
            }
         }
         throw new IllegalArgumentException("没有找到值");
      }
   }
}
//继承的枚举常量信息
public interface EnumConstant<T> {

   /**
    * 获取默认值
    *
    * @return
    */
   default T getDefaultValue(){
      return null;
   }

   /**
    * 获取枚举值
    *
    * @return 枚举值
    */
   T getValue();

   /**
    * 获取名称
    *
    * @return 名称
    */
   String getName();

   /**
    * 导出excel等的显示值
    *
    * @return 显示值
    */
   default String getExportShowValue() {
      return getName();
   }

   /**
    * 根据值获取枚举
    *
    * @param value 值
    * @return 枚举
    */
   default EnumConstant<T> findByValue(T value) {
      Class<? extends EnumConstant<T>> aClass = (Class<? extends EnumConstant<T>>) this.getClass();

      EnumConstant<T>[] enumConstants = aClass.getEnumConstants();
      for (EnumConstant<T> enumConstant : enumConstants) {
         if (enumConstant.getValue().equals(value)) {
            return enumConstant;
         }
      }
      throw new IllegalArgumentException("没有找到值");
   }

   /**
    * mybatis框架与jpa框架枚举类型自定义转换
    */
   abstract class AbstractEnumConverter<E extends EnumConstant<T>, T> extends BaseTypeHandler<EnumConstant<T>> implements AttributeConverter<EnumConstant<T>, T> {

      protected Class<E> constant;

      {
         constant = (Class<E>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
      }

      @Override
      public void setNonNullParameter(PreparedStatement ps, int i, EnumConstant<T> parameter, JdbcType jdbcType) throws SQLException {
         T value = parameter.getValue();
         ps.setObject(i, value);
      }

      @Override
      public EnumConstant<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
         T columnValue = (T) rs.getObject(columnName);
         return getValue(columnValue);

      }

      @Override
      public EnumConstant<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
         T columnValue = (T) rs.getObject(columnIndex);
         return getValue(columnValue);

      }

      @Override
      public EnumConstant<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
         T columnValue = (T) cs.getObject(columnIndex);
         return getValue(columnValue);
      }

      /**
       * 根据值获取枚举
       * @param columnValue 值
       * @return 枚举
       */
      public EnumConstant<T> getValue(T columnValue) {
         EnumConstant<T>[] enumConstants = constant.getEnumConstants();
         for (EnumConstant<T> enumConstant : enumConstants) {
            if (enumConstant.getValue().equals(columnValue)) {
               return enumConstant;
            }
         }
         throw new IllegalArgumentException("没有找到值");
      }

      /**
       * 转换枚举到数据库字段
       *
       * @param enumConstant the entity attribute value to be converted
       * @return 存储数据
       */
      @Override
      public T convertToDatabaseColumn(EnumConstant<T> enumConstant) {
         if (Objects.isNull(enumConstant)) {
            throw new IllegalArgumentException("转换参数为空");
         }
         return enumConstant.getValue();
      }

      /**
       * 转换数据为枚举
       *
       * @param value the data from the database column to be
       *              converted
       * @return 枚举数据
       */
      @SneakyThrows
      @Override
      public EnumConstant<T> convertToEntityAttribute(T value) {
         return this.getValue(value);
      }

   }

   /**
    * json序列化
    */
   class EnumJsonSerializer<E extends EnumConstant<T>, T> extends JsonSerializer<EnumConstant<T>> {

      @Override
      public void serialize(EnumConstant<T> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
         gen.writeObject(value.getValue());
      }
   }

   /**
    * json反序列化
    */
   class EnumJsonDeserializer<E extends EnumConstant<T>, T> extends JsonDeserializer<EnumConstant<T>> {

      @Override
      public EnumConstant<T> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
         ObjectCodec codec = p.getCodec();
         Field field = findField(p.getCurrentName(), p.getCurrentValue().getClass());
         Class<E> e = (Class<E>) field.getType();
         Type[] genericInterfaces = e.getGenericInterfaces();
         ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
         Class<T> t = (Class<T>) genericInterface.getActualTypeArguments()[0];
         T value = codec.readValue(p, t);

         EnumConstant<T>[] enumConstants = e.getEnumConstants();
         for (EnumConstant<T> enumConstant : enumConstants) {
            if (enumConstant.getValue().equals(value)) {
               return enumConstant;
            }
         }
         throw new IllegalArgumentException("没有找到值");
      }

      public Field findField(String name, Class<?> c) {
         for (; c != null; c = c.getSuperclass()) {
            for (Field field : c.getDeclaredFields()) {
               if (Modifier.isStatic(field.getModifiers())) {
                  continue;
               }
               if (field.getName().equals(name)) {
                  return field;
               }
            }
         }
         return null;
      }
   }

}