在数据库中保存枚举的方法

我想知道将枚举保存到数据库中的最佳方法是什么。

我知道有name()valueOf()方法,使其成为一个string回来。 但有没有其他(灵活的)选项来存储这些值?

有一个聪明的方法,使他们成为唯一的数字( ordinal()是不安全的使用)?

任何意见和build议将是有益的:)

更新:

感谢所有真棒和快速的答案! 正如我怀疑的那样。

但是要注意“工具包”; 这是一个方法。 问题是,我将不得不添加相同的方法与我创build的每个枚举types。 这是很多重复的代码,目前,Java不支持任何解决scheme(你不能让枚举扩展其他类)。

但是,谢谢你的所有答案!

我们不再将枚举存储为数字序数值了; 它使debugging和支持的方式太困难了。 我们存储实际的枚举值转换为string:

 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"]); 

过去的问题是盯着企业pipe理者,试图破译:

 Name Suit ================== ========== Shelby Jackson 2 Ian Boyd 1 

经文

 Name Suit ================== ========== Shelby Jackson Diamond Ian Boyd Heart 

后者要容易得多。 前者需要获取源代码并find分配给枚举成员的数值。

是的,它需要更多的空间,但枚举成员名称短,硬盘便宜,当你有问题时,它是更值得帮助。

此外,如果您使用数字值,则与您绑定。 你不能很好地插入或重新排列成员,而不必强制旧的数值。 例如,将Suit的枚举更改为:

 public enum Suit { Unknown, Heart, Club, Diamond, Spade } 

将不得不成为:

 public enum Suit { Unknown = 4, Heart = 1, Club = 3, Diamond = 2, Spade = 0 } 

以维护存储在数据库中的遗留数值。

除非你有特定的性能原因来避免它,否则我会build议使用一个单独的表进行枚举。 使用外键完整性,除非额外的查找真的杀死你。

西装表:

 suit_id suit_name 1 Clubs 2 Hearts 3 Spades 4 Diamonds 

球员表

 player_name suit_id Ian Boyd 4 Shelby Lake 2 
  1. 如果您将枚举重构为具有行为的类(例如优先级),那么您的数据库已经正确地对其进行了build模
  2. 你的DBA很高兴,因为你的模式是规范化的(每个玩家存储一个整数,而不是整个string,可能有错别字)。
  3. 您的数据库值( suit_id )与您的枚举值无关,这有助于您处理来自其他语言的数据。

我会争辩说,这里唯一的安全机制是使用String name()值。 写入数据库时​​, 可以使用sproc插入值,读取时使用View。 以这种方式,如果枚举改变,那么在sproc / view中存在一个间接级别,以便能够将数据呈现为枚举值,而不在数据库上“施加”这个数据。

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

 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 } 

这意味着你所有的谎言将被误解为“文件未find”

最好只使用string表示

我们只是存储枚举名称本身 – 它更可读。

我们曾经为一些有限的值存储了具体的枚举值,例如,这个枚举具有一组有限的状态,我们使用一个char来表示(比数值更有意义):

 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; } } } 

当你有很多值的时候,你需要在你的枚举中有一个Map来保持getFromXYZ方法的小。

对于大型数据库,我不愿意丢失数字表示的大小和速度优势。 我经常以一个代表Enum的数据库表结束。

您可以通过声明一个外键来强制数据库的一致性 – 尽pipe在某些情况下,最好不要声明这是一个外键约束,这会对每个事务施加成本。 您可以通过在您select的时间周期性地进行支票来确保一致性:

 SELECT reftable.* FROM reftable LEFT JOIN enumtable ON reftable.enum_ref_id = enumtable.enum_id WHERE enumtable.enum_id IS NULL; 

这个解决scheme的另一半是编写一些testing代码,检查Java枚举和数据库枚举表是否具有相同的内容。 这是读者的一个练习。

如果将枚举types保存为数据库中的string,则可以创build实用程序方法来(对)任何枚举进行序列化:

  public static String getSerializedForm(Enum<?> enumVal) { String name = enumVal.name(); // possibly quote value? return name; } public static <E extends Enum<E>> E deserialize(Class<E> enumType, String dbVal) { // possibly handle unknown values, below throws IllegalArgEx return Enum.valueOf(enumType, dbVal.trim()); } // Sample use: String dbVal = getSerializedForm(Suit.SPADE); // save dbVal to db in larger insert/update ... Suit suit = deserialize(Suit.class, dbVal); 

我面临同样的问题,我的目标是将枚举string值保存到数据库而不是序号值。

为了解决这个问题,我使用了@Enumerated(EnumType.STRING) ,我的目标得到了解决。

例如,你有一个Enum类:

 public enum FurthitMethod { Apple, Orange, Lemon } 

在实体类中,定义@Enumerated(EnumType.STRING)

 @Enumerated(EnumType.STRING) @Column(name = "Fruits") public FurthitMethod getFuritMethod() { return fruitMethod; } public void setFruitMethod(FurthitMethod authenticationMethod) { this.fruitMethod= fruitMethod; } 

在尝试将值设置为Database时,String值将作为“ APPLE ”,“ ORANGE ”或“ LEMON ”保存到数据库中。

带有OR关系的多个值,枚举字段。 .NET的概念,在数据库中存储enumtypes,如字节或int,并在代码中使用FlagsAttribute。

http://blogs.msdn.com/b/efdesign/archive/2011/06/29/enumeration-support-in-entity-framework.aspx

我所有的经验告诉我,任何地方坚持枚举的最安全的方法是使用额外的代码值或id(@jeebee答案的某种进化)。 这可能是一个很好的例子:

 enum Race { HUMAN ("human"), ELF ("elf"), DWARF ("dwarf"); private final String code; private Race(String code) { this.code = code; } public String getCode() { return code; } } 

现在你可以去任何持久化引用你的枚举常量的代码。 即使您决定更改一些常量名称,您也可以将代码值(例如DWARF("dwarf")GNOME("dwarf")

好吧,再深入一点这个概念。 这里有一些实用的方法,可以帮助你find任何枚举值,但首先让我们扩展我们的方法。

 interface CodeValue { String getCode(); } 

让我们的枚举实现它:

 enum Race implement CodeValue {...} 

这是魔术search方法的时间:

 static <T extends Enum & CodeValue> T resolveByCode(Class<T> enumClass, String code) { T[] enumConstants = enumClass.getEnumConstants(); for (T entry : enumConstants) { if (entry.getCode().equals(code)) return entry; } // In case we failed to find it, return null. // I'd recommend you make some log record here to get notified about wrong logic, perhaps. return null; } 

并使用它像一个魅力: Race race = resolveByCode(Race.class, "elf")