问题
将枚举保存到数据库的最佳方法是什么?
我知道Java提供了name()
和valueOf()
方法来将枚举值转换为String并返回。但是有没有其他(灵活的)选项来存储这些值?
有没有一种聪明的方法可以将枚举变成唯一的数字(ordinal()
使用起来不安全)?
更新:
感谢所有令人敬畏和快速的答案!这是我怀疑的。
不过要注意'工具包';这是一种方式。问题是我必须为我创建的每个枚举类型添加相同的方法。这是很多重复的代码,目前,Java不支持任何解决方案(Java枚举不能扩展其他类)。
#1 热门回答(141 赞)
Weneverstore枚举作为数字序数值了;它使调试和支持方式太难了。我们存储转换为字符串的实际枚举值:
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 }
为了保持存储在数据库中的遗留数值。
#如何在数据库中对它们进行排序
问题出现了:让我说我想订购价值。有些人可能想按enum的序数值对它们进行排序。当然,按照枚举的数值排序卡片是没有意义的:
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 KingsNew Deck Orderyou可以更改它以用于显示目的而不丢弃所有数据:
| 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
#2 热门回答(35 赞)
除非你有特定的性能原因要避免它,否则我建议使用单独的表进行枚举。使用外键完整性,除非额外的查找真的杀了你。
###西装表:
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)与枚举值无关,这有助于你处理来自其他语言的数据。
#3 热门回答(5 赞)
我认为这里唯一安全的机制是使用Stringname()
value。写入数据库时,你可以使用sproc来插入值,并在读取时使用View。以这种方式,如果枚举改变,则在sproc / view中存在一个间接级别,以便能够将数据作为枚举值呈现而不将其"强加"在DB上。