Java 使用固定值映射JPA中的枚举?
我正在寻找使用JPA映射枚举的不同方法。我特别想设置每个枚举项的整数值,并且只保存整数值Java 使用固定值映射JPA中的枚举?,java,spring,orm,jpa,enums,Java,Spring,Orm,Jpa,Enums,我正在寻找使用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) { th
@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.ORDINAL一起使用:
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
但在本例中,JPA映射的是枚举索引(0,1,2),而不是我想要的值(100200300)
我发现的两个解决方案似乎并不简单
第一种解决方案
解决方案使用@PrePersist和@PostLoad将枚举转换为其他字段,并将枚举字段标记为瞬态:
@Basic
private int intValueForAnEnum;
@PrePersist
void populateDBFields() {
intValueForAnEnum = right.getValue();
}
@PostLoad
void populateTransientFields() {
right = Right.valueOf(intValueForAnEnum);
}
第二种解决方案
第二个解决方案提出了一个通用的转换对象,但看起来仍然很重并且面向hibernate(Java EE中似乎不存在@Type):
还有其他解决办法吗?
我有几个想法,但我不知道JPA中是否有:
- 加载和保存Authority对象时,请使用Authority类的右成员的setter和getter方法
- 一个等价的想法是告诉JPA什么是正确的enum方法来将enum转换为int和int转换为enum
- 因为我使用的是Spring,有没有办法告诉JPA使用特定的转换器(RightEditor)
名称,另一种是通过枚举的序号。而且标准JPA不支持自定义类型。因此:
- 如果要进行自定义类型转换,必须使用提供程序扩展(使用Hibernate
UserType
、EclipseLinkConverter
等)。(第二种解决办法)~或~
- 您必须使用@PrePersist和@PostLoad技巧(第一种解决方案)~或~
- 注释getter和setter获取并返回
int
value~或~
- 在实体级别使用整数属性,并在getter和setter中执行转换
我将说明最新的选项(这是一个基本的实现,根据需要进行调整):
可能是Pascal的密切相关代码
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR(300);
private Integer value;
private Right(Integer value) {
this.value = value;
}
// Reverse lookup Right for getting a Key from it's values
private static final Map<Integer, Right> lookup = new HashMap<Integer, Right>();
static {
for (Right item : Right.values())
lookup.put(item.getValue(), item);
}
public Integer getValue() {
return value;
}
public static Right getKey(Integer value) {
return lookup.get(value);
}
};
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
@Column(name = "RIGHT_ID")
private Integer rightId;
public Right getRight() {
return Right.getKey(this.rightId);
}
public void setRight(Right right) {
this.rightId = right.getValue();
}
}
@实体
@表(name=“AUTHORITY\”)
公共类权限实现了可序列化{
公共枚举权{
读(100)、写(200)、编辑(300);
私有整数值;
私权(整数值){
这个值=值;
}
//从其值获取密钥的反向查找权限
私有静态最终映射查找=新建HashMap();
静止的{
for(右项:Right.values())
lookup.put(item.getValue(),item);
}
公共整数getValue(){
返回值;
}
公共静态右getKey(整数值){
返回lookup.get(值);
}
};
@身份证
@GeneratedValue(策略=GenerationType.AUTO)
@列(name=“AUTHORITY\u ID”)
私人长id;
@列(name=“RIGHT\u ID”)
私有整数rightId;
公共权利{
返回Right.getKey(this.rightId);
}
公共无效设置权(右){
this.rightId=right.getValue();
}
}
问题是,我认为JPA从来没有想到我们可能已经有了一个复杂的预先存在的模式
我认为这导致了两个主要缺点,具体到Enum:
使用name()和ordinal()的限制。为什么不像对待@Entity那样,用@Id标记一个getter呢
Enum通常在数据库中具有表示形式,允许与各种元数据关联,包括专有名称、描述性名称,可能还有本地化名称等。我们需要Enum的易用性与实体的灵活性相结合
帮助我的事业并投票表决
这不比使用@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;
}
}
这在JPA 2.1中现在是可能的:
@Column(name = "RIGHT")
@Enumerated(EnumType.STRING)
private Right right;
进一步详情:
您可以使用JPA2.1
创建一个枚举类,如下所示:
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;
}
}
并创建一个如下所示的转换器:
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);
}
}
您使用的@Converter(autoApply=true)
可能因容器而异,但经过测试可以在Wildfly 8.1.0上工作。如果它不起作用,您可以在实体类列中添加@Convert(converter=NodeTypeConverter.class)
。我将执行以下操作:
在自己的文件中单独声明枚举:
public enum RightEnum {
READ(100), WRITE(200), EDITOR (300);
private int value;
private RightEnum (int value) { this.value = value; }
@Override
public static Etapa valueOf(Integer value){
for( RightEnum r : RightEnum .values() ){
if ( r.getValue().equals(value))
return r;
}
return null;//or throw exception
}
public int getValue() { return value; }
}
声明名为Right的新JPA实体
@Entity
public class Right{
@Id
private Integer id;
//FIElDS
// constructor
public Right(RightEnum rightEnum){
this.id = rightEnum.getValue();
}
public Right getInstance(RightEnum rightEnum){
return new Right(rightEnum);
}
}
您还需要一个转换器来接收这些值(仅限JPA 2.1,这里我不讨论使用转换器直接持久化这些枚举的问题,因此它将是单向的)
通过这种方式,您不能直接设置为enum字段。但是,您可以使用设置权限中的正确字段
autorithy.setRight( Right.getInstance( RightEnum.READ ) );//for example
如果需要比较,可以使用:
authority.getRight().equals( RightEnum.READ ); //for example
我觉得这很酷。它不是完全正确的,因为转换器不打算与enum一起使用。实际上,文档中说,如果不想使用它,应该使用@Enumerated注释。问题是只有两种枚举类型:序数或字符串,但序数比较复杂且不安全
然而,如果它不能让你满意,你可以做一些更简单的事情(或者不做)
让我们看看
右枚举:
public enum RightEnum {
READ(100), WRITE(200), EDITOR (300);
private int value;
private RightEnum (int value) {
try {
this.value= value;
final Field field = this.getClass().getSuperclass().getDeclaredField("ordinal");
field.setAccessible(true);
field.set(this, value);
} catch (Exception e) {//or use more multicatch if you use JDK 1.7+
throw new RuntimeException(e);
}
}
@Override
public static Etapa valueOf(Integer value){
for( RightEnum r : RightEnum .values() ){
if ( r.getValue().equals(value))
return r;
}
return null;//or throw exception
}
public int getValue() { return value; }
}
以及管理局实体
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
// the **Enum** to map (to be persisted or updated) :
@Column(name="COLUMN1")
@Enumerated(EnumType.ORDINAL)
private RightEnum rightEnum;
}
在第二个想法中,这不是一个完美的情况,因为我们破解了序数属性,但它是一个小得多的编码
我认为JPA规范应该包括EnumType.ID,其中enum value字段应该使用某种@EnumId注释进行注释。最好的方法是将一个唯一的ID映射到每个enum类型,从而避免序号和字符串的陷阱。请参见此部分,其中概述了映射枚举的5种方法
摘自上面的链接:
1和2。使用@Enumerated
目前有两种方法可以使用@E在JPA实体中映射枚举
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
// the **Entity** to map :
private Right right;
// the **Enum** to map (not to be persisted or updated) :
@Column(name="COLUMN1", insertable = false, updatable = false)
@Convert(converter = RightEnumConverter.class)
private RightEnum rightEnum;
}
autorithy.setRight( Right.getInstance( RightEnum.READ ) );//for example
authority.getRight().equals( RightEnum.READ ); //for example
public enum RightEnum {
READ(100), WRITE(200), EDITOR (300);
private int value;
private RightEnum (int value) {
try {
this.value= value;
final Field field = this.getClass().getSuperclass().getDeclaredField("ordinal");
field.setAccessible(true);
field.set(this, value);
} catch (Exception e) {//or use more multicatch if you use JDK 1.7+
throw new RuntimeException(e);
}
}
@Override
public static Etapa valueOf(Integer value){
for( RightEnum r : RightEnum .values() ){
if ( r.getValue().equals(value))
return r;
}
return null;//or throw exception
}
public int getValue() { return value; }
}
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
// the **Enum** to map (to be persisted or updated) :
@Column(name="COLUMN1")
@Enumerated(EnumType.ORDINAL)
private RightEnum rightEnum;
}