使用和持久化枚举的最佳实践

发布于 2024-07-16 09:51:17 字数 433 浏览 6 评论 0原文

我在这里看到了几个关于处理和保留类似枚举值的最佳方法的问题/讨论(例如 持久化适合枚举的数据如何使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

明媚殇 2024-07-23 09:51:17

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

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.

平安喜乐 2024-07-23 09:51:17

最初的文章对我来说看起来不错。 尽管如此,根据评论,似乎一些有关 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. )

作死小能手 2024-07-23 09:51:17

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

在代码中

在代码中,应该使用语言的本机枚举类型(至少在 Java 和 C# 中)或使用类似 “类型安全枚举模式”。 不鼓励使用普通常量(整数或类似常量),因为您会失去类型安全性(并且很难理解哪些值是例如方法的合法输入)。

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

  • 如果您想将大量功能放入枚举中(这很好,因为您始终避免对其进行 switch() 操作),班级通常更合适。
  • 另一方面,对于简单的类似枚举的值,语言的枚举通常更清晰。

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

持久化枚举

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

  • 在软件中,每个枚举都应该具有映射函数,用于在枚举(用于软件内部使用)和 ID 值(用于持久化)之间进行转换。 某些框架(例如(N)Hibernate)对自动执行此操作的支持有限。 否则,您必须将其放入枚举类型/类中。
  • 数据库(理想情况下)应该为每个枚举包含一个列出合法值的表。 其中一列是 ID(见上文),即 PK。 附加列可能对于描述等有意义。 然后,将包含该枚举中的值的所有表列都可以使用此“枚举表”作为 FK。 这保证了错误的枚举值永远不会被持久化,并允许数据库“独立存在”。

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

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

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:

  • If you want to put loads of functionality into the enum (which is good, because you avoid switch()ing on it all the time), a class is usually more appropriate.
  • On the other hand, for simple enum-like values, the language's enum is usually clearer.

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).

  • In the software, every enum should then have mapping functions to convert between the enum (for use inside the software) and the ID value (for persisting). Some frameworks (e.g. (N)Hibernate) have limited suppport for doing this automatically. Otherwise, you have to put it into the enum type/class.
  • The database should (ideally) contain a table for each enum listing the legal values. One column would be the ID(see above), which is the PK. Additional columns might make sense for e.g. a description. All table columns that will contain values from that enum can then use this "enum table" as a FK. This guarantees that incorrect enum values can never be persisted, and allows the DB to "stand on its own".

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:

  • Only keep the list of values in the DB, generate the enum type at build time. Elegant, but means that a DB connection is required for a build to run, which seems problematic.
  • Define the list of values in the code to be authoritative. Check against the values in the DB at runtime (usually at startup), complain/abort on mismatch.
一梦等七年七年为一梦 2024-07-23 09:51:17

在 C# 的代码处理中,您错过了定义 decaring 0 值。
我几乎总是将我的第一个值声明为:

public enum SomeEnum
{
    None = 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:

public enum SomeEnum
{
    None = 0,
}

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.

戈亓 2024-07-23 09:51:17

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

如果要将值保留到数据库中,则应显式定义每个枚举成员的整数值,以便以后代码更改不会意外更改转换后的枚举值,从而更改应用程序行为。

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

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

如果需要对枚举成员应用自定义逻辑,您有一些选择:

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

我还没有尝试过这个,但是使用 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:

  • If you have an enum MyEnum, create a static class MyEnumInfo which offers utility methods to discover additional information about the enum member, by switch statements or whatever means necessary. Appending "Info" to the end of the enum name in the class name ensures that they will be next to each other in IntelliSense.
  • Decorate the enumeration members with attributes to specify additional parameters. For example we have developed an EnumDropDown control which creates an ASP.NET dropdown filled with enumeration values, and an EnumDisplayAttribute specifies the nicely formatted display text to use for each member.

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.

不离久伴 2024-07-23 09:51:17

恕我直言,至于代码部分:

您应该始终使用“枚举”类型进行枚举,基本上,如果您这样做,您会得到很多免费赠品:类型安全,封装和避免切换,一些人的支持EnumSetEnumMap 等集合以及代码清晰度。

至于持久化部分,您始终可以持久化枚举的字符串表示形式,并使用 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 and EnumMap 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.

可爱暴击 2024-07-23 09:51:17

由于需要额外的空间且搜索速度较慢,因此在数据库中存储枚举的文本值不如存储整数。 它的价值在于它比数字更有意义,但是数据库是用于存储的,而表示层是用于使事物看起来更好的。

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.

夜唯美灬不弃 2024-07-23 09:51:17

嗯,根据我的经验,除了将选项(作为标志)传递给立即方法调用之外,使用枚举进行任何其他操作都会在某些时候导致 switch-ing。

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

因此,对于具有如果您需要更多的功能,您应该花一些时间将其创建为一个类,并记住以下几点:

  • 要使您的类表现得像枚举,您可以强制每个派生类实例化为 Singleton,或者重写 Equals 以允许值 如果您的类是类似
  • 包含可序列化的状态 - 仅从其类型就可以进行反序列化(正如您所说,应该是一种“ID”)。
  • 枚举的,则应该意味着它不应该 仅限于基类,否则扩展“枚举”将是一场噩梦,如果您选择单例模式,则需要确保正确反序列化为单例实例。

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.

  • If you are going to use the enum all over your code, then you might end up with code that is not so easy to maintain (the infamous switch statement)
  • Extending enums is a pain. You add a new enum item and end up in going through all of your code to check for all conditions.
  • With .NET 3.5, you can add extension methods to enums to make them behave a bit more like classes. However, adding real functionality this way is not so easy since it's still not a class (you would end up using 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:

  • To make your class behave like an enum, you can either force each derived class to instantiate as a Singleton, or override Equals to allow value comparison of different instances.
  • If your class is enum-like, it should mean that it should contain no serializable state - deserialization should be possible from its type alone (a sort of an "ID", as you said).
  • Persistence logic should be confined to the base class only, otherwise extending your "enum" would be a nightmare. In case that you went for the Singleton pattern, you would need to ensure proper deserialization into singleton instances.
2024-07-23 09:51:17

每次您发现自己在代码中使用“幻数”时,都会更改为枚举。 除了节省时间(因为当错误出现时,魔法就会消失......)它会节省你的眼睛和记忆(有意义的枚举使代码更具可读性和自我记录),因为你猜怎么着 - 你很可能是维护和开发的人你自己的代码

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

π浅易 2024-07-23 09:51:17

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文