【问题标题】:History, Diff and reverts of persisted objects持久对象的历史、差异和恢复
【发布时间】:2014-11-02 14:40:29
【问题描述】:

在 Spring MVC / Spring Data 项目中,我需要实现一种机制来跟踪历史记录、呈现差异并将更改还原为实体对象。

假设我有一个与其他人有这样关系的实体:

@Entity
public Class ModelA{
    @OneToOne(cascade = CascadeType.ALL)
    private ModelB modelB;

    @OneToOne(cascade = CascadeType.ALL)
    private ModelC modelC;
}

我想要更改列表,能够比较和还原它们。我知道使用 Ruby 有一些库可以提供这种功能,但我不知道 Java 中是否存在这样的东西。

Spring 有一个 historiography API 并且 Hibernate Envers 已被合并到核心功能中,尽管我仍然找不到一个简单的示例或一些如何实现它的指导。

如果相关,使用的数据库是 PostgreSQL 和 Oracle 11g,但我想保持它独立于数据库。

【问题讨论】:

  • 我曾经看到过使用历史支持存储数据的实现。在 PLSQL 上,它在没有任何框架的情况下被白色化,这太可怕了。主要思想 - 你有 PK = 实体 ID + 修订时间。所有选择查询都可以返回最后一个修订版(默认情况下)或某个时间的修订版。这种方法的缺点 - 编写代码和查询非常困难,sql查询速度慢并且失去了FK能力。我知道,这不是对您问题的回答,但也许这对您有所帮助。

标签: java spring hibernate jpa spring-data


【解决方案1】:

请改用 Enver 和 Auditions

【讨论】:

  • 感谢您的回答,但我想要更多详细信息,我已经看过 API,但我正在寻找一个简单的示例。保存对象的历史似乎很容易,但管理修订、恢复和差异却不是。
  • 这个例子没有展示如何管理修订,只是为了保存和检索。
  • 您想保存过去更改过的版本吗?这是不允许的,因为对 n-m 关系有奇怪的行为!您只能读取修订并将其状态合并回数据库。这是一项非常个人化和方便的工作,没有(也永远不会)一个框架!
【解决方案2】:

Christian Bauer(Hibernate 提交者和 Hibernate in Action 和 Java Persistence with Hibernate 的作者)在this post 中提供了一个非常有趣的方法。

您创建一个 HISTORY 表:

create table ITEM (
    ITEM_ID    NUMBER(19) NOT NULL,
    DESC       VARCHAR(255) NOT NULL,
    PRICE      NUMBER(19,2) NOT NULL,
    PRIMARY KEY(ITEM_ID)
)

create table ITEM_HISTORY (
    ITEM_ID    NUMBER(19) NOT NULL,
    DESC       VARCHAR(255) NOT NULL,
    PRICE      NUMBER(19,2) NOT NULL,
    VERSION    NUMBER(10) NOT NULL,
    PRIMARY KEY(ITEM_ID, VERSION)
)

然后您将实体映射到视图:

create or replace view ITEM_VERSIONED (ITEM_ID, VERSION, DESC, PRICE) as
    select I.ITEM_ID as ITEM_ID,
        (select max(IH.VERSION)
            from ITEM_HISTORY HI
            where HI.ITEM_ID = I.ITEM_ID) as VERSION,
        I.DESC as DESC,
        I.PRICE as PRICE
    from   ITEM I

DML 语句由 PostgreSQL 和 Oracle 支持的 INSTEAD OF TRIGGERS 解析:

create or replace trigger ITEM_INSERT
    instead of insert on ITEM_VERSIONED begin
    
    insert into ITEM(ITEM_ID, DESC, PRICE)
           values (:n.ITEM_ID, :n.DESC, :n.PRICE);
           
    insert into ITEM_HISTORY(ITEM_ID, DESC, PRICE, VERSION)
           values (:n.ITEM_ID, :n.DESC, :n.PRICE, :n.VERSION);
end;

create or replace trigger ITEM_UPDATE
    instead of update on ITEM_VERSIONED begin
    
    update ITEM set
            DESC = :n.DESC,
            PRICE = :n.PRICE,
           where
            ITEM_ID = :n.ITEM_ID;
           
    insert into ITEM_HISTORY(ITEM_ID, DESC, PRICE, VERSION)
           values (:n.ITEM_ID, :n.DESC, :n.PRICE, :n.VERSION);
end;

这甚至适用于可能不使用 Hibernate 但它们在同一个数据库上运行的其他应用程序。

【讨论】:

  • 我看过上面提到的帖子,我倾向于避免需要数据库逻辑的解决方案。
【解决方案3】:

如果我理解得很好,你问的是某种Memento pattern 来管理一些受历史跟踪影响的实体。

在这种情况下,我的建议是配置 Spring Data 以支持第二个数据库(即跟踪数据库),您将在其中插入您感兴趣的实体的历史记录。 然后您可以创建一个新注释(可能使用 AspectJ)并将其应用到您的 DAO(例如,应用到您的存储库,如果您正在使用它们)。这样,每次对被跟踪的类(或者更准确地说,在管理要跟踪的类的 dao/存储库)上进行 CRUD 操作时,您都会在跟踪数据库中进行“插入”,以存储刚刚发生的更改发生了。

我可以给您this reference,它与您的需求不完全匹配,但可能会支持您找到解决您问题的一种解决方案。

【讨论】:

  • 这显然是目前最好的选择,至少它给出了明确的指导。尽管没有给出完整的例子,但由于最好的方法,我会接受这个答案。
猜你喜欢
  • 2022-06-14
  • 1970-01-01
  • 2012-05-12
  • 2023-04-05
  • 2013-05-04
  • 2023-04-08
  • 1970-01-01
  • 2018-06-08
  • 1970-01-01
相关资源
最近更新 更多