我想为JPA写一个转换器,把任何枚举都存储为大写。我们遇到的一些枚举还没有遵循只使用大写字母的约定,所以直到它们被重构之前,我仍然存储未来的值。
目前为止我得到的是:
package student;
public enum StudentState {
Started,
Mentoring,
Repeating,
STUPID,
GENIUS;
}
我希望将"已开始"存储为"已开始"等。
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;
}
转换器当前如下所示:
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在运行时是什么。2而且我也找不到一种方法来把这个信息传递给@Converter注解中的转换器。
那么有没有办法给转换器添加参数或者做点小手脚呢?或者有没有别的办法呢?
我使用的是Eclipse链接2.4.2
谢谢!
7条答案
按热度按时间w7t8yxp51#
基于@scottb解决方案,我做了这个,在hib4.3上测试:(没有休眠类,应该可以在JPA上运行)
接口枚举必须实现:
基本抽象转换器:
必须为每个枚举创建一个转换器类,我发现在枚举内部创建静态类更容易:(JPA/Hibernate可以只提供枚举的接口,哦,好吧...)
带注解的Map示例:
通过一些更改,您可以创建一个IntegerEnum接口并将其泛化。
whlutmcx2#
你需要做的是写一个泛型基类,然后为每个你想要持久化的枚举类型扩展它,然后在
@Converter
注解中使用扩展的类型:其中
Foo
是要处理的枚举。另一种方法是定义一个自定义注解,修补JPA提供程序以识别该注解,这样,您就可以在构建Map信息时检查字段类型,并将必要的枚举类型提供给一个纯泛型转换器。
相关:
x6yk4ghg3#
这个答案已经过修改,以利用Java 8中的
default
接口方法。设施点的组件数量(下面列举)仍然是四个,但是所需的样板文件数量要少得多。以前的
AbstractEnumConverter
类已经被一个名为JpaEnumConverter
的接口所取代,该接口现在扩展了JPAx 1 m3n1x接口。此外,每个占位符JPAx 1 m4n1x类现在只需要实现一个抽象方法,该方法返回枚举的Class<E>
对象(甚至更少的样板)。此解决方案与其他解决方案类似,并且还利用了JPA 2.1中引入的JPA Converter工具。由于Java 8中的泛型类型没有具体化,因此似乎没有一种简单的方法可以避免为您希望能够转换为数据库格式或从数据库格式转换的每个Java枚举编写单独的占位符类。
不过,您可以将编写枚举转换器类的过程简化为纯样板。此解决方案的组件包括:
Encodable
接口;枚举类的协定,该协定为每个枚举常量赠款对String
令牌的访问权限。该协定仅编写一次,并由所有要通过JPA持久化的枚举类实现。此接口还包含一个静态工厂方法,用于为其匹配令牌取回枚举常量。JpaEnumConverter
接口;提供了用于将标记转换为枚举常量或从枚举常量转换标记的公共代码。这也只编写了一次,并由项目中的所有占位符@Converter
类实现。1.项目中的每个Java枚举类都实现
Encodable
接口。1.每个JPA占位符@Converter类都实现
JpaEnumConverter
接口。Encodable
接口很简单,包含一个静态工厂方法forToken()
,用于获取枚举常量:JpaEnumConverter
接口是一个通用接口,也很简单。它扩展了JPA 2.1AttributeConverter
接口,并实现了用于在实体和数据库之间来回转换的方法。这些方法随后由每个JPA @Converter类继承。每个占位符类必须实现的唯一抽象方法是返回枚举的Class<E>
对象的方法。下面显示了一个具体的枚举类示例,现在可以使用JPA 2.1 Converter工具将其持久化到数据库中(请注意,它实现了
Encodable
,并且每个枚举常量的令牌都定义为私有字段):每个占位符JPA 2.1
@Converter
类的样板现在看起来像下面的代码。注意,每个这样的转换器都需要实现JpaEnumConverter
并提供getEnumClass()
的实现......仅此而已!JPAAttributeConverter
接口方法的实现是继承的。这些占位符
@Converter
类可以很容易地嵌套为其关联枚举类的static
成员类。qnakjoqk4#
以上的解决方案都很好,我在这里补充一下。
我还添加了以下内容,以便在编写转换器类实现接口时强制执行。当你忘记jpa开始使用默认机制时,这些机制实际上是模糊的解决方案(特别是当Map到某个数值时,我总是这样做)。
接口类如下所示:
PersistedEnumConverter类似于之前的帖子。但是,当实现这个接口时,您必须处理getConverterClass实现,除了强制提供特定的转换器类之外,它完全无用。
下面是一个示例实现:
我在数据库中所做的总是为每个枚举创建一个伴随表,每个枚举值对应一行
并且在使用枚举类型的任何地方设置一个fk约束。这样就可以保证使用正确的枚举值。我特别在这里设置了值0,1和5来显示它是多么的灵活,并且仍然是可靠的。
46scxncf5#
我发现了一种不用java.lang.Class、默认方法或反射就能做到这一点的方法。我是通过使用一个Function来做到的,该Function是通过方法引用从枚举传递给构造函数中的Convertor的。另外,来自枚举的Convertos应该是私有的,不需要它们在外部。
1.枚举为了持久化而应实现的接口
1.抽象转换器将使用函数来覆盖convertToEntityAttribute转换
1.枚举将实现接口(我使用lombok作为getter),并通过使用接收Function的构造函数创建转换后的,我使用方法引用传递ofCode。我更喜欢这样,而不是使用java.lang.Class或使用反射,我在枚举中有更多的自由。
4.在实体中,你只需要使用枚举类型,它会保存它的String代码。
ufj5ltwl6#
如果你不介意反思的话,这是可行的。
然后在实体类中:
这是用groovy编写的,因此需要针对Java做一些调整。
gk7wooem7#
对于那些在Kotlin工作的人,这里有一个抽象转换器的例子: