Search code examples
hibernatehibernate-envershibernate-6.x

How to revert the ddl of hibernate envers primary keys to the same behavious as version 5


I'm in the process of upgrading to hibernate 6. We use envers extensively, on some services with complex domains and lots of data. We generate our database migration scripts with liquibase.

I was surprised to see that liquibase generates A LOT of database changesets after upgrading ... all of those changes related to "_aud" tables, dropping and recreating the primary key : in hibernate 5 the audit table's order of fields in the primary key was "id, rev", in hibernate 6 it's "rev, id".

Liquibase seing this difference generates the changesets trying to resolve all of this, but it's not complete ... (dropping a primary key used in a foreign key won't work)... meaning that a 2000+ lines file needs to be manually fixed ...

This is a lot of work to fix something that works, the change will take A LONG time to fix but also to execute in prod, and there may be an impact on performances that I can not measure ... so I would really like to postpone this to a later time.

Is there a way to revert to the old behaviour of hibernate 5 like a config or something ? (I looked but found nothing)


here is a simple entity used to illustrate the behaviour :

@Entity
@Audited
public class MyEntity {

    @Id
    private Long id;

    private String name;
}

And here is what is generated in hibernate 5.6.15.Final :

  <changeSet author="snussbaumer (generated)" id="1693555682159-2">
    <createTable tableName="my_entity_aud">
      <column name="id" type="BIGINT">
        <constraints nullable="false" primaryKey="true" primaryKeyName="my_entity_audPK" />
      </column>
      <column name="rev" type="INT">
        <constraints nullable="false" primaryKey="true" primaryKeyName="my_entity_audPK" />
      </column>
      <column name="revtype" type="TINYINT" />
      <column name="name" type="VARCHAR(255)" />
    </createTable>
  </changeSet>

and that's with hibernate 6.2.7.Final :

  <changeSet author="snussbaumer (generated)" id="1693555682159-2">
    <createTable tableName="my_entity_aud">
      <column name="id" type="BIGINT">
        <constraints nullable="false" />
      </column>
      <column name="rev" type="INT">
        <constraints nullable="false" />
      </column>
      <column name="revtype" type="TINYINT" />
      <column name="name" type="VARCHAR(255)" />
    </createTable>
  </changeSet>
  <changeSet author="snussbaumer (generated)" id="1693555682159-4">
    <addPrimaryKey columnNames="rev, id" constraintName="my_entity_audPK" tableName="my_entity_aud" />
  </changeSet>

Solution

  • So after quite some digging, the "problem" comes from this rather large commit : https://github.com/hibernate/hibernate-orm/commit/fb882f56f313e09889c362952e489ec26d527765

    Now keys are sorted by name, and that's it ... there is no config to change this. This is really missing any consideration to perfs (but maybe that's left to the user of the library to do).

    Some workarounds :

    • play with the "org.hibernate.envers.revisionFieldName" property. Comparison is case sensitive. Using "rev" in lower case (instead of the default uppercase "REV") did some of the work : I get "id, rev", instead of "REV, id". This is a progress for simple schemes but for collection tables this puts the rev anywhere between other columns when it should be first, so not sufficient but for the simplest cases.
    • if using liquibase implement an ObjectChangeFilter that drops changes that only apply to the order of primary key columns of audited tables. This solves the problem, but new audit entities' primary keys column order will be different than the order of those created with hibernate 5
    • if using liquibase extend the liquibase.database.Database that you're using, an reorder the columns in buildMetadataFromPath after calling the parent buildMetadataFromPath.