在数据库中保存枚举的方法
我想知道将枚举保存到数据库中的最佳方法是什么。
我知道有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
- 如果您将枚举重构为具有行为的类(例如优先级),那么您的数据库已经正确地对其进行了build模
- 你的DBA很高兴,因为你的模式是规范化的(每个玩家存储一个整数,而不是整个string,可能有错别字)。
- 您的数据库值(
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")