当前位置:  开发笔记 > 编程语言 > 正文

如何在数据库中保存枚举

如何解决《如何在数据库中保存枚举》经验,为你挑选了4个好方法。

将枚举保存到数据库的最佳方法是什么?

我知道Java提供name()valueOf()方法来枚举值转换为字符串和背部.但是有没有其他(灵活的)选项来存储这些值?

有没有一种聪明的方法可以将枚举变成唯一的数字(ordinal()使用起来不安全)?

更新:

感谢所有令人敬畏和快速的答案!这是我怀疑的.

但请注意"工具包"; 这是一种方式.问题是我必须为我创建的每个Enum类型添加相同的方法.这是很多重复的代码,目前,Java不支持任何解决方案(Java枚举不能扩展其他类).



1> Ian Boyd..:

我们永远不会将枚举存储为数字序数值; 它使调试和支持方式太难了.我们存储转换为字符串的实际枚举值:

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


通常会覆盖toString以提供显示值.name()是一个更好的选择,因为它定义为valueOf()的对应物
我强烈不同意这一点,如果需要枚举持久性,那么就不应该坚持名字.只要读回去它就更简单了,而不是名称可以将它强制转换为SomeEnum enum1 =(SomeEnum)2;
mamu:数字等价变化时会发生什么?
我会劝阻任何使用这种方法的人.将自己与字符串表示联系起来会限制代码的灵活性和重构.你最好使用独特的ID.存储字符串也会浪费存储空间.

2> Tom..:

除非您有特定的性能原因要避免它,否则我建议使用单独的表进行枚举.使用外键完整性,除非额外的查找真的杀了你.

西装表:

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)与枚举值无关,这有助于您处理其他语言的数据.


虽然我同意将它标准化并在数据库中进行约束是很好的,但这确实会导致两个地方的更新添加新值(代码和数据库),这可能会导致更多的开销.此外,如果所有更新都是从Enum名称以编程方式完成的,则拼写错误应该不存在.
我同意上述评论.数据库级别的另一种强制机制是编写约束触发器,该触发器将拒绝尝试使用无效值的插入或更新.

3> oxbow_lakes..:

我认为这里唯一安全的机制是使用String name()值.写入数据库时​​,可以使用sproc插入值,在读取时使用View.以这种方式,如果枚举改变,则在sproc/view中存在一个间接级别,以便能够将数据作为枚举值呈现而不将"强加"在DB上.



4> toolkit..:

正如你所说,序数有点冒险.考虑例如:

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
}

这意味着你所有的谎言都会被误解为"未找到文件"

最好只使用字符串表示


+1参考http://thedailywtf.com/Articles/What_Is_Truth_0x3f_.aspx
推荐阅读
贴进你的心聆听你的世界
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有