JPA枚举序数与字符串

Posted

技术标签:

【中文标题】JPA枚举序数与字符串【英文标题】:JPA Enum ORDINAL vs STRING 【发布时间】:2011-10-10 23:40:52 【问题描述】:

可以在 JPA 中使用任一方法定义枚举

@Enumerated(EnumType.ORDINAL)

@Enumerated(EnumType.STRING)

我想知道这两种定义的优缺点是什么?

我听说 ORDINAL 使用 EclipseLink 比 STRING 性能更好(更快)。 这是真的吗?

【问题讨论】:

我认为 ORDINAL 默认是没有使用@Enumerated 注释 在 JPA 2.1 中,您可以指定 @Converter 并允许映射到字符串或其他数据类型,这些数据类型可以尽可能简单/短,也可以尽可能长/复杂。 【参考方案1】:

我总是去STRING

速度很少是最重要的问题 - 可读性和可维护性重要。

我使用STRING 是因为手动检查数据库中的行要容易得多,但更重要的是,我可以做两件事,而无需接触数据库,ORDINAL 无法处理:

    我可以更改枚举的顺序 我可以在枚举列表的中间插入新的枚举

这两项更改都会改变数据库中已使用的枚举的序数值,因此如果您使用ORDINAL,则会破坏现有数据。

如果你改变一个枚举值(不那么常见),处理它很简单:

UPDATE table SET enum_column = 'NEW_ENUM_NAME' where enum_column = 'OLD_ENUM_NAME';

【讨论】:

另一方面,你不能重命名你的枚举。 ;) 我不会说有一个首选。 确定可以:`UPDATE table SET enum_column = 'NEW_ENUM' where enum_column = 'OLD_ENUM' 重命名枚举与重命名表或表中的列一样具有灾难性,它具有长期而深远的非平凡影响,我认为这不是一个问题,因为像这样的重大变化将被仔细考虑。您更有可能添加新的枚举而不是删除或重命名它们,并且不必担心更改序数位置对于外部代码更为重要,在编译时无法检查,重命名可以。最终客户端代码不必关心您使用哪个,名称更易于维护。 “灾难性”这个词过于情绪化,无法描述重命名枚举 - 请参阅简单处理的答案。 @1in9ui5t 有趣的一点,尽管大多数数据库都有按枚举排序的方法。特别是,如果您也将列实现为 mysql 枚举,则按枚举顺序排序,如果是纯字符串,您可以order by find_in_set(mycolumn, 'ENUM_NAME_1', 'ENUM_NAME_2', ...)(有点难看,但足够简单)【参考方案2】:

ORDINAL 可能更有效,但这并不重要。 ORDINAL 有一些缺点:

在数据库中可读性较差 如果您对枚举定义重新排序,数据库将不一致。

使用STRING,您无法重命名枚举。

选择其中一个并在整个应用程序中使用它 - 保持一致。

如果您的数据库将被其他客户端/语言使用 - 使用 STRING,它更具可读性。

【讨论】:

【参考方案3】:

我更喜欢使用Ordinal,但这真的取决于使用情况

举例:

你有一个枚举,用来保存你所有的用户状态,在这种情况下顺序无关紧要,你可以在未来添加更多状态(最好使用@Enumerated(EnumType.ORDINAL)):

public enum UserStates  ACTIVE, DELETED, PENDING 

但是现在,您有一个枚举,可以拯救太阳系中的植物(最好使用 @Enumerated(EnumType.STRING)):

public enum Planets MERCURY,VENUS,EARTH,MARS,JUPITER,SATURN,URANUS,NEPTUNE,PLUTO,NINE

现在想你想重新排序你的行星,@Enumerated(EnumType.ORDINAL) 你不能,因为你的数据库不能知道你的 Java 文件中的新顺序。

您可以使用 @Enumerated(EnumType.STRING) 重新排序您的 Plantes,因为您的 Planet 链接到枚举名称,而不是枚举顺序。

无论如何,您可以修改您的@Enumerated(EnumType.ORDINAL)enums,因为它们与订单相关联,但您不能更改您的@Enumerated(EnumType.STRING)enums,因为它们将像新枚举一样使用。

字符串类型在数据库中更具可读性,但会比序数数据占用更多的大小。如果数据库被更多客户使用,也许会有用,但最好有一个好的软件文档,而不是保存 1000 倍于“EARTH”而不是“4”

USERSTATE
------------
ID | STATE |
------------
1 | 1
2 | 2
3 | 1

Planets
------------
ID | Name |
------------
1 | EARTH
2 | EARTH
3 | MARS
4 | EARTH

【讨论】:

【参考方案4】:

这是个好问题。过去我使用String,但今天我更喜欢使用Ordinal

String 的主要缺点是对 DBA 而言。对于字符串,他们不知道列的可能值是什么,因为此信息在应用程序代码中。 DBA 只能对将表上现有信息分组的可能值有一些想法,但在应用程序将它们插入表之前,他永远无法确定其他可能值。

在 Ordinal 中,您会遇到上述相同的问题。但我对 Ordinal 的偏爱是为了解决对数据库来说似乎很自然的 DBA 问题。您可以创建一个新表来显示数据库上 Enumerator 的可能值,在列(序数枚举值)和这个新表之间有一个外键。此策略在here 进行了描述和实施。

关于有人可能重新排序枚举器并破坏系统的问题,一个简单的单元测试可以解决这个问题,并保证没有人会在没有非常好的错误的情况下重新排序它们。同样的想法在重命名枚举器时也是有效的。因此,意外重命名(在 String 上)或重新排序(在 Ordinal 上)并不是反对 String 或 Ordinal 方法的有力论据。

顺便说一句,在我看来,开发人员更需要重命名而不是重新排序枚举器,所以我认为这是使用 Ordinal 的另一个积极点。

因此,使用这种额外的表方法,您解决了序数的主要问题(现在,可读)并且信息将占用更少的数据库空间(并且您的 DBA 会很高兴)。

【讨论】:

【参考方案5】:

这取决于您的应用程序,如果有更多机会添加更多枚举使用 String 类型,如果有更多机会更改枚举名称使用 Ordinal。

【讨论】:

【参考方案6】:

这里有很多好的建议,但我只是想补充一些我还没有看到的东西:

无论您选择哪种解决方案,都不要忘记在枚举类的顶部添加一个大警告,说明应该使用哪个。希望其他开发人员会看到您已完成此操作并使用相同的方法来保存枚举。

【讨论】:

【参考方案7】:

我更喜欢 EnumType.STRING。 EnumType.ORDINAL 的一个缺点是时间的影响和保持枚举按逻辑顺序的愿望。使用 EnumType.ORDINAL 任何新的枚举元素都必须添加到列表的末尾,否则您将不小心更改所有记录的含义。 请检查此链接: https://tomee.apache.org/examples-trunk/jpa-enumerated/

【讨论】:

【参考方案8】:

您确定需要人类可读的数据库吗? 存储字符串值是浪费空间。对可读性的唯一妥协可能是使用 @Enumerated(STRING) 并将数据库列映射为 ENUM(如果您使用的是 mysql ......我认为其他 dbms 也有类似的东西)但是当您必须更改枚举名称时,这真的很痛苦。

【讨论】:

(讽刺性评论警告)是的,必须通过代码才能找到序数值的实际含义要好得多......哦,不要忘记排序 - 当有人插入新值时很棒在枚举的中间/顶部。不要让实现决定设计。 @Enumerated(ORDINAL) 被认为是有害的 可维护性和可读性远比你说的重要。这样的东西(空间)在服务速度和内存消耗方面几乎没有任何成本。当您最终因为这些事情解决问题时,这会立即花费您(而不是空间)。 @earcam(讽刺回应)您可以随时在专栏上添加评论并在每次需要时阅读。这有那么难读吗?恕我直言,这张票没有回答这个问题,这两种情况都有其优点和缺点 @dwilda 哈哈,很公平。但可以肯定的是,基数 +(复合)索引可以缓解任何性能问题......mismatch 已经够糟糕了?

以上是关于JPA枚举序数与字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JPA 和 Hibernate 映射 PostgreSQL 枚举

org.hibernate.QueryException:JPA 风格的位置参数不是一个完整的序数

StringComparison 枚举的选项

使用枚举的序数是一种好习惯吗?

从整数值(序数)获取枚举

Java 枚举(enum)的用法