我正在寻找使用JPA映射枚举的不同方法。我特别想设置每个enum条目的整数值,并且只保存整数值。
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR (300);
private int value;
Right(int value) { this.value = value; }
public int getValue() { return value; }
};
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
// the enum to map :
private Right right;
}
一个简单的解决方案是将枚举注释与EnumType一起使用。顺序:
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
但是在本例中,JPA映射的是枚举索引(0,1,2),而不是我想要的值(100,200,300)。
我发现的两个解决方案似乎并不简单……
第一个解决方案
这里提出了一个解决方案,使用@PrePersist和@PostLoad将枚举转换为其他字段,并将枚举字段标记为瞬态:
@Basic
private int intValueForAnEnum;
@PrePersist
void populateDBFields() {
intValueForAnEnum = right.getValue();
}
@PostLoad
void populateTransientFields() {
right = Right.valueOf(intValueForAnEnum);
}
第二个解决方案
这里提出的第二个解决方案提出了一个通用的转换对象,但仍然看起来很重并且面向hibernate (@Type似乎在Java EE中不存在):
@Type(
type = "org.appfuse.tutorial.commons.hibernate.GenericEnumUserType",
parameters = {
@Parameter(
name = "enumClass",
value = "Authority$Right"),
@Parameter(
name = "identifierMethod",
value = "toInt"),
@Parameter(
name = "valueOfMethod",
value = "fromInt")
}
)
还有其他解决方案吗?
我有几个想法,但我不知道它们是否存在于JPA:
在加载和保存Authority对象时,使用Authority类的right成员的setter和getter方法
一个等效的想法是告诉JPA什么是右枚举的方法来将enum转换为int和int转换为enum
因为我使用Spring,有任何方法告诉JPA使用特定的转换器(理义)吗?
我自己解决这种Enum JPA映射的方案如下。
步骤1 -编写下面的接口,我们将使用所有我们想要映射到db列的枚举:
public interface IDbValue<T extends java.io.Serializable> {
T getDbVal();
}
步骤2 -实现一个自定义通用JPA转换器,如下所示:
import javax.persistence.AttributeConverter;
public abstract class EnumDbValueConverter<T extends java.io.Serializable, E extends Enum<E> & IDbValue<T>>
implements AttributeConverter<E, T> {
private final Class<E> clazz;
public EnumDbValueConverter(Class<E> clazz){
this.clazz = clazz;
}
@Override
public T convertToDatabaseColumn(E attribute) {
if (attribute == null) {
return null;
}
return attribute.getDbVal();
}
@Override
public E convertToEntityAttribute(T dbData) {
if (dbData == null) {
return null;
}
for (E e : clazz.getEnumConstants()) {
if (dbData.equals(e.getDbVal())) {
return e;
}
}
// handle error as you prefer, for example, using slf4j:
// log.error("Unable to convert {} to enum {}.", dbData, clazz.getCanonicalName());
return null;
}
}
该类将使用枚举E上的getDbVal()将枚举值E转换为类型为T的数据库字段(例如String),反之亦然。
步骤3 -让原始enum实现我们在步骤1中定义的接口:
public enum Right implements IDbValue<Integer> {
READ(100), WRITE(200), EDITOR (300);
private final Integer dbVal;
private Right(Integer dbVal) {
this.dbVal = dbVal;
}
@Override
public Integer getDbVal() {
return dbVal;
}
}
步骤4 -为步骤3的Right enum扩展步骤2的转换器:
public class RightConverter extends EnumDbValueConverter<Integer, Right> {
public RightConverter() {
super(Right.class);
}
}
第5步-最后一步是在实体中注释字段,如下所示:
@Column(name = "RIGHT")
@Convert(converter = RightConverter.class)
private Right right;
结论
恕我直言,这是最干净和最优雅的解决方案,如果你有许多枚举映射,你想使用枚举本身的一个特定字段作为映射值。
对于项目中需要类似映射逻辑的所有其他枚举,您只需重复步骤3到5,即:
在你的枚举上实现接口IDbValue;
仅用3行代码扩展EnumDbValueConverter(你也可以在你的实体中这样做,以避免创建一个分离的类);
用@Convert from javax注解enum属性。持久性方案。
希望这能有所帮助。
我自己解决这种Enum JPA映射的方案如下。
步骤1 -编写下面的接口,我们将使用所有我们想要映射到db列的枚举:
public interface IDbValue<T extends java.io.Serializable> {
T getDbVal();
}
步骤2 -实现一个自定义通用JPA转换器,如下所示:
import javax.persistence.AttributeConverter;
public abstract class EnumDbValueConverter<T extends java.io.Serializable, E extends Enum<E> & IDbValue<T>>
implements AttributeConverter<E, T> {
private final Class<E> clazz;
public EnumDbValueConverter(Class<E> clazz){
this.clazz = clazz;
}
@Override
public T convertToDatabaseColumn(E attribute) {
if (attribute == null) {
return null;
}
return attribute.getDbVal();
}
@Override
public E convertToEntityAttribute(T dbData) {
if (dbData == null) {
return null;
}
for (E e : clazz.getEnumConstants()) {
if (dbData.equals(e.getDbVal())) {
return e;
}
}
// handle error as you prefer, for example, using slf4j:
// log.error("Unable to convert {} to enum {}.", dbData, clazz.getCanonicalName());
return null;
}
}
该类将使用枚举E上的getDbVal()将枚举值E转换为类型为T的数据库字段(例如String),反之亦然。
步骤3 -让原始enum实现我们在步骤1中定义的接口:
public enum Right implements IDbValue<Integer> {
READ(100), WRITE(200), EDITOR (300);
private final Integer dbVal;
private Right(Integer dbVal) {
this.dbVal = dbVal;
}
@Override
public Integer getDbVal() {
return dbVal;
}
}
步骤4 -为步骤3的Right enum扩展步骤2的转换器:
public class RightConverter extends EnumDbValueConverter<Integer, Right> {
public RightConverter() {
super(Right.class);
}
}
第5步-最后一步是在实体中注释字段,如下所示:
@Column(name = "RIGHT")
@Convert(converter = RightConverter.class)
private Right right;
结论
恕我直言,这是最干净和最优雅的解决方案,如果你有许多枚举映射,你想使用枚举本身的一个特定字段作为映射值。
对于项目中需要类似映射逻辑的所有其他枚举,您只需重复步骤3到5,即:
在你的枚举上实现接口IDbValue;
仅用3行代码扩展EnumDbValueConverter(你也可以在你的实体中这样做,以避免创建一个分离的类);
用@Convert from javax注解enum属性。持久性方案。
希望这能有所帮助。