我对这个话题很感兴趣。我已经工作了几年,致力于开发使用这些模式的应用程序,这个想法来自于我们的一篇德国文凭论文。
我不知道“DAO Fusion”框架,它们提供了有趣的信息和链接,感谢您提供这些信息。尤其是图案页 http://opensource.anasoft.com/daofusion-site/reference/bitemporal-pattern.html和方面页面 http://opensource.anasoft.com/daofusion-site/technote/temporal-aspects.html太棒了!
对于你的问题:不,我不能指出其他网站、示例或框架。恐怕您必须使用 DAO Fusion 框架或自己实现此功能。您必须区分您真正需要哪种功能。从“DAO Fusion”框架来看:“有效时间”和“记录时间”是否都需要?记录更改应用于数据库时的时间状态(通常用于审核问题)、更改在现实生活中发生或在现实生活中有效(由应用程序使用)时的有效时间状态,这可能与记录时间不同。在大多数情况下,一维就足够了,不需要第二维。
无论如何,时间功能会对您的数据库产生影响。正如你所说:“现在他们的主键包括有效期”。那么如何对实体的身份进行建模呢?我更喜欢使用代理键 http://en.wikipedia.org/wiki/Surrogate_key。在这种情况下,这意味着:
- 实体的一个 id
- 数据库中对象的一个 id(行)
- 时间列
表的主键是对象 ID。每个实体在表中都有一个或多个 (1-n) 条目,由对象 id 标识。表之间的链接基于实体 ID。由于时间条目会增加数据量,因此标准关系不起作用。标准的 1-n 关系可能会变成 x*1-y*n 关系。
你如何解决这个问题?标准方法是引入映射表,但这不是一种自然的方法。仅为了编辑一张表(例如,发生居住地变更),您还必须更新/插入映射表,这对于每个程序员来说都是陌生的。
另一种方法是不使用映射表。在这种情况下,您不能使用引用完整性和外键,每个表都是独立运行的,从一个表到其他表的链接必须手动实现,而不是使用 JPA 功能。
初始化数据库对象的功能应该在对象内(如在 DAO Fusion 框架中)。我不会把它放在服务中。是否将其放入 DAO 或使用 Active Record 模式取决于您。
我知道我的答案并没有为您提供“随时可用”的框架。你处在一个非常复杂的领域,从我的经验资源到这个使用场景都很难找到。谢谢你的提问!但无论如何,我希望对您的设计有所帮助。
在这个答案中,您将找到参考书“用 SQL 开发面向时间的数据库应用程序”,请参阅https://stackoverflow.com/a/800516/734687 https://stackoverflow.com/a/800516/734687
更新:示例
- 问题:假设我有一个 PERSON 表,它有一个代理键,它是一个名为“id”的字段。此时每个引用表都将具有该“ID”作为外键约束。如果我现在添加临时列,我必须将主键更改为“id+from_date+to_date”。在更改主键之前,我必须首先将每个引用表的每个外来约束删除到该引用表(Person)。我对吗?我相信这就是你所说的代理键的意思。 ID 是可以由序列生成的生成密钥。 Person表的业务键是SSN。
- 答:不完全是。 SSN 将是一个自然密钥,我不将其用于 objcet 身份。另外“id+from_date+to_date”将是复合键 http://en.wikipedia.org/wiki/Composite_key,我也会避免。如果你看一下example http://opensource.anasoft.com/daofusion-site/technote/temporal-aspects.html#Bitemporal_example您将有两个表:人员和住所,对于我们的示例,假设我们与外键住所具有 1-n 关系。
现在我们在每个表上添加时间字段。是的,我们放弃了每个外键约束。 Person 将获得 2 个 ID,一个用于标识行的 ID(称为 ROW_ID),一个用于标识人员本身的 ID(称为 ENTIDY_ID)以及该 id 上的索引。对人来说也是如此。当然,您的方法也可以工作,但在这种情况下,您将进行更改 ROW_ID 的操作(当您关闭时间间隔时),我会避免这种情况。
为了延长example http://opensource.anasoft.com/daofusion-site/technote/temporal-aspects.html#Bitemporal_example根据上述假设实施(2 个表,1-n):
-
显示数据库中所有条目的查询(包括所有有效性信息和记录 - 又名技术 - 信息):
SELECT * FROM Person p, Residence r
WHERE p.ENTITY_ID = r.FK_ENTITY_ID_PERSON // JOIN
-
隐藏记录(又名技术信息)的查询。这显示了实体的所有有效更改。
SELECT * FROM Person p, Residence r
WHERE p.ENTITY_ID = r.FK_ENTITY_ID_PERSON AND
p.recordTo=[infinity] and r.recordTo=[infinity] // only current technical state
-
显示实际值的查询。
SELECT * FROM Person p, Residence r
WHERE p.ENTITY_ID = r.FK_ENTITY_ID_PERSON AND
p.recordTo=[infinity] and r.recordTo=[infinity] AND
p.validFrom <= [now] AND p.validTo > [now] AND // only current valid state person
r.validFrom <= [now] AND r.validTo > [now] // only current valid state residence
如您所见,我从不使用 ROW_ID。将 [now] 替换为时间戳以返回过去。
更新以反映您的更新
我推荐以下数据模型:
引入一个“PlaysInTeam”表:
- ID
- ID Team(团队的外键)
- ID Player(播放器的外键)
- 有效日期
- ValidTo
当您列出球队的球员时,您必须查询关系有效的日期,并且必须位于 [ValidFrom, ValidTo)
为了让团队变得临时化,我有两种方法;
方法一:
引入一个“季节”表,该表对季节的有效性进行建模
- ID
- 季节名称(例如 Summer 2011)
- 从(也许没有必要,因为每个人都知道季节是什么时候)
- 到(也许没有必要,因为每个人都知道季节是什么时候)
拆分团队表。您将拥有属于球队且与时间无关的字段(姓名、地址……)以及与赛季时间相关的字段(胜利、失败……)。在这种情况下,我会使用 Team 和 TeamInSeason。 PlaysInTeam 可以链接到 TeamInSeason 而不是 Team(必须考虑 - 我会让它指向 Team)
赛季球队
- ID
- ID Team
- 身份季节
- Win
- Loss
- ...
方法2:
不要明确地模拟季节。拆分团队表。您将拥有属于团队且与时间无关的字段(姓名、地址等)以及与时间相关的字段(获胜、失败等)。在这种情况下,我会使用 Team 和 TeamInterval。 TeamInterval 将具有表示间隔的字段“from”和“to”。 PlaysInTeam 可以链接到 TeamInterval 而不是 Team(我会将其链接到 Team)
团队间隔
- ID
- ID Team
- From
- To
- Win
- Loss
- ...
在这两种方法中:如果您不需要为没有时间相关字段的单独团队表,请不要拆分。