Kar*_*och 180 java orm enums spring jpa
我正在寻找使用JPA映射枚举的不同方法.我特别想设置每个枚举条目的整数值并仅保存整数值.
@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;
}
Run Code Online (Sandbox Code Playgroud)
一个简单的解决方案是使用EnumType注释和EnumType.ORDINAL:
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,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);
}
Run Code Online (Sandbox Code Playgroud)
这里提出的第二个解决方案提出了一个通用的转换对象,但仍然看起来很重,并且面向hibernate(Java EE中似乎不存在@Type):
@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")
}
)
Run Code Online (Sandbox Code Playgroud)
我有几点想法,但我不知道它们是否存在于JPA中:
Pas*_*ent 163
对于早于JPA 2.1的版本,JPA只提供两种方式来处理枚举,由它们name或由它们来处理ordinal.标准JPA不支持自定义类型.所以:
UserType,EclipseLink Converter等).(第二种解决方案).〜或〜int值〜或〜我将说明最新的选项(这是一个基本的实现,根据需要进行调整):
@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; }
public static Right parse(int id) {
Right right = null; // Default
for (Right item : Right.values()) {
if (item.getValue()==id) {
right = item;
break;
}
}
return right;
}
};
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
@Column(name = "RIGHT_ID")
private int rightId;
public Right getRight () {
return Right.parse(this.rightId);
}
public void setRight(Right right) {
this.rightId = right.getValue();
}
}
Run Code Online (Sandbox Code Playgroud)
Tva*_*roh 63
现在可以使用JPA 2.1:
@Column(name = "RIGHT")
@Enumerated(EnumType.STRING)
private Right right;
Run Code Online (Sandbox Code Playgroud)
更多详情:
Poo*_*ool 16
从JPA 2.1开始,您可以使用AttributeConverter.
像这样创建一个枚举类:
public enum NodeType {
ROOT("root-node"),
BRANCH("branch-node"),
LEAF("leaf-node");
private final String code;
private NodeType(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
Run Code Online (Sandbox Code Playgroud)
并创建一个像这样的转换器:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class NodeTypeConverter implements AttributeConverter<NodeType, String> {
@Override
public String convertToDatabaseColumn(NodeType nodeType) {
return nodeType.getCode();
}
@Override
public NodeType convertToEntityAttribute(String dbData) {
for (NodeType nodeType : NodeType.values()) {
if (nodeType.getCode().equals(dbData)) {
return nodeType;
}
}
throw new IllegalArgumentException("Unknown database value:" + dbData);
}
}
Run Code Online (Sandbox Code Playgroud)
在您需要的实体上:
@Column(name = "node_type_code")
Run Code Online (Sandbox Code Playgroud)
你的运气@Converter(autoApply = true)可能因容器而异,但经过测试可以在Wildfly 8.1.0上运行.如果它不起作用,您可以添加@Convert(converter = NodeTypeConverter.class)实体类列.
Chr*_*hie 16
最好的方法是将唯一ID映射到每个枚举类型,从而避免ORDINAL和STRING的陷阱.请参阅这篇文章,其中概述了映射枚举的5种方法.
取自上面的链接:
1&2.使用@Enumerated
目前有两种方法可以使用@Enumerated注释在JPA实体中映射枚举.不幸的是,EnumType.STRING和EnumType.ORDINAL都有其局限性.
如果使用EnumType.String,则重命名其中一个枚举类型将导致枚举值与数据库中保存的值不同步.如果使用EnumType.ORDINAL,则在枚举中删除或重新排序类型将导致数据库中保存的值映射到错误的枚举类型.
这两个选项都很脆弱.如果在不执行数据库迁移的情况下修改枚举,则可能会破坏数据的完整性.
3.生命周期回调
一种可能的解决方案是使用JPA生命周期回调注释,@ PrePersist和@PostLoad.这感觉非常难看,因为您现在在实体中有两个变量.一个映射存储在数据库中的值,另一个映射存储在实际的枚举中.
4.将唯一ID映射到每个枚举类型
首选解决方案是将枚举映射到枚举中定义的固定值或ID.映射到预定义的固定值使您的代码更加健壮.对枚举类型的顺序或名称的重构的任何修改都不会产生任何不利影响.
5.使用Java EE7 @Convert
如果您使用的是JPA 2.1,则可以选择使用新的@Convert注释.这需要创建一个使用@Converter注释的转换器类,您可以在其中定义为每个枚举类型保存到数据库中的值.在您的实体中,您将使用@Convert注释您的枚举.
我的偏好:( 4号)
我更喜欢在enum中定义我的ID而不是使用转换器的原因是良好的封装.只有枚举类型应该知道它的ID,并且只有实体应该知道它如何将枚举映射到数据库.
有关代码示例,请参阅原始帖子.
YoY*_*oYo 10
问题是,我认为,JPA从未接受过这样的想法,即我们可能已经有一个复杂的预先存在的Schema.
我认为这有两个主要的缺点,特别是Enum:
帮助我的事业并投票JPA_SPEC-47
这不比使用@Converter解决问题更优雅吗?
// Note: this code won't work!!
// it is just a sample of how I *would* want it to work!
@Enumerated
public enum Language {
ENGLISH_US("en-US"),
ENGLISH_BRITISH("en-BR"),
FRENCH("fr"),
FRENCH_CANADIAN("fr-CA");
@ID
private String code;
@Column(name="DESCRIPTION")
private String description;
Language(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
}
Run Code Online (Sandbox Code Playgroud)
我自己解决这种 Enum JPA 映射的解决方案如下。
步骤 1 - 编写以下接口,我们将用于所有要映射到 db 列的枚举:
public interface IDbValue<T extends java.io.Serializable> {
T getDbVal();
}
Run Code Online (Sandbox Code Playgroud)
第 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;
}
}
Run Code Online (Sandbox Code Playgroud)
此类将使用on enum将枚举值转换E为类型T(例如String)的数据库字段,反之亦然。getDbVal()E
步骤 3 - 让原始枚举实现我们在步骤 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;
}
}
Run Code Online (Sandbox Code Playgroud)
第 4 步-Right为第 3步的枚举扩展第 2 步的转换器:
public class RightConverter extends EnumDbValueConverter<Integer, Right> {
public RightConverter() {
super(Right.class);
}
}
Run Code Online (Sandbox Code Playgroud)
第 5 步- 最后一步是对实体中的字段进行注释,如下所示:
@Column(name = "RIGHT")
@Convert(converter = RightConverter.class)
private Right right;
Run Code Online (Sandbox Code Playgroud)
恕我直言,如果您有许多要映射的枚举并且想要使用枚举本身的特定字段作为映射值,那么这是最干净、最优雅的解决方案。
对于您项目中需要类似映射逻辑的所有其他枚举,您只需重复步骤 3 到 5,即:
IDbValue在您的枚举上实现接口;EnumDbValueConverter仅用 3 行代码扩展(您也可以在实体中执行此操作以避免创建单独的类);@Convertfromjavax.persistence包注释 enum 属性。希望这可以帮助。
| 归档时间: |
|
| 查看次数: |
124762 次 |
| 最近记录: |