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;
}
一个简单的解决方案是将枚举注释与 EnumType.ORDINAL 结合使用:
@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 类右侧成员的 setter 和 getter 方法,
- 一个等效的想法是告诉 JPA 这些方法是什么Right enum 将 enum 转换为 int 和 int 转换为 enum
- 因为我正在使用 Spring,有什么方法可以告诉 JPA 使用特定的转换器(RightEditor)?
I'm looking for the different ways to map an enum using JPA. I especially want to set the integer value of each enum entry and to save only the integer value.
@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;
}
A simple solution is to use the Enumerated annotation with EnumType.ORDINAL:
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
But in this case JPA maps the enum index (0,1,2) and not the value I want (100,200,300).
Th two solutions I found do not seem simple...
First Solution
A solution, proposed here, uses @PrePersist and @PostLoad to convert the enum to an other field and mark the enum field as transient:
@Basic
private int intValueForAnEnum;
@PrePersist
void populateDBFields() {
intValueForAnEnum = right.getValue();
}
@PostLoad
void populateTransientFields() {
right = Right.valueOf(intValueForAnEnum);
}
Second Solution
The second solution proposed here proposed a generic conversion object, but still seems heavy and hibernate-oriented (@Type doesn't seem to exist in 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")
}
)
Is there any other solutions ?
I've several ideas in mind but I don't know if they exist in JPA:
- use the setter and getter methods of right member of Authority Class when loading and saving the Authority object
- an equivalent idea would be to tell JPA what are the methods of Right enum to convert enum to int and int to enum
- Because I'm using Spring, is there any way to tell JPA to use a specific converter (RightEditor) ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
对于 JPA 2.1 之前的版本,JPA 仅提供两种处理枚举的方法,即按
名称
或按序号
。并且标准JPA不支持自定义类型。因此:UserType
、EclipseLinkConverter
等)。 (第二种解决方案)。 〜或〜int
值 ~或~我将说明最新的选项(这是一个基本实现,根据需要进行调整):
For versions earlier than JPA 2.1, JPA provides only two ways to deal with enums, by their
name
or by theirordinal
. And the standard JPA doesn't support custom types. So:UserType
, EclipseLinkConverter
, etc). (the second solution). ~or~int
value ~or~I'll illustrate the latest option (this is a basic implementation, tweak it as required):
现在 JPA 2.1 可以实现这一点:
更多详细信息:
This is now possible with JPA 2.1:
Further details:
从 JPA 2.1 开始,您可以使用 AttributeConverter。
创建一个像这样的枚举类:
并创建一个像这样的转换器:
在您只需要的实体上:
@Converter(autoApply = true)
的运气可能因容器而异,但经过测试可在 Wildfly 8.1 上工作。 0。如果它不起作用,您可以在实体类列上添加@Convert(converter = NodeTypeConverter.class)
。From JPA 2.1 you can use AttributeConverter.
Create an enumerated class like so:
And create a converter like this:
On the entity you just need:
You luck with
@Converter(autoApply = true)
may vary by container but tested to work on Wildfly 8.1.0. If it doesn't work you can add@Convert(converter = NodeTypeConverter.class)
on the entity class column.最好的方法是将唯一的 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)
我更喜欢在枚举中定义 ID 而不是使用转换器,原因是良好的封装。只有枚举类型应该知道它的 ID,并且只有实体应该知道它如何将枚举映射到数据库。
请参阅原始帖子 代码示例。
The best approach would be to map a unique ID to each enum type, thus avoiding the pitfalls of ORDINAL and STRING. See this post which outlines 5 ways you can map an enum.
Taken from the link above:
1&2. Using @Enumerated
There are currently 2 ways you can map enums within your JPA entities using the @Enumerated annotation. Unfortunately both EnumType.STRING and EnumType.ORDINAL have their limitations.
If you use EnumType.String then renaming one of your enum types will cause your enum value to be out of sync with the values saved in the database. If you use EnumType.ORDINAL then deleting or reordering the types within your enum will cause the values saved in the database to map to the wrong enums types.
Both of these options are fragile. If the enum is modified without performing a database migration, you could jeopodise the integrity of your data.
3. Lifecycle Callbacks
A possible solution would to use the JPA lifecycle call back annotations, @PrePersist and @PostLoad. This feels quite ugly as you will now have two variables in your entity. One mapping the value stored in the database, and the other, the actual enum.
4. Mapping unique ID to each enum type
The preferred solution is to map your enum to a fixed value, or ID, defined within the enum. Mapping to predefined, fixed value makes your code more robust. Any modification to the order of the enums types, or the refactoring of the names, will not cause any adverse effects.
5. Using Java EE7 @Convert
If you are using JPA 2.1 you have the option to use the new @Convert annotation. This requires the creation of a converter class, annotated with @Converter, inside which you would define what values are saved into the database for each enum type. Within your entity you would then annotate your enum with @Convert.
My preference: (Number 4)
The reason why I prefer to define my ID's within the enum as oppose to using a converter, is good encapsulation. Only the enum type should know of its ID, and only the entity should know about how it maps the enum to the database.
See the original post for the code example.
我认为,问题在于,JPA 诞生之初就没有考虑到我们可以拥有一个复杂的预先存在的架构。
我认为由此产生的两个主要缺点,特别是 Enum:
帮助我的事业并投票 JPA_SPEC-47
这不是比使用@Converter 解决问题吗?
The problem is, I think, that JPA was never incepted with the idea in mind that we could have a complex preexisting Schema already in place.
I think there are two main shortcomings resulting from this, specific to Enum:
Help my cause and vote on JPA_SPEC-47
Would this not be more elegant than using a @Converter to solve the problem?
我自己解决这种 Enum JPA 映射的解决方案如下。
第 1 步 - 编写以下接口,我们将使用该接口来映射到数据库列的所有枚举:
第 2 步 - 实现自定义通用 JPA 转换器,如下所示:
此类将使用
getDbVal()< 将枚举值
E
转换为T
类型的数据库字段(例如String
) /code> 位于枚举E
上,反之亦然。第 3 步 - 让原始枚举实现我们在第 1 步中定义的接口:
第 4 步 - 为
Right
枚举扩展第 2 步的转换器步骤 3 的步骤:步骤 5 - 最后一步是对实体中的字段进行注释,如下所示:
结论
恕我直言,如果您有许多要映射的枚举并且想要使用,这是最干净、最优雅的解决方案枚举本身的特定字段作为映射值。
对于项目中需要类似映射逻辑的所有其他枚举,您只需重复步骤 3 到 5,即:
IDbValue
;EnumDbValueConverter
(您也可以在实体中执行此操作以避免创建单独的类);javax.persistence
包中的@Convert
注释枚举属性。希望这有帮助。
My own solution to solve this kind of Enum JPA mapping is the following.
Step 1 - Write the following interface that we will use for all enums that we want to map to a db column:
Step 2 - Implement a custom generic JPA converter as follows:
This class will convert the enum value
E
to a database field of typeT
(e.g.String
) by using thegetDbVal()
on enumE
, and vice versa.Step 3 - Let the original enum implement the interface we defined in step 1:
Step 4 - Extend the converter of step 2 for the
Right
enum of step 3:Step 5 - The final step is to annotate the field in the entity as follows:
Conclusion
IMHO this is the cleanest and most elegant solution if you have many enums to map and you want to use a particular field of the enum itself as mapping value.
For all others enums in your project that need similar mapping logic, you only have to repeat steps 3 to 5, that is:
IDbValue
on your enum;EnumDbValueConverter
with only 3 lines of code (you may also do this within your entity to avoid creating a separated class);@Convert
fromjavax.persistence
package.Hope this helps.
可能与 Pascal 密切相关的代码
Possibly close related code of Pascal
我会执行以下操作:
在它自己的文件中单独声明枚举:
声明一个名为 Right 的新 JPA 实体
您还需要一个转换器来接收此值(仅限 JPA 2.1,有一个问题我不会在这里讨论)这些枚举将使用转换器直接持久化,因此这只是一条单向道路)
权威实体:
通过这种方式,您无法直接设置到枚举字段。但是,您可以使用以下方式在 Authority 中设置 Right 字段
如果您需要比较,您可以使用:
我认为这非常酷。它并不完全正确,因为转换器不打算与枚举一起使用。实际上,文档说永远不要将其用于此目的,您应该使用 @Enumerated 注释。问题是只有两种枚举类型:ORDINAL 或 STRING,但 ORDINAL 很棘手且不安全。
但是,如果它不能满足您,您可以做一些更古怪和更简单的事情(或不做)。
让我们来看看。
RightEnum:
和 Authority 实体
在第二个想法中,这不是一个完美的情况,因为我们破解了序数属性,但它的编码要小得多。
我认为 JPA 规范应该包括 EnumType.ID,其中枚举值字段应该用某种 @EnumId 注释进行注释。
I would do the folowing:
Declare separetly the enum, in it´s own file:
Declare a new JPA entity named Right
You will also need a converter for receiving this values (JPA 2.1 only and there´s a problem I´ll not discuss here with these enum´s to be directly persisted using the converter, so it will be a one way road only)
The Authority entity:
By doing this way, you can´t set directly to the enum field. However, you can set the Right field in Authority using
And if you need to compare, you can use:
Which is pretty cool, I think. It´s not totally correct, since the converter it´s not intended to be use with enum´s. Actually, the documentation says to never use it for this purpose, you should use the @Enumerated annotation instead. The problem is that there are only two enum types: ORDINAL or STRING, but the ORDINAL is tricky and not safe.
However, if it doesn´t satisfy you, you can do something a little more hacky and simpler (or not).
Let´s see.
The RightEnum:
and the Authority entity
In this second idea, its not a perfect situation since we hack the ordinal attribute, but it´s a much smaller coding.
I think that the JPA specification should include the EnumType.ID where the enum value field should be annotated with some kind of @EnumId annotation.