审计:对子修改的父实体修订(Javers/Envers/... + Hibernate)

Posted

技术标签:

【中文标题】审计:对子修改的父实体修订(Javers/Envers/... + Hibernate)【英文标题】:Audit: Parent entity revision on child modification (Javers/Envers/... + Hibernate) 【发布时间】:2019-08-20 23:48:07 【问题描述】:

我的应用程序中有一种父对象需要进行版本控制(审核),我的意思是:每次对其进行更改,或者对其任何 n 深子对象进行持久化时,都会进行新修订必须创建。

@Entity class Document 
    @Id String name;
    // some props ...
    @OneToMany List<Page> pages;
    // getters and setters ...


@Entity class Page 
    @Id Long number;
    @ManyToOne Document document;
    // some props ...
    String header;
    // getters and setters ...

在上面的示例中,如果我要更改页面的标题,则应为整个 Document 实体保留新的修订:

// retrieve document
Document document = documentRepository.findById(myBookId);
// change something on first found page
document.getPages().iterator().next().setHeader("Hello World");
// persist the document
documentRepository.save(document);
// new revision of the document!

我正在考虑使用Javers 或Envers,但似乎他们都做不到。

你们中有人知道如何在这些库上做类似的事情吗?或者知道我可以为此目的使用的任何库吗?

【问题讨论】:

哪一方是所有者,因为您提供的映射没有指定一方是该关系的反向(非所有者)。 在本例中为PageOneToMany/ManyToOne 双向的 one 侧怎么可能是所有者? 【参考方案1】:

如果您将Page 类映射为ValueObject,然后如果您使用影子查询,您可以在 Javers 中实现它。你也应该一直提交Document

    @Entity
    class Document 
        @Id String name
        @OneToMany List<Page> pages

        @Override
        String toString() 
            "Document $name, pages:$pages"
        
    

    @org.javers.core.metamodel.annotation.ValueObject
    @Entity
    @ToString
    class Page 
        @Id Long number
        @ManyToOne Document document
        String header

        @Override
        String toString() 
            "Page $number, $header"
        
    

    def "should treat Document as aggregate"()
      given:
      def javers = JaversBuilder.javers().build()

      def doc = new Document(name:"1", pages: [new Page(header:"a"), new Page(header:"b")])
      javers.commit("author", doc)

      when:
      doc = new Document(name:"1", pages: [new Page(header:"a"), new Page(header:"ccc")])
      javers.commit("author", doc)

      def shadows = javers.findShadows(QueryBuilder.byClass(Document).build())

      then:
      shadows

      shadows.forEachs -> println(s.commitMetadata.commitDate.toString() + " " + s.get())
    

输出:

23:25:47.297 [main] INFO  org.javers.core.Javers - Commit(id:1.0, snapshots:3, author:author, changes - NewObject:3), done in 56 millis (diff:45, persist:11)
23:25:47.315 [main] INFO  org.javers.core.Javers - Commit(id:2.0, snapshots:1, author:author, changes - ValueChange:1), done in 17 millis (diff:17, persist:0)
23:25:47.337 [main] DEBUG org.javers.JQL - SHALLOW query: 4 snapshots loaded (entities: 1, valueObjects: 3)
23:25:47.358 [main] DEBUG org.javers.JQL - queryForShadows executed: 
2019-04-08T23:25:47.298618 Document 1, pages:[Page null, a, Page null, ccc]
2019-04-08T23:25:47.275570 Document 1, pages:[Page null, a, Page null, b]

【讨论】:

我不熟悉groovy语法,你的意思是我不能更改一堆页面并且只在最后提交?

以上是关于审计:对子修改的父实体修订(Javers/Envers/... + Hibernate)的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate Envers - 一起审计多个实体

在 envers 中创建多个自定义修订表

有没有办法在测试中选择性地禁用对实体修改的审计?

人工智能将完成审计的前99步工作(修订)

symfony2 对子实体的验证防止编辑父实体

NestJS 中的 TypeORM - 如何在实体中获取 MySQL 嵌套对象并对子关系进行“位置”查询?