Java 通用枚举JPA AttributeConverter实现 我试图解决的问题
我正在尝试为Hibernate实现枚举映射。到目前为止,我已经研究了可用的选项,Java 通用枚举JPA AttributeConverter实现 我试图解决的问题,java,hibernate,jpa,generics,enums,Java,Hibernate,Jpa,Generics,Enums,我正在尝试为Hibernate实现枚举映射。到目前为止,我已经研究了可用的选项,@Enumerated(EnumType.ORDINAL)和@Enumerated(EnumType.STRING)似乎都不适合我的需要。@Enumerated(EnumType.ORDINAL)似乎非常容易出错,因为仅仅对enum常量进行重新排序就可能弄乱映射,而@Enumerated(EnumType.STRING)也不够,因为我使用的数据库中已经满是要映射的值,这些值不是我希望枚举常量命名的值(这些值是外语字符
@Enumerated(EnumType.ORDINAL)
和@Enumerated(EnumType.STRING)
似乎都不适合我的需要。@Enumerated(EnumType.ORDINAL)
似乎非常容易出错,因为仅仅对enum常量进行重新排序就可能弄乱映射,而@Enumerated(EnumType.STRING)
也不够,因为我使用的数据库中已经满是要映射的值,这些值不是我希望枚举常量命名的值(这些值是外语字符串/整数)
目前,所有这些值都映射到字符串/整数属性。同时,属性应只允许有限的值集(假设meetingStatus
属性允许字符串:PLANNED
、CANCELED
和DONE
。或者另一个属性允许一组受限的整数值:1
、2
、3
、4
、5
)
我的想法是用枚举替换实现,以提高代码的类型安全性。字符串/整数实现可能导致错误的一个很好的例子是表示此类值的字符串方法参数-使用字符串,任何东西都可以。另一方面,使用枚举参数类型引入编译时安全性
我迄今为止最好的方法
似乎满足我需求的唯一解决方案是为每个枚举实现带有@Converter
注释的自定义javax.persistence.AttributeConverter
。由于我的模型需要相当多的枚举,为每个枚举编写自定义转换器开始变得非常疯狂。因此我搜索了一个通用解决方案问题解答->如何为任何类型的枚举编写通用转换器。以下答案在这里有很大帮助:。答案中的代码示例提供了某种通用实现,但对于每个枚举,仍然需要一个单独的转换器类。答案的作者还继续说:
另一种方法是定义自定义批注,修补JPA提供程序以识别此批注。这样,您可以在生成映射信息时检查字段类型,并将必要的枚举类型提供给纯泛型转换器
这就是我想我会感兴趣的。不幸的是,我找不到更多关于这一点的信息,我需要更多的指导来理解需要做什么,以及如何使用这种方法
当前实施
公共接口PersistableEnum{
T getValue();
}
公共枚举整数实现PersistableEnum{
一(1),
两(2),
三(3),
四(4),
五(5),
六(6);
私有int值;
整数(整数值){
这个值=值;
}
@凌驾
公共整数getValue(){
返回值;
}
}
公共抽象类PersistableEnumConverter实现AttributeConverter{
私有类枚举类型;
公共PersistableEnumConverter(类enumType){
this.enumType=enumType;
}
@凌驾
公共T convertToDatabaseColumn(E属性){
返回属性.getValue();
}
@凌驾
公共E convertToEntityAttribute(T dbData){
对于(E enumConstant:enumType.getEnumConstants()){
if(enumConstant.getValue().equals(dbData)){
返回枚举常数;
}
}
抛出新的EnumConversionException(enumType,dbData);
}
}
@转换器
公共类IntegerEnumConverter扩展了PersistableEnumConverter{
公共整数转换器(){
超级(整层类);
}
}
这就是我如何实现部分通用转换器实现的原因
目标:不再需要为每个枚举创建新的转换器类。幸运的是,您不应该为此修补hibernate
import java.lang.annotation.Retention;
导入java.lang.annotation.Target;
导入java.sql.Types;
导入静态java.lang.annotation.ElementType.METHOD;
导入静态java.lang.annotation.ElementType.FIELD;
导入静态java.lang.annotation.RetentionPolicy.RUNTIME;
@目标({方法,字段})
@保留(运行时)
公共@接口枚举转换器
{
Class>enumClass()默认IntegerEnum.Class;
int sqlType()默认类型.INTEGER;
}
import java.io.Serializable;
导入java.lang.annotation.annotation;
导入java.sql.PreparedStatement;
导入java.sql.ResultSet;
导入java.sql.SQLException;
导入java.sql.Types;
导入java.util.Objects;
导入java.util.Properties;
导入org.hibernate.hibernateeexception;
导入org.hibernate.engine.spi.SharedSessionCompactmentor;
导入org.hibernate.usertype.DynamicParameterizedType;
导入org.hibernate.usertype.usertype;
公共类PersistableEnumType实现UserType、DynamicParameterizedType
{
私有int-sqlType;
私人课堂>课堂;
@凌驾
公共void setParameterValues(属性参数)
{
ParameterType读取器=(ParameterType)parameters.get(参数类型);
EnumConverter converter=getEnumConverter(读卡器);
sqlType=converter.sqlType();
clazz=converter.enumClass();
}
私有EnumConverter getEnumConverter(参数类型读取器)
{
对于(注释:reader.getAnnotationsMethod()){
if(枚举转换器的注释实例){
返回(枚举转换器)注释;
}
}
抛出新的IllegalStateException(“PersistableEnumType应与@EnumConverter注释一起使用”);
}
@凌驾
公共整数[
public interface PersistableEnum<T> {
T getValue();
}
public enum IntegerEnum implements PersistableEnum<Integer> {
ONE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6);
private int value;
IntegerEnum(int value) {
this.value = value;
}
@Override
public Integer getValue() {
return value;
}
}
public abstract class PersistableEnumConverter<E extends PersistableEnum<T>, T> implements AttributeConverter<E, T> {
private Class<E> enumType;
public PersistableEnumConverter(Class<E> enumType) {
this.enumType = enumType;
}
@Override
public T convertToDatabaseColumn(E attribute) {
return attribute.getValue();
}
@Override
public E convertToEntityAttribute(T dbData) {
for (E enumConstant : enumType.getEnumConstants()) {
if (enumConstant.getValue().equals(dbData)) {
return enumConstant;
}
}
throw new EnumConversionException(enumType, dbData);
}
}
@Converter
public class IntegerEnumConverter extends PersistableEnumConverter<IntegerEnum, Integer> {
public IntegerEnumConverter() {
super(IntegerEnum.class);
}
}
import com.pismo.apirest.mvc.enums.OperationType;
import com.pismo.apirest.mvc.enums.support.PersistableEnum;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@SuppressWarnings("unused")
public interface EnumsConverters {
@RequiredArgsConstructor
abstract class AbstractPersistableEnumConverter<E extends Enum<E> & PersistableEnum<I>, I> implements AttributeConverter<E, I> {
private final E[] enumConstants;
public AbstractPersistableEnumConverter(@NonNull Class<E> enumType) {
enumConstants = enumType.getEnumConstants();
}
@Override
public I convertToDatabaseColumn(E attribute) {
return Objects.isNull(attribute) ? null : attribute.getId();
}
@Override
public E convertToEntityAttribute(I dbData) {
return fromId(dbData, enumConstants);
}
public E fromId(I idValue) {
return fromId(idValue, enumConstants);
}
public static <E extends Enum<E> & PersistableEnum<I>, I> E fromId(I idValue, E[] enumConstants) {
return Objects.isNull(idValue) ? null : Stream.of(enumConstants)
.filter(e -> e.getId().equals(idValue))
.findAny()
.orElseThrow(() -> new IllegalArgumentException(
String.format("Does not exist %s with ID: %s", enumConstants[0].getClass().getSimpleName(), idValue)));
}
}
@Converter(autoApply = true)
class OperationTypeConverter extends AbstractPersistableEnumConverter<OperationType, Integer> {
public OperationTypeConverter() {
super(OperationType.class);
}
}
}