0

I joined a project where they implemented Springboot v.2.0.5, Hibernate + Envers v5.3.7. My job is now to generate logs for a nice little endpoint. When I try to do this utilizing the auditreader through the following code:

public List<Drug> getDrugAudByID(long drugId) {
        try (Session session = hibernateSession.openSession()) {
            AuditReader auditReader = AuditReaderFactory.get(session);
            List<Number> revisions = AuditReaderFactory.get(session).getRevisions(Drug.class, drugId);
            List<Drug> drugVersions = new ArrayList<>();

            for (Number rev : revisions) {
                Drug drug = auditReader.find(Drug.class, drugId, rev);
                drugVersions.add(drug);
            }

it works pretty well for normal elements such as id, name etc. Problem is, that every join column is not correctly initialized and throw

'org.hibernate.ObjectNotFoundException' exception. Cannot evaluate classPath.class$HibernateProxy$YUt19z4c.toString()

A sample of the entity looks like this:

@Entity
@Audited
@Table(name = "drugs")
@Check(constraints = "id <> genericDrug")
public class Drug {
    public static String genericPrefix = "73019";
    public static String specificPrefix = "73010";
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "drugId", unique = true)
    private BigDecimal drugId;

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

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

    @OneToOne()
    @JoinColumn(name = "form", referencedColumnName = "id")
    private FormType form;

I have not implemented any custom RevisionEntities, and the tables revision entities where generated manually by hibernate. Here are the DDLs.

create table REVINFO
(
    REV      int auto_increment
        primary key,
    REVTSTMP bigint null
);
#Only a sample of the true DLL, but encapsulates the problematic form Field
create table drugs_AUD
(
    REV                int                  not null,
    REVTYPE            tinyint              null,
    id                 bigint               not null,
    drugId             decimal(11)          null,
    partType           varchar(2)           null,
    name               varchar(30)          null,
    form               bigint               null,
    substancesComplete bit     default b'0' null,
    primary key (id, REV),
    constraint fk_drugs_REV
        foreign key (REV) references REVINFO (REV),

);

I tried to follow the guides from: https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#envers-schema

Similar question was asked in: Hibernate Envers: Initializing Envers Proxies but using a modelMapper did not solve the issue for me.

Otherwise I searched the whole internet to day, so any help will be appreciated.

1 Answers1

1

I found the reason why.

Data was inserted using SQL-dumps at the start of the project meaning it bypassed hibernate and normal client site execution. The outcome of that is the AUD tables for forms and other entities are out of date.

As the revision history was not previously used my solution was:

  1. Truncate all AUD tables
  2. Truncate REVINFO
  3. Reinsert values into AUD and REVINFO. Do this in the right order in respect to foreign keys.
  4. Initiate hibernate proxies using https://candrews.integralblue.com/2009/03/hibernate-deep-deproxy/

Be aware that this will drop all previously recorded AUD history.

Showing an example for one AUD table below

SET FOREIGN_KEY_CHECKS=0;

#Step 1 truncate AUD tables. Example of truncating a single AUD table
TRUNCATE TABLE drugs_AUD;

#Step 2 truncate REVINFO
TRUNCATE REVINFO;

SET FOREIGN_KEY_CHECKS=1;

#Step 3 reinsert values into REVINFO and AUD tables. 
INSERT INTO REVINFO (REV, REVTSTMP) VALUES (1, UNIX_TIMESTAMP());

#An example of a single AUD table
INSERT INTO drugs_AUD (REV, REVTYPE, id, drugId, partType, name, form, substancesComplete)
SELECT 1, 0, id, drugId, partType, name, form, substancesComplete from drugs;
  • That was going to be my very first question is whether or not the data in the tables may have been inserted incorrectly or via some external means. Glad you found the culprit and resolved it. – Naros Dec 03 '19 at 16:44
  • Yes. Another important discovery I made is, that any unit of work such as `updates` / `deletions` to one or more `entities` need to be done in the same **open** `transaction` for `Envers` to record it. For this project, an entity was fetched, changes made, then a `transaction` was `opened` and the `entity` saved. Therefore, `Envers` did not know what was changed. – Jeppe Gravgaard Dec 05 '19 at 08:21
  • What you describe is called a detached entity, I believe there are explicit tests for this in the test suite. Can you try to reproduce this using templates at https://github.com/hibernate/hibernate-test-case-templates and open a bug with Envers if you can reproduce it? – Naros Dec 06 '19 at 00:31