Search code examples
sqlhibernatejpahibernate-envers

Integrity constraint violated when creating custom revision entity with hibernate envers


I have an application that uses hibernate envers and has auditing for some of the entities.

I want to add some extra columns in the audit tables and followed the standard instructions that I have found in multiple blogs.

First I created the a custom revision entity:

@Entity
@RevisionEntity(UserRevisionListener.class)
public class UserRevEntity extends DefaultRevisionEntity {
    private String username;

    public String getUsername() { return username; }

    public void setUsername(String username) { this.username = username; }
}

then the revision listener:

public class UserRevisionListener implements RevisionListener {
    @Override
    public void newRevision(Object revisionEntity) {
        UserRevEntity exampleRevEntity = (UserRevEntity) revisionEntity;
        exampleRevEntity.setUsername("TEST");
    }
}

The problem is that when the application saves an entity (let's call it MyEntity) it violates a foreign key constraint on the database.

What I concluded is that up until now envers wrote into the REVINFO table but now by looking at the query that JPA sends to the db I see that it writes to the USERREVTABLE. Because the table that MyEntity is persisted has a foreign key constraint referencing REVINFO it causes the constraint violation.

How can I overcome this issue? Is there any envers annotation to fix this scenario?

Please help, thanks in advance.


Solution

  • The easiest solution is to do this

    @Entity
    @Table(name = "REVINFO")
    @RevisionEntity(UserRevisionListener.class) 
    public class UserRevEntity extends DefaultRevisionEntity {
      ...
    }
    

    If you notice, I have explicitly added a @Table annotation to solve the problem.

    The reason you run into this with a custom revision-entity has a lot to do with entity discovery.

    In the default scenario where you are not providing a custom revision-entity, Envers actually uses a class and manually maps that class as an entity in a Hibernate XML mapping configuration. In that default scenario, we explicitly tell ORM that the backing table for that class is REVINFO.

    When you supply your own custom revision-entity, that entity mapping is actually discovered first by ORM since it is a real entity in your domain model. This means that Envers must inspect the entity mappings that ORM has during bootstrap and if one is found with @RevisionEntity, then we don't submit a Hibernate XML mapping with the default configuration.

    The side effect is that ORM has determined things like table-name, entity-name, column-name, etc of all the mapping data from your custom revision-entity, those are attributes in which we can't technically change at the point this bootstrap happens, that data has already been bound by the normal ORM bootstrap process.

    Therefore, if you want to overlay your custom revision-entity over the default table and simply alter it by adding a new column like you need; you'll need to be explicit in your annotations on the custom entity mapping and force ORM to use a specific table-name or else it will use the default naming strategy policy, which is precisely why you started seeing it use UserRevEntity rather than REVINFO.

    Add @Table(name = "REVINFO") and it will insert into REVINFO like it always has.