分享在Spring应用程序中使用Java枚举Java Enum映射字段的有效方法。目标:
- 一个RestController 类,以通过不同的HTTP方法(GET,POST等)获取并使用枚举。
- 一个服务类,它通常用来做一些种类的业务逻辑的实现
- 一些DAO类(例如JPA Entity和Spring Data Repository),通过保存或检索数据以非常简单的方式与数据库进行通信
我希望我的Web层(RestController)能够执行以下任务:
- 自动将请求正文字段中的JSON转换(反序列化)为枚举
- 自动将枚举转换(序列化)为JSON字段
- 自动将请求参数或路径变量转换为枚举
在DAO层中,我希望具有以下机制:
- 自动转换JPA实体的字段,该字段是要作为字符串写入数据库的枚举
- 检索varchar字段并自动映射到JPA实体的Enum字段,而不是字符串
代码在https://github.com/dariux2016/template-projects上
模型:
@Getter @Setter @ToString @EqualsAndHashCode @Builder @AllArgsConstructor @NoArgsConstructor public class Item { private String name; private EnItemType type; private String explanation;
}
|
EnItemType是一个枚举它通过类型代码将被解析为正确的数据值,然后在我们的网页和DAO层来管理。目前,仅注意此枚举有一个代码字段,通过它我们可以在整个应用程序中维护引用。public enum EnItemType {
TYPE1("CODE_1"), TYPE2("CODE_2"), TYPE3("CODE_3"); private String code; private EnItemType(String code) { this.code=code; } @JsonCreator public static EnItemType decode(final String code) { return Stream.of(EnItemType.values()).filter(targetEnum -> targetEnum.code.equals(code)).findFirst().orElse(null); } @JsonValue public String getCode() { return code; } }
|
注意上面代码中:@JsonCreator和@JsonValue批注注解,下面解释。
在 Web层将JSON与枚举序列化和反序列化
对于GET请求,我们必须返回必须以JSON方式进行转换。这就是所谓的JSON序列化操作。
我们可以调用GET方法以按项目类型查找项目,并且可以在path变量中传递Enum代码(而不是Enum名称)。访问:GET http://localhost:8080/item/CODE_1,希望获得下面数据:
{
"name":"A",
"type":"CODE_1",
"explanation":"item A of first type"
}
@JsonCreator注释仅足以将JSON数据解释为枚举,但是如果枚举值是通过路径或请求参数到达的,该怎么办?在这种情况下,我们必须使用从String类型转换为Enum的机制。为此,我实现了以下转换器:
@RequestParameterConverter public class StringToEnItemTypeConverter implements Converter<String, EnItemType> { @Override public EnItemType convert(String source) { return EnItemType.decode(source); } }
|
这是一个Spring转换器,它使用一个自定义的@RequestParameterConverter批注,我利用该批注利用了组件扫描机制。然后,我实现了注释,如下所示,声明了用该注释注释的方法是Spring Component:@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Component public @interface RequestParameterConverter {
}
|
为了注册转换器,我使用了一个特定的配置类来实现Spring Web MVC接口。此类检索使用我的自定义@RequestParameterConverter注释注释的所有Spring Bean,然后将转换器注册到FormatterRegistry中,该类是专用于在Spring中格式化数据的类。该类如下:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private ListableBeanFactory beanFactory; @Override public void addFormatters(FormatterRegistry registry) { Map<String, Object> components = beanFactory.getBeansWithAnnotation(RequestParameterConverter.class); components.values().parallelStream().forEach(c -> { if(c instanceof Converter) { registry.addConverter((Converter)c); } }); } }
|
这样,无论您在何处定义了转换器类:如果Spring对其进行了扫描,那么它将被注册并添加到格式化程序集中。
我们已经完成了Web层枚举的使用,现在我们能够:
- 自动将JSON请求正文字段转换为枚举
- 自动将枚举转换为JSON字段
- 自动将请求参数或路径变量转换为枚举
这种转换在RestController中自动进行:
@Api @RestController @RequestMapping( value = "/item", produces = { MediaType.APPLICATION_JSON_VALUE }) public class ItemController { @Autowired private ItemService itemService; @GetMapping public List<Item> findItems() { return itemService.findItems(); } @GetMapping("/{type}") public List<Item> findItemByType(@PathVariable("type") EnItemType type) { return itemService.findItemByType(type); } @PostMapping @ResponseStatus(HttpStatus.CREATED) public Item saveItem(@RequestBody Item item) { return itemService.saveItem(item); }
}
|
DAO层转换
ItemEntity:
@Entity @Table(name="items") @Getter @Setter public class ItemEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Column(name="type_code") private EnItemType type; private String explanation; }
|
在ItemEntity类中,我们直接放置了EnItemType类型的字段,并且希望在插入或更新行时将该字段自动转换为varchar(String)字段,并在执行操作时自动将该字段映射为EnItemType选择操作。
为了实现此目标,我们可以使用JPA提供的名为AttributeConverter的组件。您可以实现此接口,以便在与数据库“对话”时自动将一种数据类型转换为另一种数据类型。我的自定义转换器如下:/** * AttributeConvertEnItemTypeype, String>. Implements the following methods : * <ul> * <li>convertToDatabaseColumn : (given an Enum returns a String) * <li>convertToEntityAttribute : (given a String returns an Enum) * </ul> */ @Component @Converter(autoApply = true) public class EnItemTypeConverter implements AttributeConverter<EnItemType, String> {
@Override public String convertToDatabaseColumn(final EnItemType attribute) { return Optional.ofNullable(attribute).map(EnItemType::getCode).orElse(null); }
@Override public EnItemType convertToEntityAttribute(final String dbData) { return EnItemType.decode(dbData); } }
|
它必须实现两个方法:
- convertToDatabaseColumn:将枚举转换为字符串,在插入或更新行时使用
- convertToEntityAttribute:将来自数据库的数据(仅映射到具有EnItemType枚举的实体中的数据)转换为正确的枚举值
ItemRepository :public interface ItemRepository extends JpaRepository<ItemEntity, Long>{ public List<ItemEntity> findByType(EnItemType type);
}
|
点击标题见原文。