【问题标题】:Persisting data suited for enums适合枚举的持久化数据
【发布时间】:2010-10-04 06:34:57
【问题描述】:

大多数项目都有某种数据,这些数据在发布之间基本上是静态的,非常适合用作枚举,例如状态、事务类型、错误代码等。例如,我将只使用一个常见的状态枚举:

public enum Status {
    ACTIVE(10, "Active");
    EXPIRED(11, "Expired");
    /* other statuses... */

    /* constructors, getters, etc. */
}

我想知道其他人在对此类数据的持久性方面做了什么。我看到了几个选项,每个选项都有一些明显的优点和缺点:

  • 将可能的状态保存在状态表中,并将所有可能的状态域对象缓存起来以供整个应用程序使用
  • 只使用枚举而不持久化可用状态列表,在我和我的 DBA 之间制造了一场数据一致性圣战
  • 保持状态并在代码中维护一个枚举,但不要将它们捆绑在一起,从而创建重复数据

我的偏好是第二个选项,尽管我的 DBA 声称我们的最终用户可能希望访问原始数据以生成报告,并且不持久化状态会导致数据模型不完整(反驳:这可以解决有文档)。

这里有大多数人使用的约定吗?人们对每种方法的体验如何?还有其他选择吗?

编辑:

经过一段时间的思考,我真正的持久性斗争来自于处理与数据库中的状态相关的 id 值。这些值将在安装应用程序时作为默认数据插入。此时,他们将拥有可用作其他表中的外键的 id。我觉得我的代码需要知道这些 id,以便我可以轻松地检索状态对象并将它们分配给其他对象。我该怎么办?我可以添加另一个字段,例如“代码”,以查找内容,或者仅按名称查找状态,这很恶心。

【问题讨论】:

  • 感谢所有的答案 - 已经提出了许多不同的解决方案,所有这些都有一些好处。我将让赞成票决定赏金,因为我更喜欢社区的意见,而不是我自己的偏好。

标签: java types enums persistence


【解决方案1】:

我们使用数据库中的一些显式字符串或字符值来存储枚举值。然后从数据库值返回枚举,我们在枚举类上编写一个静态方法来迭代并找到正确的。

如果您期望有很多枚举值,您可以创建一个静态映射 HashMap<String,MyEnum> 来快速翻译。

不要存储实际的枚举名称(即您的示例中的“ACTIVE”),因为开发人员很容易对其进行重构。

【讨论】:

  • 想知道您在数据库中存储的除“ACTIVE”之外的显式字符串或字符值。
【解决方案2】:

Joshua Bloch 在他的书“Effective Java, Second Edition”(第 147 页)中对枚举以及如何使用它们进行了出色的解释

在那里,您可以找到各种技巧,如何定义枚举、持久化枚举以及如何在数据库和代码之间快速映射它们 (p.154)。

在 Jazoon 2007 的一次演讲中,Bloch 给出了使用额外属性将枚举映射到 DB 字段并返回的原因如下: 枚举是常量,但代码不是。为确保编辑源代码的开发人员不会通过重新排序枚举或重命名意外破坏数据库映射,您应该向枚举添加特定属性(如“dbName”)并使用它来映射它。

枚举有一个固有的 id(在 switch() 语句中使用),但是当你改变元素的顺序时这个 id 会改变(例如通过对它们进行排序或在中间添加元素)。

所以最好的解决方案是添加一个 toDB() 和 fromDB() 方法以及一个附加字段。我建议对这个新字段使用简短易读的字符串,这样您就可以解码数据库转储而无需查找枚举。

【讨论】:

  • 我实际上在那一章中并没有找到很多关于持久性的内容,这是我主要关心的问题。如果不对 ID、字符串之类的东西进行硬编码,或者将我的代码/枚举中的对象映射到数据库行,我真的无法找到一种方法。
【解决方案3】:

我正在使用您记录的三种方法的混合...

使用数据库作为枚举值的权威来源。将值存储在某种“代码”表中。每次构建时,为要包含在项目中的 Enum 生成一个类文件。

这样,如果枚举更改数据库中的值,您的代码将正确失效,并且您将从持续集成服务器收到适当的编译错误。您对数据库中的枚举值有一个强类型绑定,您不必担心在代码和数据之间手动同步值。

【讨论】:

  • 我的解决方案仍然适用于您的新条件:如果数据库中枚举的 id 值发生变化,只需重新生成枚举脚本并重新编译。如果你在代码中正确使用了枚举,你应该不会遇到数据库外键不匹配的问题。
  • 这仅适用于完全拥有的自定义应用程序。但是,这与 OTS 产品无关,甚至与无权访问生产系统的开发人员构建的自定义应用程序无关。
  • @JeffFritz ...请告诉我您是如何实现这一目标的?我们有类似的要求..
  • @KrithikaVittal 我们编写了一个自定义 T4 模板,该模板将连接到数据库,从我们的“代码”表中提取值并为枚举编写 C# 语法。
  • @JeffFritz 这不是最好的解决方案。如果数据库值发生变化而您没有再次编译怎么办?最好在部署时在生产环境中执行相同的操作。
【解决方案4】:

虽然我不熟悉 Java 中“属性”的概念(而且我不知道您使用的是什么语言),但我通常使用代码表(或特定于域的表)的概念,并且我已经将我的枚举值归因于更具体的数据,例如人类可读的字符串(例如,如果我的枚举值是 NewStudent,我会将其归因于“New Student”作为显示值)。然后我使用反射检查数据库中的数据并插入或更新记录,以使它们与我的代码保持一致,使用实际的枚举值作为键 ID。

【讨论】:

  • 我使用的是 Groovy,但从 Groovy 和/或 Java 的角度来看。感谢您的回答。
【解决方案5】:

我在几个场合使用的是在代码中定义枚举和在持久层(数据库、文件等)中定义存储表示,然后有转换方法将它们相互映射。只有在读取或写入持久存储时才需要使用这些转换方法,并且应用程序可以在任何地方使用类型安全的枚举。在转换方法中,我使用 switch 语句进行映射。如果要转换新的或未知的状态,这也允许抛出异常(通常是因为应用程序或数据比另一个更新,并且已经声明了新的或附加的状态)。

【讨论】:

    【解决方案6】:

    如果值列表需要更新的可能性很小,则为 1。否则,为 3。

    【讨论】:

      【解决方案7】:

      好吧,我们没有 DBA 可以回答,所以我们更喜欢选项 2)。

      我们只是将 Enum 值保存到数据库中,当我们将数据从数据库中加载到域对象中时,我们只需将整数值转换为 enum 类型。 这避免了选项 1) 和 3) 的任何同步问题。该列表在代码中定义一次。

      但是,我们有一个政策,没有其他人可以直接访问数据库;他们必须通过我们的网络服务来访问任何数据。所以这就是为什么它对我们很有效。

      【讨论】:

      • 如果有人在代码中添加一个新的枚举成员,而不是在底部,而是在顶部或中间的某个地方呢?
      【解决方案8】:

      在您的数据库中,此“域”表的主键不必是数字。只需使用 varchar pk 和描述列(出于您的 dba 所关注的目的)。如果您需要在不依赖字母排序的情况下保证值的排序,只需添加一个名为“order 或“sequence”的数字列。

      在您的代码中,创建一个带有常量的静态类,其名称(无论是否为驼峰式)映射到描述,值映射到 pk。如果您需要更多,请创建一个具有必要结构和比较运算符的类,并将其实例用作常量的值。

      如果你这样做太多,构建一个脚本来生成实例化/声明代码。

      【讨论】:

      • 但是你需要确保“顺序”或“序列”中的数值是唯一的,有点像主键......
      猜你喜欢
      • 2010-10-15
      • 2011-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-10
      相关资源
      最近更新 更多