分享在Spring应用程序中使用Java枚举Java Enum映射字段的有效方法。目标:

一个RestController类,以通过不同的HTTP方法(GET,POST等)获取并使用枚举。

一个服务类,它通常用来做一些种类的业务逻辑的实现

一些DAO类(例如JPA Entity和Spring Data Repository),通过保存或检索数据以非常简单的方式与数据库进行通信

我希望我的Web层(RestController)能够执行以下任务:

自动将请求正文字段中的JSON转换(反序列化)为枚举

自动将枚举转换(序列化)为JSON字段

自动将请求参数或路径变量转换为枚举

在DAO层中,我希望具有以下机制:

自动转换JPA实体的字段,该字段是要作为字符串写入数据库的枚举

检索varchar字段并自动映射到JPA实体的Enum字段,而不是字符串

模型:

@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 {
@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 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 findItems() {
return itemService.findItems();
}
@GetMapping("/{type}")
public List 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 :
* 
* 
convertToDatabaseColumn : (given an Enum returns a String) 
 
* 
convertToEntityAttribute : (given a String returns an Enum) 
 
* 
*/@Component
@Converter(autoApply = true)
public class EnItemTypeConverter implements AttributeConverter {
@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{
public List findByType(EnItemType type);
}