使用和持久化枚举的最佳实践
我在这里看到了几个关于处理和保留类似枚举值的最佳方法的问题/讨论(例如 持久化适合枚举的数据,如何使用 NHibernate 持久化枚举 ),我想问一下普遍的共识是什么。
特别是:
- 代码中应该如何处理这些值?
- 应如何将它们保存到数据库(作为文本/作为数字)?
- 不同解决方案的权衡是什么?
注意:我将最初包含在该问题中的解释移至答案中。
I've seen several questions/discussions here about the best way to handle and persist enum-like values (e.g. Persisting data suited for enums , How to persist an enum using NHibernate ), and I'd like to ask what the general consenus is.
In particular:
- How should these values be handled in the code?
- How should they be persisted to a database (as text/as a number)?
- What are the tradeoffs of different solutions?
Note: I moved the explanations originally included in this question to an answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我同意你所说的大部分内容。 不过,我想补充一件事,关于枚举的持久性:我不认为在构建时从数据库值生成枚举是可以接受的,但我也认为运行时检查不是一个好的解决方案。 我定义第三种方法:进行单元测试,根据数据库检查枚举的值。 这可以防止“偶然”分歧,并避免每次运行代码时对照数据库检查枚举的开销。
I agree with much of what you say. One thing I'd like to append, though, about the persistence of enums: I don't believe the generation of the enums at build time from the DB values is acceptable, but I also think that the runtime check is not a good solution. I'd define a third means: have a unit test which will check the values of the enum against the database. This prevents "casual" divergence, and avoids the overhead of checking the enums against the database every time the code is run.
最初的文章对我来说看起来不错。 尽管如此,根据评论,似乎一些有关 Java 枚举的评论可能会澄清一些事情。
Java 中的枚举类型根据定义是一个类,但许多程序员往往会忘记这一点,因为他们宁愿将其与其他一些语言中的“允许值列表”联系起来。 不仅如此。
因此,为了避免这些 switch 语句,在枚举类中放置一些代码和附加方法可能是合理的。 几乎不需要创建一个单独的“类似枚举的真实类”。
还要考虑文档的要点 - 您想在数据库中记录枚举的实际含义吗? 在反映值(您的枚举类型)的源代码中或在某些外部文档中? 我个人更喜欢源码。
如果由于速度或任何原因想要在数据库中将枚举值呈现为整数,则该映射也应该驻留在 Java 枚举中。 默认情况下,您将获得字符串名称映射,我对此感到满意。 每个枚举值都有一个关联的序号,但是直接使用它作为代码和数据库之间的映射并不是很明智,因为如果有人对源代码中的值重新排序,该序号将会改变。 或者在现有值之间添加其他枚举值。 或者删除一些值。
(当然,如果有人更改了源代码中枚举的名称,默认的字符串映射也会变质,但这不太可能意外发生。如果需要,您可以通过进行一些运行时检查来更轻松地防止这种情况发生并按照此处的建议检查数据库中的约束。)
The initial article looks fine to me. Still, based on the comments, it seems some comments concerning Java enums might clarify few things.
Enum type in Java is a class by definition, but many programmers tend to forget this, because they rather relate it to "a list of allowed values" as in some other languages. It's more than that.
So, to avoid those switch statements it might be reasonable to put some code and additional methods in the enum class. There's almost never a need to create a separate "enum-like real class".
Consider also the point of documentation - do you want to document the actual meaning of your enum in the database? In the source code reflecting the values (your enum type) or in some external documentation? I personally prefer the source code.
If you want to present enum values as integers in the database due to speed or whatever reason, that mapping should also reside in the Java enum. You'll get string-name mapping by default, and I've been content with that. There's an ordinal number associated with each enum value, but using that directly as a mapping between code and database is not very bright, because that ordinal number will change if someone reorders the values in the source code. Or adds additional enum values in between existing values. Or removes some value.
(Of course, if someone changes the name of the enum in the source code, the default string-mapping goes sour too, but that's less likely to happen accidentally. And you can more easily protect against that if necessary by putting some runtime-checking and check constraints in the database as suggested here already. )
我尝试总结一下我的理解。 如果您有任何更正,请随时编辑。 所以这里是:
在代码中
在代码中,应该使用语言的本机枚举类型(至少在 Java 和 C# 中)或使用类似 “类型安全枚举模式”。 不鼓励使用普通常量(整数或类似常量),因为您会失去类型安全性(并且很难理解哪些值是例如方法的合法输入)。
这两者之间的选择取决于要向枚举附加多少附加功能:
特别是,至少在 Java 中,枚举不能从另一个类继承,因此,如果您有多个具有类似行为的枚举,并且希望将其放入超类中,则不能使用 Java 的枚举。
持久化枚举
要持久化枚举,应为每个枚举值分配一个唯一的 ID。 这可以是整数,也可以是短字符串。 短字符串是首选,因为它可以助记(使 DBA 等更容易理解数据库中的原始数据)。
这种方法的一个问题是合法枚举值列表存在于两个地方(代码和数据库)。 这是很难避免的,因此通常被认为是可以接受的,但有两种选择:
I've tried to summarize my understanding. Feel free to edit this if you have any corrections. So here it goes:
In the code
In the code, enums should be handled using either the language's native enum type (at least in Java and C#), or using something like the "typesafe enum pattern". Using plain constants (Integer or similar) is discouraged, as you lose type safety (and make it hard to understand which values are legal input for e.g. a method).
The choice between these two depends on how much additional functionality is to be attached to the enum:
In particular, at least in Java an enum cannot inherit from another class, so if you have several enums with similar behavior which you'd like to put into a superclass, you cannot use Java's enums.
Persisting enums
To persist enums, each enum value should be assigned a unique ID. This can be either an integer, or a short string. A short string is preferred, since it can be mnemonic (makes it easier for DBAs etc. to understand the raw data in the db).
One problem with this approach is that the list of legal enum values exists in two places (code and database). This is hard to avoid and therefore often considered acceptable, but there are two alternatives:
在 C# 的代码处理中,您错过了定义 decaring 0 值。
我几乎总是将我的第一个值声明为:
以便充当空值。 因为支持类型是一个整数,并且整数默认为 0,所以它在很多地方对于了解枚举是否实际上已通过编程方式设置非常有用。
In the code handling for C# you've missed out defining delcaring the 0 value.
I almost without fail always declare my first value as:
So as to serve as a null value. Because the backing type is an integer and an integer defaults to 0 so it is massively useful in a lot of places to know if an enum has actually been programatically set or not.
Java 或 C# 应始终在代码中使用枚举。 免责声明:我的背景是 C#。
如果要将值保留到数据库中,则应显式定义每个枚举成员的整数值,以便以后代码更改不会意外更改转换后的枚举值,从而更改应用程序行为。
值应始终作为整数值保存到数据库中,以防止枚举名称重构。 在 wiki 中保留每个枚举的文档,并向数据库字段添加注释,指向记录该类型的 wiki 页面。 还将 XML 文档添加到包含 wiki 条目链接的枚举类型,以便可以通过 Intellisense 访问它。
如果您使用工具生成 CRUD 代码,它应该能够定义用于列的枚举类型,以便生成的代码对象始终使用枚举成员。
如果需要对枚举成员应用自定义逻辑,您有一些选择:
我还没有尝试过这个,但是使用 SQL Server 2005 或更高版本,理论上您可以将 C# 代码注册到数据库,该数据库将包含枚举信息以及将值转换为枚举以在视图或其他构造中使用的能力,从而形成一种转换以 DBA 更容易使用的方式获取数据。
Java or C# should always use enums in code. Disclaimer: My background is C#.
If the value is to be persisted to a database, the integral values of each enumeration member should be explicitly defined so that a later change in code does not accidentally alter translated enum values and thus application behavior.
Values should always be persisted to a database as integral values, to protect against enum name refactoring. Keep documentation on each enumeration in a wiki and add a comment to the database field pointing to the wiki page documenting the type. Also add XML documentation to the enum type containing a link to the wiki entry so that it is available through Intellisense.
If you use a tool to generate CRUD code it should be capable of defining an enumeration type to use for a column so that generated code objects always use enumerated members.
If custom logic needs to be applied for an enumeration member, you have some options:
I have not tried this, but with SQL Server 2005 or later, you could theoretically register C# code with the database that would contain enum information and the ability to convert values to enums for use in views or other constructs, making a method of translating the data in a manner easier for DBAs to use.
恕我直言,至于代码部分:
您应该始终使用“枚举”类型进行枚举,基本上,如果您这样做,您会得到很多免费赠品:类型安全,封装和避免切换,一些人的支持
EnumSet
和EnumMap
等集合以及代码清晰度。至于持久化部分,您始终可以持久化枚举的字符串表示形式,并使用 enum.valueOf(String) 方法将其加载回来。
Imho, as for the code part:
You should always use the 'enum' type for your enumerations, basically you get alot of freebies if you do: Type safety, encapsulation and switch avoidance, the support of some collections such as
EnumSet
andEnumMap
and code clarity.as for the persistence part you can always persist the string representation of the enum and load it back using the enum.valueOf(String) method.
由于需要额外的空间且搜索速度较慢,因此在数据库中存储枚举的文本值不如存储整数。 它的价值在于它比数字更有意义,但是数据库是用于存储的,而表示层是用于使事物看起来更好的。
Storing the text value of an enum in a database is less preferred to storing an integer, due to the additional space required and slower searching. It is valuable in that it has more meaning than a number, however the database is for storage, and the presentation layer is for making things look nice.
嗯,根据我的经验,除了将选项(作为标志)传递给立即方法调用之外,使用枚举进行任何其他操作都会在某些时候导致
switch
-ing。switch
语句)switch
-es 。因此,对于具有如果您需要更多的功能,您应该花一些时间将其创建为一个类,并记住以下几点:
Well, from my experience, using enums for anything other than for passing options (as flags) to an immediate method call, results in
switch
-ing at some point.switch
statement)switch
-es in your extension methods if not elsewhere.So for an enum-like entity with a bit more of functionality you should take some time and create it as a class, with several things in mind:
每次您发现自己在代码中使用“幻数”时,都会更改为枚举。 除了节省时间(因为当错误出现时,魔法就会消失......)它会节省你的眼睛和记忆(有意义的枚举使代码更具可读性和自我记录),因为你猜怎么着 - 你很可能是维护和开发的人你自己的代码
Each time you find your self using "magic numbers" in code change to enums. Besides time savings ( since magic will disappear when the bugs come ...) it will save your eyes and memory (meaningful enums make code more readable and self-documenting), since guess what - you are most probably the person to maintain and develop your own code
我知道这是一个旧论坛,如果数据库可能有其他东西直接集成到其中怎么办? 例如,当生成的数据库是代码的唯一目的时。 然后,您将在每次集成时定义枚举。 最好将它们放在数据库中。 否则,我同意原帖。
I know this is an old forum, what if the database might have other things integrating directly to it? E.g. when the resulting DB is the SOLE purpose of the code. Then, you will be defining the enums at every integration. Better then to have them in the DB. Otherwise, I agree with the original post.