可以使用以下任一方法在JPA中定义枚举
@Enumerated(EnumType.ORDINAL)
要么
@Enumerated(EnumType.STRING)
我想知道这两个定义的优缺点是什么?
我听说ORDINAL的性能比EclipseLink的STRING更好(更快)。
真的吗?
我认为ORDINAL默认情况下不使用@Enumerated批注
在JPA 2.1中,您可以指定@Converter并允许映射到字符串或其他数据类型,该类型可以是简单/简短,也可以是长/复杂。
我总是去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其中enum_column = OLD_ENUM
重命名Enums就像重命名表或表中的列一样会带来灾难性的后果,它已经产生了长远的不平凡的影响,我不认为这是个问题,因为这样的大变化将被仔细考虑。您更有可能添加新的枚举而不删除或重命名它们,而不必担心更改序数位置对于外部代码更为重要,因为外部代码在编译时无法检查,重命名可以。最后,客户端代码不必关心您使用哪个代码,名称更易于维护。
如果有人不知道该数据库中使用了枚举名称,则不会。我会说我重命名枚举的次数要比重新排序的次数多。而且您也可以使用常规的UPDATE查询
"灾难性"这个词太令人动容,无法描述重命名枚举-有关简单处理,请参见答案。
如何在数据库和代码中同时更新enum名称?
@Sangdol将是一个释放任务:重命名枚举后,在释放代码时,运行sql脚本以更新所使用的枚举名称:update mytable set myenumcol = newname where myenumcol = oldname
Ordinal的优点是始终使用@OrderBy,而使用String时,排序是按字母顺序进行的(这里我是说db SELECT语句)。
@ 1in9ui5t有趣的一点是,尽管大多数数据库都有按枚举排序的方式。特别是,如果您也将该列也实现为mysql枚举,则按枚举顺序排序,如果可以使用纯字符串,则可以order by find_in_set(mycolumn, ENUM_NAME_1, ENUM_NAME_2, ...)(有点难看,但足够简单)
@Bohemian我不知道数据库中的ENUM类型。这样就解决了订购问题。只需在列定义中包括Enum值即可。
ORDINAL可能更有效,但这是次要的。 ORDINAL有一些缺点:
它在数据库中不太可读
如果您对枚举定义重新排序,则数据库将不一致。
使用STRING,您无法重命名枚举。
选择其中之一并在整个应用程序中使用它-保持一致。
如果您的数据库将被其他客户端/语言使用-使用STRING,它更具可读性。
我更喜欢使用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.STRING)枚举,因为它们已链接到订单,但是您无法更改@Enumerated(EnumType.STRING)枚举,因为它们将像新枚举一样使用。
字符串类型在数据库中更具可读性,但比序数数据占用更多的大小。如果数据库被更多的客户端使用,可能会很有用,但是最好是拥有一个良好的软件文档,而不是将" EARTH"保存为" 4"比保存1000倍。
USERSTATE
------------
ID | STATE |
------------
1 | 1
2 | 2
3 | 1
Planets
------------
ID | Name |
------------
1 | EARTH
2 | EARTH
3 | MARS
4 | EARTH
这是一个很好的问题。过去我使用String,但今天我的偏好通常是Ordinal。
字符串的主要缺点是对于DBA。使用String时,他们不知道列的可能值是多少,因为此信息在应用程序代码中。 DBA只能对将表上的现有信息分组的可能值有所了解,但是他永远不会确定其他可能的值或是否添加了新值,直到应用程序将它们插入表中为止。
在Ordinal中,您有同样的问题。但是我更喜欢Ordinal来解决数据库似乎很自然的DBA问题。您创建一个新表以在数据库上显示枚举数的可能值,并在列(常规枚举值)和此新表之间使用外键。在此描述和实施此策略。
关于有人可以对Enumerator进行重新排序并破坏系统的问题,一个简单的单元测试可以解决此问题,并确保没有人在没有良好警告的情况下对其进行重新排序。在重命名枚举器时,同样的想法是有效的。因此,意外地重命名(在String上)或重新排序(在Ordinal上)并不是针对String或Ordinal方法的强烈反对。
顺便说一句,开发人员比重命名Enumerator更需要重命名,因此这是使用Ordinal的另一个积极方面。
因此,使用这种方法,您可以解决Ordinal的主要问题(现在已经可以读取)了,该信息将在数据库上占用更少的空间,并且DBA会很高兴。
我更喜欢EnumType.STRING。
EnumType.ORDINAL的一个缺点是时间的影响以及保持枚举逻辑顺序的愿望。使用EnumType.ORDINAL必须将任何新的枚举元素添加到列表的末尾,否则您将不小心更改所有记录的含义。
请检查此链接:
https://tomee.apache.org/examples-trunk/jpa-enumerated/
这里有很多好的建议,但是我只想添加一些我尚未看到的东西:
无论您选择哪种解决方案,都不要忘记在枚举类的顶部添加一个大的警告提示,指出应该使用哪种警告。希望其他开发人员可以看到您已经完成此操作,并使用相同的方法来保存枚举。
这取决于您的应用程序,是否有更多的机会使用String类型添加更多的枚举,是否有更多的机会使用Ordinal更改枚举的名称。
您真的确定需要人类可读的数据库吗?
存储字符串值是浪费空间。可读性的唯一折衷办法是使用@Enumerated(STRING)并将map数据库列作为ENUM使用(如果您使用的是mysql ...我想其他dbms也有类似的用法),但是当您必须更改枚举名称时,这确实很痛苦。
(讽刺评论警告)是的,这样更好,因为必须仔细检查代码才能找到该序数值的实际含义。...哦,别忘了排序-当有人在枚举的中间/顶部插入一个新值时,效果很好。不要让实现决定设计。 @Enumerated(ORDINAL)被认为是有害的
可维护性和可读性比您所说的重要得多。这些东西(空间)在服务速度和内存消耗方面几乎完全没有成本。当您最终由于此类问题而解决问题时,这会立即使您付出代价(比空间大)。
@earcam(讽刺的回答),您随时可以在列上添加评论,并在需要时随时阅读。这难读吗?恕我直言,这张票没有回答这个问题,两种情况各有利弊
@dwilda哈哈,很公平。但是可以肯定的是,基数+(复合)索引可以缓解任何性能问题...不匹配已经很严重了?