Java 应用程序中处理引用数据的类型安全方法

Type-safe approaches for handling reference data in Java applications

除了“我们一直这样做”之外,目前可能没有其他充分理由,如何设计新系统以使用用于表示州代码的参考数据?

例如,一个案例可能有 2 个有效状态,'Open' 或 'Closed'。从历史上看,我见过许多系统将这些有效值存储在包含此参考数据的数据库 table 中,并称为代码类型 ('CaseStatus'),并且每个有效值都有一个 'code' 值(例如 'OPN')和当需要向用户显示该值时使用的解码或显示值(在本例中为 'Open')。

如果今天开发一个基于 Java 的系统,从具有类型安全的代码的角度来看,我们会像这样定义一个枚举:

public enum CaseStatus{
    Open("OPN"),
    Closed("CLS");

    private String codeValue;

    private CaseStatus(String codeValue){
        this.codeValue = codeValue;
    }
}

仅从源代码的角度来看,这非常棒,枚举通过有效值的限制列表强制执行类型安全,但就其本身而言,这种代码类型或其有效值在数据库中没有表示。如果数据的用户 运行 直接针对数据库进行临时报告,他们需要一种方法来查找 'OPN'、'CLS' 的解码值。从历史上看,这是使用包含代码类型、代码及其解码值的参考 table 完成的。

我们继续使用这些州代码值作为“3 字母代码”似乎很奇怪,此时的动机不再是因为我们需要在数据库中保存 space('OPN' vs 'Open' 无论如何都不是一个很好的优化)。

人们在他们最近使用的系统上使用或看到了哪些其他方法?您是仅在数据库中维护参考数据,仅在代码中维护,还是在两个地方都维护,如果在这两个地方都维护,您使用什么方法使两者保持同步?

我遇到的一种解决方案是在数据库中使用物化视图来动态重新计算非规范化关系。在基于文档的数据库中,您可能会将 CaseStatus 存储为 String。最后,您可以使用 ORM 工具将 CaseStatus 存储为 Object,但在我熟悉的情况下,参考数据存储在数据库中(如果将其存储在代码中,则需要构建和部署到生产环境,以及对 发布 ).

的额外测试

首先,如果只有两个可能的值,并且不可能期望它们发展成更大的数字(如您的 open/closed 示例),我会可能将 status_open 列定义为 BOOLEANSMALLINT (0/1) 或 CHAR (Y/N)。

当状态范围更大(或可能增加到两个以上的值)时,我会使用代理键。虽然节省几个字节几乎不是优化,但索引和连接 CHAR 值列比索引和连接 INTEGER 列更昂贵。虽然我没有关于 INTEGERCHAR(3) 问题的度量标准,但我认为对于这种情况,差异不会像 INTEGER 与 CHAR(50) 的情况一样大。

然而,我在小 CHAR 缩写中发现的一个缺点是有时很难找到有意义的值。假设你的状态是"broken - replacement has been ordered",我叫它"BRO"有用吗?是不是比叫3好?

另一方面,即使模型不需要它,我也发现在 status 上添加一个简短的 VARCHAR 列很方便,用于描述每个助记键或代理键的含义。 (模型长大后,就很难全部记住了!)

我的实施(在特定情况下有适当的例外)可能是:

在 Java 端,枚举,如您定义的那样。 (即使对于类似布尔值的值,有时它有助于为每个值使用不同的枚举,特别是如果有方法将其中的几个值作为参数。具有相同类型的一长串参数的方法是灾难的根源。)

SQL 方:

CREATE TABLE status (
  id INTEGER PRIMARY KEY,
  description VARCHAR(40)
)

CREATE TABLE entity (
 ...
 status_id INTEGER REFERENCES status(id)
)

INSERT INTO status VALUES (0,'Closed');
INSERT INTO status VALUES (1,'Open');
INSERT INTO status VALUES (2,'Broken - replacement has been ordered');