如何在 JPA 中使用枚举
我有一个电影租赁系统的现有数据库。 每部电影都有一个评级属性。 在 SQL 中,他们使用约束来限制该属性的允许值。
CONSTRAINT film_rating_check CHECK
((((((((rating)::text = ''::text) OR
((rating)::text = 'G'::text)) OR
((rating)::text = 'PG'::text)) OR
((rating)::text = 'PG-13'::text)) OR
((rating)::text = 'R'::text)) OR
((rating)::text = 'NC-17'::text)))
我认为使用 Java 枚举将约束映射到对象世界会很好。 但由于“PG-13”和“NC-17”中的特殊字符,不可能简单地取允许的值。 所以我实现了以下枚举:
public enum Rating {
UNRATED ( "" ),
G ( "G" ),
PG ( "PG" ),
PG13 ( "PG-13" ),
R ( "R" ),
NC17 ( "NC-17" );
private String rating;
private Rating(String rating) {
this.rating = rating;
}
@Override
public String toString() {
return rating;
}
}
@Entity
public class Film {
..
@Enumerated(EnumType.STRING)
private Rating rating;
..
使用 toString() 方法,方向枚举 -> 字符串工作正常,但字符串 -> 枚举不起作用。 我得到以下异常:
[TopLink警告]:2008.12.09 01:30:57.434--ServerSession(4729123)--异常 [TOPLINK-116](Oracle TopLink Essentials - 2.0.1(构建 b09d-fcs (12/06/2007)): oracle.toplink.essentials.exceptions.DescriptorException 异常 说明:没有为 [NC-17] 中的值提供转换值。 字段[电影评级]。 映射: oracle.toplink.essentials.mappings.DirectToFieldMapping[评级-->FILM.RATING] 描述符:RelationalDescriptor(de.fhw.nsdb.entities.Film --> [数据库表(FILM)])
欢呼
蒂莫
I have an existing database of a film rental system. Each film has a has a rating attribute. In SQL they used a constraint to limit the allowed values of this attribute.
CONSTRAINT film_rating_check CHECK
((((((((rating)::text = ''::text) OR
((rating)::text = 'G'::text)) OR
((rating)::text = 'PG'::text)) OR
((rating)::text = 'PG-13'::text)) OR
((rating)::text = 'R'::text)) OR
((rating)::text = 'NC-17'::text)))
I think it would be nice to use a Java enum to map the constraint into the object world. But it's not possible to simply take the allowed values because of the special char in "PG-13" and "NC-17". So I implemented the following enum:
public enum Rating {
UNRATED ( "" ),
G ( "G" ),
PG ( "PG" ),
PG13 ( "PG-13" ),
R ( "R" ),
NC17 ( "NC-17" );
private String rating;
private Rating(String rating) {
this.rating = rating;
}
@Override
public String toString() {
return rating;
}
}
@Entity
public class Film {
..
@Enumerated(EnumType.STRING)
private Rating rating;
..
With the toString() method the direction enum -> String works fine, but String -> enum does not work. I get the following exception:
[TopLink Warning]: 2008.12.09
01:30:57.434--ServerSession(4729123)--Exception [TOPLINK-116] (Oracle
TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))):
oracle.toplink.essentials.exceptions.DescriptorException Exception
Description: No conversion value provided for the value [NC-17] in
field [FILM.RATING]. Mapping:
oracle.toplink.essentials.mappings.DirectToFieldMapping[rating-->FILM.RATING]
Descriptor: RelationalDescriptor(de.fhw.nsdb.entities.Film -->
[DatabaseTable(FILM)])
cheers
timo
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
您是否尝试过存储序数值? 如果您没有与该值关联的字符串,则存储字符串值可以正常工作:
have you tried to store the ordinal value. Store the string value works fine if you don't have an associated String to the value:
这里有一个问题,那就是 JPA 在处理枚举时的能力有限。 对于枚举,您有两种选择:
Enum.name()
的字符串。 注意: 不是您所期望的toString()
,特别是因为Enum.toString()
的默认行为是返回name( )
。我个人认为最好的选择是(2)。
现在您遇到的问题是您定义的值不代表 Java 中的有效实例名称(即使用连字符)。 所以你的选择是:
我会按照优先顺序(从第一个到最后一个)执行它们。
有人建议使用 Oracle TopLink 转换器,但您可能正在使用 Toplink Essentials,它是参考 JPA 1.0 实现,它是商业 Oracle Toplink 产品的子集。
作为另一个建议,我强烈建议切换到 EclipseLink。 它是一个比 Toplink Essentials 更完整的实现,Eclipselink 将成为 JPA 2.0 发布时的参考实现(JavaOne 预计将于明年年中发布)。
You have a problem here and that is the limited capabilities of JPA when it comes to handling enums. With enums you have two choices:
Enum.ordinal()
, which is a terrible idea (imho); orEnum.name()
. Note: nottoString()
as you might expect, especially since the default behaviourforEnum.toString()
is to returnname()
.Personally I think the best option is (2).
Now you have a problem in that you're defining values that don't represent vailid instance names in Java (namely using a hyphen). So your choices are:
I would do them in that order (first to last) as an order of preference.
Someone suggested Oracle TopLink's converter but you're probably using Toplink Essentials, being the reference JPA 1.0 implementation, which is a subset of the commercial Oracle Toplink product.
As another suggestion, I'd strongly recommend switching to EclipseLink. It is a far more complete implementation than Toplink Essentials and Eclipselink will be the reference implementation of JPA 2.0 when released (expected by JavaOne mid next year).
听起来您需要添加对自定义类型的支持:
Extending OracleAS TopLink to支持自定义类型转换
Sounds like you need to add support for a custom type:
Extending OracleAS TopLink to Support Custom Type Conversions
然而,我不知道如何在带注释的 TopLink 方面进行映射。
I don't know how to do the mappings in the annotated TopLink side of things however.
我不知道 toplink 的内部结构,但我有根据的猜测如下:它使用 Rating.valueOf(String s) 方法在另一个方向进行映射。 不可能重写 valueOf(),因此您必须遵守 java 的命名约定,以允许正确的 valueOf 方法。
getRating 产生“人类可读”的评级。 请注意,枚举标识符中不允许使用“-”字符。
当然,您必须将这些值存储在 DB 中作为 NC_17。
i don't know internals of toplink, but my educated guess is the following: it uses the Rating.valueOf(String s) method to map in the other direction. it is not possible to override valueOf(), so you must stick to the naming convention of java, to allow a correct valueOf method.
getRating produces the "human-readable" rating. note that the "-" chanracter is not allowed in the enum identifier.
of course you will have to store the values in the DB as NC_17.
我认为,问题在于,JPA 从未考虑过我们可以拥有一个复杂的预先存在的架构。
我认为,对于 Enum 来说,有两个主要缺点:
帮助我的事业并对 JPA_SPEC-47 进行投票
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
使用您现有的
枚举评级
。 您可以使用AttributeCoverter
。Using your existing
enum Rating
. You can useAttributeCoverter
s.在 JPA 2.0 中,既不使用
name()
也不使用ordinal()
来持久化enum
的方法可以通过包装
。Embeddable
类中的 enum假设我们有以下
enum
,其中code
值要存储在数据库中:请注意,无法使用
code
值作为enum
的名称,因为它们包含点。 这句话证明了我们提供的解决方法是正确的。我们可以构建一个不可变类(作为值对象),使用静态方法
from()
包装枚举的code
值,以从enum< 构建它/code>,如下所示:
编写正确的
equals()
和hashcode()
对于确保此类的“值对象”目标非常重要。如果需要,可以添加 CourseType 和 ECourseType 之间的等价方法(但不能与 equals() 混合):
该类现在可以嵌入到实体类中:
请注意 setter
setType(ECourseType type)。 可以添加类似的 getter 来获取
type
作为ECourseType
。使用此建模,hibernate 生成(对于 H2 db)以下 SQL 表:
枚举的“code”值将存储在 COURSE_TYPE 中。
可以使用像这样简单的查询来搜索
Course
实体:结论:
这展示了如何既不使用
name
也不使用name
来持久化枚举ordinal
但确保依赖它的实体的干净建模。当 db 中存储的值不符合枚举名称和序号的 java 语法时,这对于遗留系统特别有用。
它还允许重构枚举名称,而无需更改数据库中的值。
In JPA 2.0, a way to persist an
enum
using neither thename()
norordinal()
can be done by wrapping theenum
in aEmbeddable
class.Assume we have the following
enum
, with acode
value intended to be stored in the database :Please note that the
code
values could not be used as names for theenum
since they contain dots. This remark justifies the workaround we are providing.We can build an immutable class (as a value object) wrapping the
code
value of the enum with a static methodfrom()
to build it from theenum
, like this :Writing proper
equals()
andhashcode()
is important to insure the "value object" aim of this class.If needed, an equivalence method between the CourseType et ECourseType may be added (but not mixed with equals()) :
This class can now be embedded in an entity class :
Please note that the setter
setType(ECourseType type)
has been added for convenience. A similar getter could be added to get thetype
asECourseType
.Using this modeling, hibernate generates (for H2 db) the following SQL table :
The "code" values of the enum will be stored in the COURSE_TYPE.
And the
Course
entities can be searched with a query as simple as this :Conclusion:
This shows how to persist an enum using neither the
name
nor theordinal
but insure a clean modelling of an entity relying on it.This is can be particularly useful for legacy when the values stored in db are not compliant to the java syntax of enum names and ordinals.
It also allows refactoring the enum names without having to change values in db.
对此,
您在数据库和实体上都将评级用作字符串,但对其他所有内容都使用枚举。
What about this
with this you use the ratings as String both on your DB and entity, but use the enum for everything else.
使用这个注释
use this annotation
枚举
公共枚举 ParentalControlLevelsEnum {
U(“U”),PG(“PG”),_12(“12”),_15(“15”),_18(“18”);
}
比较 -> 枚举
公共类RatingComparator 实现Comparator {
}
Enum
public enum ParentalControlLevelsEnum {
U("U"), PG("PG"), _12("12"), _15("15"), _18("18");
}
compare -> Enum
public class RatingComparator implements Comparator {
}
解决!!!
我在哪里找到了答案: http://programming.itags.org/development-tools/65254/
简而言之,转换查找枚举的名称,而不是属性“评级”的值。
在您的情况下:如果您的数据库值中有“NC-17”,则您的枚举中需要有:
enum Rating {
(...)
NC-17(“NC-17”);
(...)
Resolved!!!
Where I found the answer: http://programming.itags.org/development-tools/65254/
Briefly, the convertion looks for the name of enum, not the value of attribute 'rating'.
In your case: If you have in the db values "NC-17", you need to have in your enum:
enum Rating {
(...)
NC-17 ( "NC-17" );
(...)