你把字典数据放在哪里?

4
假设我的应用程序中有一组国家数据。我期望这些数据会发生变化,但不是很频繁。换句话说,我不认为这组数据是运营数据(例如,我不会提供CRUD操作以修改Country数据)。
基于此,我需要将这些数据存储在某个地方。我看到两种方法可以实现:
1. 数据库驱动。创建并填充一个Country表,提供某种类型的DAO进行访问(例如findById())。这样客户端代码将必须知道国家的ID(这也可以是名称或ISO代码)。在应用程序侧,我将拥有一个Country类。
2. 应用程序驱动。创建一个枚举,在其中列出系统已知的所有国家。它也将存储在数据库中,但是区别在于现在客户端代码不需要查询方法(findById、findByName等)并硬编码ID、名称或ISO代码。它将直接引用特定的国家。
我倾向于第二种解决方案,原因有几个。您如何做到这一点?
这样称呼它为“字典数据”是否正确?
附加说明:这里的主要问题之一是,如果我有一个查找方法,例如findByName(“捷克斯洛伐克”),那么在1992年之后这将返回空值。我不知道客户端代码会如何反应(毕竟它会期望永远都能得到国家数据,因为这是一个字典数据)。如果有类似findById(ID_CZ)的操作,则情况会更糟。找到所有这些依赖关系将非常困难。
如果我从枚举中删除Country.Czechoslovakia,我将强制自己处理任何对Czechoslovakia的依赖关系。
7个回答

2
在我工作的一些应用程序中,数据库中有一个单独的“枚举”表格,其中包含此类数据。它只包含两个列:EnumName和Value,并且将被填充如下:
  • "Country", "Germany"
  • "Country", "United Kingdom"
  • "Country", "United States"
  • "Fruit", "Apple"
  • "Fruit", "Banana"
  • "Fruit", "Orange"
然后在应用程序执行开始时读取并缓存。优点是我们不需要为每个不同的枚举类型使用数十个数据库表;如果需要更改数据,则无需重新编译任何内容。
这可以轻松扩展以包括额外的列,例如指定默认排序顺序或替代ID。

这个解决方案可能对经常更改的枚举类型很有趣。国家枚举类型可能会长期保持稳定。 - Georgy Bolyuba

1

如果数据不经常更改,而且您可以承受将应用程序关闭以应用更新,则可以将其放置在Java枚举中,并编写自己的findById()findByName()等方法。

优点:

  • 快速 - 不需要访问不变数据的数据库(或缓存要求);
  • 简单;
  • 与重构工具兼容。

缺点:

  • 需要关闭应用程序进行更新。

如果将数据放置在自己的jar文件中,则更新就像更新jar并重新启动应用程序一样简单。

硬编码问题可以通过消费者存储枚举本身的值或引用ISO代码来解决,这些代码不太可能更改国家...

如果您担心此枚举与数据库保持同步,请编写集成测试以检查并定期运行它(例如:在CI机器上)。


对我来说将其变为枚举类型的部分好处是我不需要使用findById()、findByName()。你不必使用findByName("USA")了,直接用Country.USA就行。在我看来,这里也不需要使用ID。 - Georgy Bolyuba
如果你的对象成员字段是该枚举类型,那么这是正确的。但有时候并非如此 - 例如,第三方组件可能将国家表示为 ISO 代码,而你想将其转换为你的枚举类型。 - Dan Vinton
同意,但我想这里我有一个难得的机会来“重新发明轮子”。只是在寻找最佳方法。客户端代码和国家实现都在我们的控制之下。 - Georgy Bolyuba

1

个人而言,我一直采用数据库方法,主要是因为我已经在数据库中存储了其他信息,所以编写另一个DAO很容易。

但另一种方法可能是将其存储在jar包中的属性文件中?我从未在Java中这样做过,但在iPhone开发中似乎很常见(我目前正在学习这方面的知识)。


不需要是属性文件 - 任何你可以轻松解析的文本文件都可以。 - Jon Skeet
属性文件的查找方法效率如何?有没有一种简单的方法将属性文件转换为Map?在ObjC中,这只需要两行代码左右。 - Paul Tomblin

1

我可能会将文本文件嵌入到我的jar包中。在启动时(或首次使用时)将其加载到内存中:

  • 易于更改(即使是没有编程知识的人也可以)
  • 即使没有进行完整的重新部署,也很容易更新 - 将只有文本文件放在类路径的某个位置
  • 不需要访问数据库

编辑:好吧,如果您需要从代码引用特定国家/地区的数据,则可以选择以下方法之一:

  • 使用枚举方法,这将始终意味着重新部署
  • 使用上述方法,但保留一个国家/地区ID的枚举,然后进行单元测试以确保每个ID都映射在文本文件中。这意味着您可以更改其他数据而无需重新部署,并且非技术人员仍然可以更新数据而无需在各处看到可怕的代码。

最终,这是一个权衡利弊的问题 - 如果上述优点对您不相关(例如,总会有程序员在手边,部署也不是问题),那么枚举就是有意义的。


这里的核心问题不是数据的持久性,而是查找方法(findByName(“USA”))与对枚举特定实例(Country.USA)的依赖。 - Georgy Bolyuba

1

这并不能帮到你,因为它取决于...

- 你打算用这些国家做什么?

你会将它们存储在数据库的其他表中吗?如果添加新的国家,现有数据会发生什么?其他应用程序是否可以访问这些数据?

- 你打算将国家名称翻译成几种语言吗?

- 你的应用程序业务逻辑是否取决于所选择的国家? - 你需要一个 Country 类吗?

等等...

没有更多信息的情况下,我会从一个包含几个国家的枚举开始,并根据需要进行重构...


1
使用数据库表的好处之一是可以放置 外键 约束。这样你的 参照完整性 将始终保持完整。无需像 DanVinton 建议 枚举那样运行集成测试,它永远不会失去同步。
我也不会尝试制作一个通用的枚举表,就像 saw-lau 建议 的那样,主要是因为你会失去干净的外键约束,这是首先将它们放入数据库的主要优势(最好将它们放入文本文件中)。数据库擅长处理大量表格。如果要以某种方式区分它们,请在表格名称前加上“ENUM_”。
应用程序始终可以在启动时间或触发重新加载事件时将它们加载到 Map 中。

编辑:从评论中得知,"当然我会在我的数据库中使用外键约束。但是无论是否在应用程序端使用枚举类型都可以完成"

啊,我在阅读您问题的第二个要点时错过了那一部分。然而,基于DRY原则,我仍然认为最好将它们加载到Map中。否则,当需要维护它的人添加一个新的国家时,他们肯定会在一个地方更新而不是另一个地方,并且在弄清楚他们需要在两个不同的地方更新之前,他们会感到困惑。这是过早优化的情况。在我看来,性能收益微不足道,代价是代码可维护性降低。


当然我会在我的数据库中使用外键约束。但是无论是在应用程序端使用枚举还是不使用枚举都可以实现。 - Georgy Bolyuba
为了遵循DRY原则,我不会为Country表提供任何初始化脚本,并将强制在启动时创建它(例如使用JPA和Hibernate)。 - Georgy Bolyuba
不熟悉JPA。如果它在应用程序启动时创建它,那么我想可以。您不希望一直删除和重新创建表和约束。 - Evan

0

我会从最简单的枚举开始。当国家变化频繁到几乎和我的代码一样时,我会将表格外置,以便可以在不需要重新构建的情况下进行更新。但请注意,当您将其外置时,会引入整个用户界面、测试和文档方面的问题。


我同意从简单的解决方案开始,但我认为将客户端代码从一种方法转换为另一种方法会很困难。 - Georgy Bolyuba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接