将枚举保存到数据库的最佳方法是什么?
我知道Java提供name()
和valueOf()
方法来枚举值转换为字符串和背部.但是有没有其他(灵活的)选项来存储这些值?
有没有一种聪明的方法可以将枚举变成唯一的数字(ordinal()
使用起来不安全)?
更新:
感谢所有令人敬畏和快速的答案!这是我怀疑的.
但请注意"工具包"; 这是一种方式.问题是我必须为我创建的每个Enum类型添加相同的方法.这是很多重复的代码,目前,Java不支持任何解决方案(Java枚举不能扩展其他类).
我们永远不会将枚举存储为数字序数值; 它使调试和支持方式太难了.我们存储转换为字符串的实际枚举值:
public enum Suit { Spade, Heart, Diamond, Club } Suit theSuit = Suit.Heart; szQuery = "INSERT INTO Customers (Name, Suit) " + "VALUES ('Ian Boyd', %s)".format(theSuit.name());
然后回读:
Suit theSuit = Suit.valueOf(reader["Suit"]);
问题是过去盯着企业管理器并试图破译:
Name Suit ================== ========== Shelby Jackson 2 Ian Boyd 1
经文
Name Suit ================== ========== Shelby Jackson Diamond Ian Boyd Heart
后者更容易.前者需要获取源代码并查找分配给枚举成员的数值.
是的,它需要更多空间,但枚举成员名称很短,硬盘驱动器很便宜,当你遇到问题时,它更值得帮助.
此外,如果您使用数值,则与它们相关联.您无法在不必强制使用旧数值的情况下很好地插入或重新排列成员.例如,将Suit枚举更改为:
public enum Suit { Unknown, Heart, Club, Diamond, Spade }
必须成为:
public enum Suit { Unknown = 4, Heart = 1, Club = 3, Diamond = 2, Spade = 0 }
为了保持存储在数据库中的遗留数值.
如何在数据库中对它们进行排序问题出现了:假设我想订购价值.有些人可能希望按枚举的序数值对它们进行排序.当然,按枚举的数值排序卡片是没有意义的:
SELECT Suit FROM Cards ORDER BY SuitID; --where SuitID is integer value(4,1,3,2,0) Suit ------ Spade Heart Diamond Club Unknown
这不是我们想要的顺序 - 我们希望它们按枚举顺序排列:
SELECT Suit FROM Cards ORDER BY CASE SuitID OF WHEN 4 THEN 0 --Unknown first WHEN 1 THEN 1 --Heart WHEN 3 THEN 2 --Club WHEN 2 THEN 3 --Diamond WHEN 0 THEN 4 --Spade ELSE 999 END
如果保存字符串,则需要保存整数值所需的相同工作:
SELECT Suit FROM Cards ORDER BY Suit; --where Suit is an enum name Suit ------- Club Diamond Heart Spade Unknown
但这不是我们想要的顺序 - 我们希望它们按枚举顺序排列:
SELECT Suit FROM Cards ORDER BY CASE Suit OF WHEN 'Unknown' THEN 0 WHEN 'Heart' THEN 1 WHEN 'Club' THEN 2 WHEN 'Diamond' THEN 3 WHEN 'Space' THEN 4 ELSE 999 END
我的观点是这种排名属于用户界面.如果您根据枚举值对项目进行排序:您做错了什么.
但是如果你想真的这样做,我会创建一个Suits
维度表:
| Suit | SuitID | Rank | Color | |------------|--------------|---------------|--------| | Unknown | 4 | 0 | NULL | | Heart | 1 | 1 | Red | | Club | 3 | 2 | Black | | Diamond | 2 | 3 | Red | | Spade | 0 | 4 | Black |
这样,当您想要更改卡片以使用Kissing Kings New Deck Order时,您可以更改它以用于显示目的而不丢弃所有数据:
| Suit | SuitID | Rank | Color | CardOrder | |------------|--------------|---------------|--------|-----------| | Unknown | 4 | 0 | NULL | NULL | | Spade | 0 | 1 | Black | 1 | | Diamond | 2 | 2 | Red | 1 | | Club | 3 | 3 | Black | -1 | | Heart | 1 | 4 | Red | -1 |
现在我们将内部编程细节(枚举名称,枚举值)与用户的显示设置分开:
SELECT Cards.Suit FROM Cards INNER JOIN Suits ON Cards.Suit = Suits.Suit ORDER BY Suits.Rank, Card.Rank*Suits.CardOrder
除非您有特定的性能原因要避免它,否则我建议使用单独的表进行枚举.使用外键完整性,除非额外的查找真的杀了你.
suit_id suit_name 1 Clubs 2 Hearts 3 Spades 4 Diamonds
player_name suit_id Ian Boyd 4 Shelby Lake 2
如果您将枚举重构为具有行为的类(例如优先级),则您的数据库已经正确地对其进行了建模
您的DBA很高兴,因为您的架构已规范化(每个玩家存储一个整数,而不是整个字符串,可能有也可能没有拼写错误).
您的数据库值(suit_id
)与枚举值无关,这有助于您处理其他语言的数据.
我认为这里唯一安全的机制是使用String name()
值.写入数据库时,可以使用sproc插入值,在读取时使用View.以这种方式,如果枚举改变,则在sproc/view中存在一个间接级别,以便能够将数据作为枚举值呈现而不将"强加"在DB上.
正如你所说,序数有点冒险.考虑例如:
public enum Boolean { TRUE, FALSE } public class BooleanTest { @Test public void testEnum() { assertEquals(0, Boolean.TRUE.ordinal()); assertEquals(1, Boolean.FALSE.ordinal()); } }
如果您将其存储为序数,则可能包含以下行:
> SELECT STATEMENT, TRUTH FROM CALL_MY_BLUFF "Alice is a boy" 1 "Graham is a boy" 0
但是如果更新布尔会发生什么?
public enum Boolean { TRUE, FILE_NOT_FOUND, FALSE }
这意味着你所有的谎言都会被误解为"未找到文件"
最好只使用字符串表示