Skip to content

Custom RevisionEntity class fails to provide RevisionNumber and RevisionTimestamp #250

Closed
@androidbod

Description

@androidbod

I have an issue with Spring Data Envers when using a Custom RevisionEntity class for storing revision information in Hibernate Envers.

Here is my Custom RevisionEntity - works well with an H2 Database for testing purposes - up to Spring Boot 2.2.7 it worked flawlessly.

package <TBD>;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;

import javax.persistence.*;

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "audit_trail" /*, schema = "history"*/)
@RevisionEntity
@GenericGenerator(name = "audit_trail_sequence",
        strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
            @Parameter(name = "sequence_name",value = "audit_trail_sequence"),
            @Parameter(name = "initial_value",value = "1"),
            @Parameter(name = "increment_size",value = "1")
        })
public class CustomRevisionEntity
{

    // =========================== Class Variables ===========================
    // =============================  Variables  =============================
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "audit_trail_sequence")
    @RevisionNumber
    private Long id;

    @RevisionTimestamp
    private Long timestamp;
}

I'm using a custom revision entity to allow for more than int limited number of revisions - hence I'm not using Hibernate's DefaultRevisionEntity - an extension of this class would not allow for changing the @RevisionNumber field type.

For Spring Boot 2.3.X (tested with 2.3.1 and 2.3.2):
Spring Data Envers' EnversRevisionRepositoryImpl uses the AnnotationRevisionMetadata class to extract and provide revision metadata for any revision loaded from the database. Through debugging I found out that, when aAnnotationRevisionMetadata instance is constructed for a revision, the @RevisionNumber and the @RevisionTimestamp field values are available, however the static methods constructing the respective Lazy suppliers will only provide appropriate Supplier functions for this information.

Somehow in the timespan from construction of these Lazy suppliers to actual usage of these suppliers, the information is "lost" along the way - currently I assume it has something to do with the fact, that my CustomRevisionEntity is in fact a HibernateProxy of the actual CustomRevisionEntity instance.

Generally this seems to have something to do with the deferred loading that is happening through the respectively constructed Lazy suppliers operating on these HibernateProxy objects which are liable to "loose" some information in the previously described timespan (possibly different Hibernate Sessions for Entity and Revision Infomation loading ? - just a thought).

I've tried wrapping the respective EnversRevisionRepositoryImpl calls into transactional business methods, but TransationContext or not - the result remains unchanged - the revision number and timestamp are not available for use after the EnversRevisionRepositoryImpl getRevision information methods return their respective result.

One possible solution that occured to me would be to call Hibernate.unproxy on the entity constructor parameter of the AnnotationRevisionMetadata from EnversRevisionRepositoryImpl.createRevisionMetadata.

		RevisionMetadata<?> createRevisionMetadata() {

			return metadata instanceof DefaultRevisionEntity //
					? new DefaultRevisionMetadata((DefaultRevisionEntity) metadata, revisionType) //
					: new AnnotationRevisionMetadata<>(Hibernate.unproxy(metadata), RevisionNumber.class, RevisionTimestamp.class, revisionType);
		}

I've tested that idea - it works - though I'm unsure if there are any performance related side effects from that solution.
I'm also considering whether or not the DefaultRevisionEntity provided by Envers could be subject to the same issue of being returned by Hibernate as a HibernateProxy instance.

If there are any questions, please contact me through Github.

Thank you

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions