1. Automatic conversion of Spring Boot enumeration types

Requirement: We usually define numeric enumeration constants in the database. Either serialization or deserialization requires manual conversion to the enumeration type. Can we make them automatic conversion? Let’s try it out:

First work out how to receive enumeration types.

Enumerated the parent class

/** * @author rookie */ public interface IEnum<T extends Serializable> {/** * getValue(); }Copy the code

1.1 Request header receiving parameters

The Convert

@Component public class EnumConvertFactory implements ConverterFactory<String, IEnum<? >> { @Override public <T extends IEnum<? >> Converter<String, T> getConverter(Class<T> targetType) { return new StringToEnum<>(targetType); } public static class StringToEnum<T extends IEnum<? >> implements Converter<String, T> { private final Class<T> targetType; public StringToEnum(Class<T> targetType) { this.targetType = targetType; } @Override public T convert(String source) { if (! StringUtils.hasText(source)) { return null; } return (T) EnumConvertFactory.getEnum(this.targetType, source); } } public static <T extends IEnum<? >> T getEnum(Class<T> targetType, String source) { for (T constant : targetType.getEnumConstants()) { if (source.equals(String.valueOf(constant.getValue()))) { return constant; } } return null; }}Copy the code

Register the Convert

@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private EnumConvertFactory enumConvertFactory; @Override public void addFormatters(FormatterRegistry registry) { registry.addConverterFactory(enumConvertFactory); }}Copy the code

We simply implement the IEnum and define the corresponding enumeration type in our receiving entity class to automatically convert it to an enumeration type, for example:

@getter @allargsconstructor Public enum implements IEnum<String>{/** * test */ TEST_ENUM("1","2"); private final String value; private final String msg; }Copy the code

1.2. Request body reception

Jackson receive enumeration

If we receive a JSON string type, Jackson will default to the subscript, which is not the same as getting the enumeration based on the matching value, so make the following changes:

Add an enumeration deserialization handler

@Data @EqualsAndHashCode(callSuper = true) public class EnumDeserializer extends JsonDeserializer<Enum<? >> implements ContextualDeserializer { private Class<? > target; @SuppressWarnings("all") @Override public Enum<? > deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException { if (! StringUtils.hasText(jsonParser.getText())) { return null; } if (IEnum.class.isAssignableFrom(target)) { return (Enum<? >) EnumConvertFactory.getEnum((Class) target, jsonParser.getText()); } return defaultEnumTransform(target,jsonParser.getText()); } /** * @param ctx ctx * @param property property * @return 1 * @throws JsonMappingException */ @Override public JsonDeserializer<? > createContextual(DeserializationContext ctx, BeanProperty property) throws JsonMappingException { Class<? > rawCls = ctx.getContextualType().getRawClass(); EnumDeserializer enumDeserializer = new EnumDeserializer(); enumDeserializer.setTarget(rawCls); return enumDeserializer; } public static Enum<? > defaultEnumTransform(Class<? > type, String indexString) { Enum<? >[] enumConstants = (Enum<? >[]) type.getEnumConstants(); try { int index = Integer.parseInt(indexString); return enumConstants[index]; } catch (NumberFormatException e) { return null; }}}Copy the code

Register handler

@Component public class JacksonConfig implements SmartInitializingSingleton { @Autowired private ObjectMapper objectMapper; @Override public void afterSingletonsInstantiated() { SimpleModule simpleModule = new SimpleModule(); simpleModule.addDeserializer(Enum.class, new EnumDeserializer()); objectMapper.registerModule(simpleModule); }}Copy the code

Use the same method as above.

1.3. Add custom enumeration serialization

Next we need to figure out how to convert numeric constant enumerations in the database to

By default, the Jackson serialization is by name and does not correspond to a value we want to return from the enumeration. Let’s make a minor change:

Add serialization Handler

public class IEnumSerializer extends JsonSerializer<IEnum> { @Override public void serialize(IEnum iEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(iEnum.getName()); }}Copy the code

Register serialization handler

@Component public class BeanLoadProcess implements SmartInitializingSingleton { @Autowired private ObjectMapper objectMapper; @Override public void afterSingletonsInstantiated() { SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(IEnum.class,new IEnumSerializer()); objectMapper.registerModule(simpleModule); }}Copy the code

Adding custom enumeration handlers:

import com.galaxy.sentry.alarm.constant.enums.IEnum; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * override enumeration handler, database can only store code type int, other types of conversion error * can extend the following processing: * @author rookie */ Public class IEnumOrdinalTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E>  { private final Class<E> type; private final E[] enums; public IEnumOrdinalTypeHandler(Class<E> type) { if (type == null) { throw new IllegalArgumentException("Type argument cannot be null"); } else { this.type = type; this.enums = (E[]) type.getEnumConstants(); if (this.enums == null) { throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type."); } } } @Override public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { ps.setInt(i, parameter.ordinal()); } @Override public E getNullableResult(ResultSet rs, String columnName) throws SQLException { int ordinal = rs.getInt(columnName); return ordinal == 0 && rs.wasNull() ? null : this.toOrdinalEnum(ordinal); } @Override public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { int ordinal = rs.getInt(columnIndex); return ordinal == 0 && rs.wasNull() ? null : this.toOrdinalEnum(ordinal); } @Override public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { int ordinal = cs.getInt(columnIndex); return ordinal == 0 && cs.wasNull() ? null : this.toOrdinalEnum(ordinal); } private E toOrdinalEnum(int ordinal) { try { if (IEnum.class.isAssignableFrom(type)) { for (E constant : type.getEnumConstants()) { if (ordinal == ((IEnum<Integer>) constant).getCode()) { return constant; } } return null; } return this.enums[ordinal]; } catch (Exception var3) { throw new IllegalArgumentException("Cannot convert " + ordinal + " to " + this.type.getSimpleName() + " by ordinal value.", var3); }}}Copy the code

Since the first step is to convert the database constants to enumerations, let’s define Mybatis Plus enumerators

mybatis-plus:
  configuration:
    default-enum-type-handler: XXX.config.IEnumOrdinalTypeHandler
Copy the code

All right, that’s it.

For more content, please pay attention to the official account or official website.