首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >有没有可能为JPA写一个通用的枚举转换器?

有没有可能为JPA写一个通用的枚举转换器?
EN

Stack Overflow用户
提问于 2014-05-09 20:18:50
回答 3查看 25.4K关注 0票数 38

我想为JPA编写一个转换器,它可以将任何枚举存储为大写。我们遇到的一些枚举不遵循只使用大写字母的约定,所以在重构它们之前,我仍然存储未来的值。

到目前为止,我得到的是:

代码语言:javascript
复制
package student;

public enum StudentState {

    Started,
    Mentoring,
    Repeating,
    STUPID,
    GENIUS;
}

我希望"Started“存储为"STARTED”,依此类推。

代码语言:javascript
复制
package student;

import jpa.EnumUppercaseConverter;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "STUDENTS")
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long mId;

    @Column(name = "LAST_NAME", length = 35)
    private String mLastName;

    @Column(name = "FIRST_NAME", nullable = false, length = 35)
    private String mFirstName;

    @Column(name = "BIRTH_DATE", nullable = false)
    @Temporal(TemporalType.DATE)
    private Date mBirthDate;

    @Column(name = "STUDENT_STATE")
    @Enumerated(EnumType.STRING)
    @Convert(converter = EnumUppercaseConverter.class)
    private StudentState studentState;

}

转换器目前看起来如下所示:

代码语言:javascript
复制
package jpa;


import javax.persistence.AttributeConverter;
import java.util.EnumSet;

public class EnumUppercaseConverter<E extends Enum<E>> implements AttributeConverter<E, String> {

    private Class<E> enumClass;

    @Override
    public String convertToDatabaseColumn(E e) {
        return e.name().toUpperCase();
    }

    @Override
    public E convertToEntityAttribute(String s) {
        // which enum is it?
        for (E en : EnumSet.allOf(enumClass)) {
            if (en.name().equalsIgnoreCase(s)) {
                return en;
            }
        }
        return null;
    }

}

不起作用的是,我不知道在运行时enumClass会是什么。我想不出在@Converter注解中将此信息传递给转换器的方法。

那么,有没有办法向转换器添加参数,或者稍微作弊呢?还是有别的办法?

我使用的是EclipseLink 2.4.2

谢谢!

EN

回答 3

Stack Overflow用户

发布于 2018-03-03 04:23:38

基于我的@scottb解决方案,针对hibernate 4.3进行了测试:(没有hibernate类,应该可以在JPA上运行良好)

接口枚举必须实现:

代码语言:javascript
复制
public interface PersistableEnum<T> {
    public T getValue();
}

基本抽象转换器:

代码语言:javascript
复制
@Converter
public abstract class AbstractEnumConverter<T extends Enum<T> & PersistableEnum<E>, E> implements AttributeConverter<T, E> {
    private final Class<T> clazz;

    public AbstractEnumConverter(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public E convertToDatabaseColumn(T attribute) {
        return attribute != null ? attribute.getValue() : null;
    }

    @Override
    public T convertToEntityAttribute(E dbData) {
        T[] enums = clazz.getEnumConstants();

        for (T e : enums) {
            if (e.getValue().equals(dbData)) {
                return e;
            }
        }

        throw new UnsupportedOperationException();
    }
}

您必须为每个枚举创建一个转换器类,我发现在枚举中创建静态类更容易:(jpa/hibernate可以为枚举提供接口,哦,好吧……)

代码语言:javascript
复制
public enum IndOrientation implements PersistableEnum<String> {
    LANDSCAPE("L"), PORTRAIT("P");

    private final String value;

    @Override
    public String getValue() {
        return value;
    }

    private IndOrientation(String value) {
        this.value= value;
    }

    public static class Converter extends AbstractEnumConverter<IndOrientation, String> {
        public Converter() {
            super(IndOrientation.class);
        }
    }
}

和带有注释的映射示例:

代码语言:javascript
复制
...
@Convert(converter = IndOrientation.Converter.class)
private IndOrientation indOrientation;
...

通过一些更改,您可以创建一个IntegerEnum接口并对其进行泛化。

票数 34
EN

Stack Overflow用户

发布于 2020-08-14 02:46:43

上面的解决方案真的很好。我在这里添加了一些小功能。

在实现编写转换器类的接口时,我还添加了以下内容以强制执行。当您忘记时,jpa开始使用默认机制,这实际上是模糊的解决方案(特别是当映射到某个数值时,我总是这样做)。

接口类如下所示:

代码语言:javascript
复制
public interface PersistedEnum<E extends Enum<E> & PersistedEnum<E>> {
  int getCode();
  Class<? extends PersistedEnumConverter<E>> getConverterClass();
}

与之前的帖子类似的PersistedEnumConverter。然而,在实现此接口时,您必须处理getConverterClass实现,它除了强制提供特定的转换器类之外,完全没有用。

下面是一个实现示例:

代码语言:javascript
复制
public enum Status implements PersistedEnum<Status> {
  ...

  @javax.persistence.Converter(autoApply = true)
  static class Converter extends PersistedEnumConverter<Status> {
      public Converter() {
          super(Status.class);
      }
  }

  @Override
  public Class<? extends PersistedEnumConverter<Status>> getConverterClass() {
      return Converter.class;
  }

  ...
}

我在数据库中所做的就是为每个枚举值创建一个配套的表,每个枚举值包含一行

代码语言:javascript
复制
 create table e_status
    (
       id    int
           constraint pk_status primary key,
       label varchar(100)
    );

  insert into e_status
  values (0, 'Status1');
  insert into e_status
  values (1, 'Status2');
  insert into e_status
  values (5, 'Status3');

并从使用枚举类型的位置放置fk约束。这样,就可以保证使用正确的枚举值。我特别在这里设置了值0、1和5,以显示它的灵活性,并且仍然是可靠的。

代码语言:javascript
复制
create table using_table
   (
        ...
    status         int          not null
        constraint using_table_status_fk references e_status,
        ...
   );
票数 0
EN

Stack Overflow用户

发布于 2022-01-28 01:39:44

如果你不介意反思,这是可行的。归功于另一个人,所以回答内联。

代码语言:javascript
复制
abstract class EnumTypeConverter<EnumType,ValueType> implements AttributeConverter<EnumType, ValueType> {

    private EnumType[] values

    @Override
    ValueType convertToDatabaseColumn(EnumType enumInstance) {
        return enumInstance ? enumInstance.getProperty(getValueColumnName()) : null
    }

    @Override
    EnumType convertToEntityAttribute(ValueType dbData) {

        if(dbData == null){
            return null
        }

        EnumType[] values = getValues()
        EnumType rtn = values.find {
            it.getProperty(getValueColumnName()).equals(dbData)
        }
        if(!rtn) {
            throw new IllegalArgumentException("Unknown ${values.first().class.name} value: ${dbData}")
        }
        rtn
    }

    private EnumType[] getValues() {
        if(values == null){
            Class cls = getTypeParameterType(getClass(), EnumTypeConverter.class, 0)
            Method m = cls.getMethod("values")
            values = m.invoke(null) as EnumType[]
        }
        values
    }

    abstract String getValueColumnName()

    // https://stackoverflow.com/a/59205754/3307720
    private static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex])
    }

    private static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>()
        Class<?> superClass
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>()
            Type superGeneric = subClass.getGenericSuperclass()
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters()
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments()

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i]
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType)
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType
                    superMap.put(typeParams[i], actualType)
                }
            }
            subClass = superClass
            subMap = superMap
        }
        return null
    }
}

然后在实体类中:

代码语言:javascript
复制
enum Type {
        ATYPE("A"), ANOTHER_TYPE("B")
        final String name

        private Type(String nm) {
            name = nm
        }
    }

...

@Column
Type type

...

@Converter(autoApply = true)
    static class TypeConverter extends EnumTypeConverter<Type,String> {
        String getValueColumnName(){
            "name"
        }
}

这是用groovy编写的,因此您需要对Java进行一些调整。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23564506

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档