use*_*298 115 java database enums
将枚举保存到数据库的最佳方法是什么?
我知道Java提供name()和valueOf()方法来枚举值转换为字符串和背部.但是有没有其他(灵活的)选项来存储这些值?
有没有一种聪明的方法可以将枚举变成唯一的数字(ordinal()使用起来不安全)?
更新:
感谢所有令人敬畏和快速的答案!这是我怀疑的.
但请注意"工具包"; 这是一种方式.问题是我必须为我创建的每个Enum类型添加相同的方法.这是很多重复的代码,目前,Java不支持任何解决方案(Java枚举不能扩展其他类).
Ian*_*oyd 156
我们永远不会将枚举存储为数字序数值; 它使调试和支持方式太难了.我们存储转换为字符串的实际枚举值:
public enum Suit { Spade, Heart, Diamond, Club }
Suit theSuit = Suit.Heart;
szQuery = "INSERT INTO Customers (Name, Suit) " +
"VALUES ('Ian Boyd', %s)".format(theSuit.name());
Run Code Online (Sandbox Code Playgroud)
然后回读:
Suit theSuit = Suit.valueOf(reader["Suit"]);
Run Code Online (Sandbox Code Playgroud)
问题是过去盯着企业管理器并试图破译:
Name Suit
================== ==========
Shelby Jackson 2
Ian Boyd 1
Run Code Online (Sandbox Code Playgroud)
经文
Name Suit
================== ==========
Shelby Jackson Diamond
Ian Boyd Heart
Run Code Online (Sandbox Code Playgroud)
后者更容易.前者需要获取源代码并查找分配给枚举成员的数值.
是的,它需要更多空间,但枚举成员名称很短,硬盘驱动器很便宜,当你遇到问题时,它更值得帮助.
此外,如果您使用数值,则与它们相关联.您无法在不必强制使用旧数值的情况下很好地插入或重新排列成员.例如,将Suit枚举更改为:
public enum Suit { Unknown, Heart, Club, Diamond, Spade }
Run Code Online (Sandbox Code Playgroud)
必须成为:
public enum Suit {
Unknown = 4,
Heart = 1,
Club = 3,
Diamond = 2,
Spade = 0 }
Run Code Online (Sandbox Code Playgroud)
为了保持存储在数据库中的遗留数值.
问题出现了:假设我想订购价值.有些人可能希望按枚举的序数值对它们进行排序.当然,按枚举的数值排序卡片是没有意义的:
SELECT Suit FROM Cards
ORDER BY SuitID; --where SuitID is integer value(4,1,3,2,0)
Suit
------
Spade
Heart
Diamond
Club
Unknown
Run Code Online (Sandbox Code Playgroud)
这不是我们想要的顺序 - 我们希望它们按枚举顺序排列:
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
Run Code Online (Sandbox Code Playgroud)
如果保存字符串,则需要保存整数值所需的相同工作:
SELECT Suit FROM Cards
ORDER BY Suit; --where Suit is an enum name
Suit
-------
Club
Diamond
Heart
Spade
Unknown
Run Code Online (Sandbox Code Playgroud)
但这不是我们想要的顺序 - 我们希望它们按枚举顺序排列:
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
Run Code Online (Sandbox Code Playgroud)
我的观点是这种排名属于用户界面.如果您根据枚举值对项目进行排序:您做错了什么.
但是如果你想真的这样做,我会创建一个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 |
Run Code Online (Sandbox Code Playgroud)
这样,当您想要更改卡片以使用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 |
Run Code Online (Sandbox Code Playgroud)
现在我们将内部编程细节(枚举名称,枚举值)与用户的显示设置分开:
SELECT Cards.Suit
FROM Cards
INNER JOIN Suits ON Cards.Suit = Suits.Suit
ORDER BY Suits.Rank,
Card.Rank*Suits.CardOrder
Run Code Online (Sandbox Code Playgroud)
Tom*_*Tom 39
除非您有特定的性能原因要避免它,否则我建议使用单独的表进行枚举.使用外键完整性,除非额外的查找真的杀了你.
suit_id suit_name
1 Clubs
2 Hearts
3 Spades
4 Diamonds
Run Code Online (Sandbox Code Playgroud)
player_name suit_id
Ian Boyd 4
Shelby Lake 2
Run Code Online (Sandbox Code Playgroud)
suit_id)与枚举值无关,这有助于您处理其他语言的数据.小智 8
我遇到了同样的问题,我的目标是将枚举字符串值而不是序数值保留到数据库中。
为了解决这个问题,我已经使用@Enumerated(EnumType.STRING)并且我的目标得到了解决。
例如,您有一个Enum班级:
public enum FurthitMethod {
Apple,
Orange,
Lemon
}
Run Code Online (Sandbox Code Playgroud)
在实体类中,定义@Enumerated(EnumType.STRING):
@Enumerated(EnumType.STRING)
@Column(name = "Fruits")
public FurthitMethod getFuritMethod() {
return fruitMethod;
}
public void setFruitMethod(FurthitMethod authenticationMethod) {
this.fruitMethod= fruitMethod;
}
Run Code Online (Sandbox Code Playgroud)
当您尝试将值设置为数据库时,字符串值将作为“ APPLE”、“ ORANGE”或“ LEMON”保存到数据库中。
我认为这里唯一安全的机制是使用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());
}
}
Run Code Online (Sandbox Code Playgroud)
如果您将其存储为序数,则可能包含以下行:
> SELECT STATEMENT, TRUTH FROM CALL_MY_BLUFF
"Alice is a boy" 1
"Graham is a boy" 0
Run Code Online (Sandbox Code Playgroud)
但是如果更新布尔会发生什么?
public enum Boolean {
TRUE, FILE_NOT_FOUND, FALSE
}
Run Code Online (Sandbox Code Playgroud)
这意味着你所有的谎言都会被误解为"未找到文件"
最好只使用字符串表示
我们只存储枚举名称本身。它更具可读性。
我们确实向枚举添加了一个附加属性,其中枚举具有一组有限的值。例如,在下面的枚举中,我们使用char属性来表示数据库中的枚举值(achar比数字值更有意义):
public enum EmailStatus {
EMAIL_NEW('N'), EMAIL_SENT('S'), EMAIL_FAILED('F'), EMAIL_SKIPPED('K'), UNDEFINED('-');
private char dbChar = '-';
EmailStatus(char statusChar) {
this.dbChar = statusChar;
}
public char statusChar() {
return dbChar;
}
public static EmailStatus getFromStatusChar(char statusChar) {
switch (statusChar) {
case 'N':
return EMAIL_NEW;
case 'S':
return EMAIL_SENT;
case 'F':
return EMAIL_FAILED;
case 'K':
return EMAIL_SKIPPED;
default:
return UNDEFINED;
}
}
}
Run Code Online (Sandbox Code Playgroud)
当您有很多值时,您可以在Map枚举内部使用一个枚举来保持该getFromXYZ方法较小。
小智 5
对于大型数据库,我不愿意失去数字表示的大小和速度优势。我经常会得到一个代表枚举的数据库表。
您可以通过声明外键来强制数据库一致性 - 尽管在某些情况下最好不要将其声明为外键约束,因为这会给每个事务带来成本。您可以在您选择的时间定期进行检查,以确保一致性:
SELECT reftable.* FROM reftable
LEFT JOIN enumtable ON reftable.enum_ref_id = enumtable.enum_id
WHERE enumtable.enum_id IS NULL;
Run Code Online (Sandbox Code Playgroud)
该解决方案的另一半是编写一些测试代码来检查 Java 枚举和数据库枚举表是否具有相同的内容。这留给读者作为练习。