Hibernate Envers:@Audited on subclass

时间:2015-12-15 19:08:32

标签: java hibernate hibernate-envers

我有一个经典的继承持久性实体 Parent Child ,其中 Child 扩展 Parent 。类是抽象的,而 Child 不是。

我想审核 Child 此实体在我的控制之下,而 Parent 则不在。此外,它还有许多其他不需要审计的子类。整个层次结构的继承策略是 JOINED

所以我使用 @Audited 注释 Child ,另外注释 @AuditOverride(forClass = Parent.class)

我得到的是这个错误:

  

“org.hibernate.MappingException:实体'Child'已经过审核,但其超类:'Parent'不是。”

顺便说一下,我正在使用envers 4.0.1.Final version。

有谁知道如何实现这一目标? 我已尝试在 Child 类中删除 @Audited ,删除 @AuditOverride ,在中使用已弃用的 auditParents @Audited 注释,但似乎没有任何效果。

这是父实体:

@Entity
@Table(name = "parent")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type")
public class Parent {
    public Parent() {
        super();
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "base_id", unique = true, nullable = false)
    private Integer baseId;

    @Column(name = "base_field")
    private String baseField;

    @Column(name = "type")
    private String type;

    // getters and setters
}

这是我的孩子实体:

@Entity
@Table(name = "child")
@DiscriminatorValue("CHILD")
@Audited
@AuditOverride(forClass = Parent.class)
public class Child extends Parent {
    public Child() {
        super();
    }

    @Column(name = "child_field")
    private String childField;

    // getters and setters
}

这是实体:

CREATE  TABLE `REVINFO` (
  `REV` BIGINT NOT NULL AUTO_INCREMENT,
  `REVTSTMP` BIGINT NULL ,
  PRIMARY KEY (`REV`)
);

CREATE TABLE `parent` (
  `base_id` int(11) NOT NULL AUTO_INCREMENT,
  `base_field` varchar(45) DEFAULT NULL,
  `type` varchar(45) NOT NULL,
  PRIMARY KEY (`base_id`)
);

CREATE TABLE `child` (
  `base_id` int(11) NOT NULL AUTO_INCREMENT,
  `child_field` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`base_id`)
);

CREATE TABLE `child_AUD` (
  `base_id` int(11) NOT NULL,
  `REV` BIGINT NOT NULL,
  `REVTYPE` tinyint(2) DEFAULT NULL,
  `child_field` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`base_id`,`REV`)
);

这是一个测试用例:

public class EnversInheritanceTest extends AbstractJUnit4SpringContextTests {

    @Inject
    private EntityManagerFactory entityManagerFactory;

    private EntityManager entityManager;

    @Test
    public void inheritanceTest() {

        this.entityManager = this.entityManagerFactory.createEntityManager();

        Child child = this.createChild();
        this.saveChild(child);

        this.modifyChild(child);
        this.saveChild(child);

        Assert.assertNotNull(child.getBaseId());
        Assert.assertNotNull(this.getOriginalRevision(child.getBaseId()));

        Child original = this.getOriginalChild(child.getBaseId());

        Assert.assertNotNull(original);
        Assert.assertEquals("child", original.getChildField());

    }

    private Child createChild() {
        Child child = new Child();
        child.setBaseField("base");
        child.setChildField("child");
        child.setType("CHILD");
        return child;
    }

    private void saveChild(Child child) {
        this.entityManager.getTransaction().begin();
        this.entityManager.persist(child);
        // We need to commit in order to trigger Envers magic
        this.entityManager.getTransaction().commit();
    }

    private void modifyChild(Child child) {
        child.setBaseField("foo");
        child.setChildField("bar");
    }

    public Child getOriginalChild(Serializable id) {
        Object queryResult = this.getAuditReader().createQuery()
                .forEntitiesAtRevision(Child.class, this.getOriginalRevision(id))
                .add(AuditEntity.id().eq(id))
                .getSingleResult();
        return (Child) queryResult;
    }

    private Number getOriginalRevision(Serializable id) {
        AuditProjection minRevNumberAuditProjection = AuditEntity.revisionNumber().min();
        Number revision = (Number) this.getAuditReader().createQuery()
                .forRevisionsOfEntity(Child.class, false, false)
                .add(AuditEntity.id().eq(id))
                .addProjection(minRevNumberAuditProjection)
                .getSingleResult();

        return revision;
    }

    private AuditReader getAuditReader() {
        return AuditReaderFactory.get(this.entityManager);
    }

}

最后这是一个土豆:

Potato image

提前谢谢!

2 个答案:

答案 0 :(得分:4)

非常好的问题。 2013年jboss开发人员论坛上有same discussion。答案来自the founder and project lead of Hibernate Enver

  

您必须以某种方式审核超类。目前   除注释外,没有其他方法可以指定此类元数据。

在同一讨论树中,根据父类应该也注释的事实,建议在运行时注释它们。但是这个决定看起来很难看,并不适合你的情况:你可以手动注释父类。

作为解决方法,如果您不希望审核父类,则可以尝试创建基本抽象MappedSuperClass,它基本上与Parent相同,而Parent将是只是它的后代,然后再次为@AuditOverride类尝试Child。它可能会跳过"跳过"审核Parent课程并为Child执行此操作。

答案 1 :(得分:0)

尝试使用@Audited批注标记父类和子类 但对于父类,请添加@Audited(targetAuditMode = NOT_AUDITED)