使用和保存枚举的最佳做法

时间:2009-04-14 08:58:27

标签: c# java database enums persistence

我在这里看到了几个关于处理和保持类似枚举值的最佳方法的问题/讨论(例如Persisting data suited for enumsHow to persist an enum using NHibernate),我想问一下一般的共识是什么是

特别是:

  • 如何在代码中处理这些值?
  • 如何将它们保存到数据库中(如文本/数字)?
  • 不同解决方案的权衡是什么?

注意:我将原来包含在此问题中的解释移到了答案中。

10 个答案:

答案 0 :(得分:18)

我同意你说的很多内容。但是,有一件事我想补充一下enums的持久性:我不相信在构建时从DB值生成枚举是可以接受的,但我也认为运行时检查不是一个好的解决方案。我定义了第三种方法:进行单元测试,检查枚举对数据库的值。这可以防止“随意”分歧,并避免每次运行代码时检查枚举对数据库的开销。

答案 1 :(得分:12)

最初的文章对我来说很好看。不过,根据评论,似乎有些关于Java枚举的评论可能会澄清一些事情。

根据定义,Java中的枚举类型是一个类,但许多程序员倾向于忘记这一点,因为他们宁愿将其与“允许值列表”相关联,就像在其他一些语言中一样。它不止于此。

因此,为了避免使用这些switch语句,在枚举类中放置一些代码和其他方法可能是合理的。几乎从来没有必要创建一个单独的“枚举类真实类”。

还要考虑文档的要点 - 您是否要记录数据库中枚举的实际含义?在反映值(您的枚举类型)或某些外部文档的源代码中?我个人更喜欢源代码。

如果由于速度或任何原因想要将枚举值作为整数呈现在数据库中,那么该映射也应该驻留在Java枚举中。默认情况下,您将获得字符串名称映射,我一直对此感到满意。有一个与每个枚举值相关联的序数,但直接使用它作为代码和数据库之间的映射并不是很明亮,因为如果有人重新排序源代码中的值,那么序号将会改变。或者在现有值之间添加额外的枚举值。或删除一些价值。

(当然,如果有人在源代码中更改了枚举的名称,那么默认的字符串映射也会变坏,但不太可能意外发生。如果有必要,你可以通过放置一些来更容易地防止这种情况发生。运行时检查并检查数据库中的约束,如此处所示。)

答案 2 :(得分:6)

在C#的代码处理中,你错过了定义delcaring 0值的方法。 我几乎没有失败,总是将我的第一个值声明为:

public enum SomeEnum
{
    None = 0,
}

以便作为空值。因为支持类型是一个整数而整数默认为0所以它在很多地方非常有用,可以知道枚举是否实际上是以编程方式设置的。

答案 3 :(得分:6)

我试图总结一下我的理解。如果您有任何更正,请随时编辑此内容。所以这就是:

在代码中

在代码中,应使用语言的本机枚举类型(至少在Java和C#中)或使用类似"typesafe enum pattern"的内容来处理枚举。不鼓励使用普通常量(整数或类似),因为你失去了类型安全性(并且很难理解哪些值是例如方法的合法输入)。

这两者之间的选择取决于将多少附加功能附加到枚举:

  • 如果你想将大量的功能放入枚举中(这很好,因为你总是避免使用switch()),一个类通常更合适。
  • 另一方面,对于简单的类似枚举的值,语言的枚举通常更清晰。

特别是,至少在Java中,枚举不能从其他类继承,因此如果您有几个具有类似行为的枚举,并且您希望将其放入超类,则不能使用Java的枚举。

保留枚举

要保留枚举,应为每个枚举值分配唯一的ID。这可以是整数,也可以是短字符串。首选短字符串,因为它可以是助记符(使DBA等更容易理解数据库中的原始数据)。

  • 在软件中,每个枚举应该具有映射功能,以便在枚举(用于软件内部)和ID值(用于持久化)之间进行转换。一些框架(例如(N)Hibernate)为自动执行此操作提供了有限的支持。否则,你必须把它放入枚举类型/类。
  • 数据库应该(理想情况下)包含列出合法值的每个枚举的表。一列是ID(见上文),即PK。其他列可能适用于例如说明。所有包含该枚举值的表列都可以使用此"枚举表"作为FK。这可以保证永远不会保留不正确的枚举值,并允许数据库独立存在"。

这种方法的一个问题是合法枚举值列表存在于两个地方(代码和数据库)。这很难避免,因此通常被认为是可以接受的,但有两种选择:

  • 仅保留数据库中的值列表,在构建时生成枚举类型。优雅,但意味着构建运行需要数据库连接,这似乎有问题。
  • 将代码中的值列表定义为具有权威性。在运行时检查数据库中的值(通常在启动时),在不匹配时进行抱怨/中止。

答案 4 :(得分:5)

Java或C#应始终在代码中使用枚举。免责声明:我的背景是C#。

如果要将值持久保存到数据库,则应明确定义每个枚举成员的整数值,以便稍后更改代码不会意外地更改已转换的枚举值,从而改变应用程序行为。

值应始终作为整数值持久保存到数据库,以防止枚举名称重构。在Wiki中保留每个枚举的文档,并在数据库字段中添加注释,指向记录该类型的Wiki页面。还要将XML文档添加到包含wiki条目链接的枚举类型,以便通过Intellisense提供。

如果使用工具生成CRUD代码,它应该能够定义用于列的枚举类型,以便生成的代码对象始终使用枚举成员。

如果需要为枚举成员应用自定义逻辑,则可以选择以下选项:

  • 如果您有一个枚举MyEnum,请创建一个静态类MyEnumInfo,它提供实用程序方法,通过switch语句或任何必要的方法发现有关枚举成员的其他信息。将“Info”附加到类名中枚举名称的末尾可确保它们在IntelliSense中彼此相邻。
  • 使用属性装饰枚举成员以指定其他参数。例如,我们开发了一个EnumDropDown控件,它创建一个填充枚举值的ASP.NET下拉列表,EnumDisplayAttribute指定用于每个成员的格式良好的显示文本。

我没有尝试过这个,但是对于SQL Server 2005或更高版本,理论上可以将C#代码注册到包含枚举信息的数据库以及将值转换为枚举以在视图或其他构造中使用的能力,从而制作方法以更容易DBA使用的方式翻译数据。

答案 5 :(得分:3)

由于需要额外的空间和较慢的搜索,将枚举的文本值存储在数据库中不太适合存储整数。它的价值在于它比数字更有意义,但数据库用于存储,而表示层则用于使事物看起来很好。

答案 6 :(得分:3)

根据我的经验,使用枚举除了将选项(作为标志)传递给立即方法调用之外的其他任何内容,都会在某个时刻导致switch

  • 如果您打算在整个代码中使用枚举,那么最终可能会找到不易维护的代码(臭名昭着的switch语句)
  • 扩展枚举是一种痛苦。您添加了一个新的枚举项,最后会查看所有代码以检查所有条件。
  • 使用.NET 3.5,您可以向枚举添加扩展方法,使它们的行为更像类。但是,以这种方式添加实际功能并不是那么容易,因为它仍然不是一个类(如果不是在其他地方,你最终会在扩展方法中使用switch - 。

因此,对于具有更多功能的类似枚举的实体,您应该花一些时间将其创建为一个类,并考虑到以下几点:

  • 要使类的行为类似于枚举,您可以强制每个派生类实例化为Singleton,或者重写Equals以允许不同实例的值比较。
  • 如果你的类是枚举式的,那么它应该意味着它应该不包含可序列化的状态 - 反序列化应该只能从它的类型(如你所说的那种“ID”)开始。
  • 持久性逻辑应仅限于基类,否则扩展“枚举”将是一场噩梦。如果您使用Singleton模式,则需要确保对单例实例进行适当的反序列化。

答案 7 :(得分:3)

每当您发现自己在代码中使用“魔术数字”更改为枚举时。除了节省时间(因为魔法会在错误来临时消失......)它会拯救你的眼睛和记忆(有意义的枚举使代码更具可读性和自我记录),因为猜猜是什么 - 你很可能是维持和发展的人你自己的代码

答案 8 :(得分:2)

Imho,至于代码部分:

你应该始终使用'enum'类型作为枚举,如果你这样做,基本上你会得到很多免费赠品:键入安全性,封装和切换避免,支持某些集合,例如{{ 1}}和EnumSet以及代码清晰度。

对于持久性部分,您始终可以使用enum.valueOf(String)方法保留枚举的字符串表示形式并将其加载回来。

答案 9 :(得分:1)

我知道这是一个古老的论坛,如果数据库可能有其他东西直接集成到它,该怎么办?例如。当结果DB是代码的SOLE目的时。然后,您将在每次集成时定义枚举。最好将它们放在DB中。否则,我同意原帖。

相关问题