22

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

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

    /* constructors, getters, etc. */
}

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

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

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

是否有大多数人在这里使用的约定?人们的经验是什么?还有其他选择吗?

编辑:

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

4

8 回答 8

8

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

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

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

于 2009-01-29T15:42:04.080 回答
3

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

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

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

于 2009-03-31T17:47:31.770 回答
2

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

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

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

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

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

于 2009-01-29T16:36:32.647 回答
1

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

于 2009-04-02T15:27:46.627 回答
1

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

于 2009-04-03T17:02:30.383 回答
1

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

于 2009-04-06T11:45:41.977 回答
1

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

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

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

于 2009-04-06T12:09:49.890 回答
1

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

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

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

于 2009-04-06T15:32:27.640 回答