Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-17612 DefaultRevisionEntity: Illegal argument on static metamodel field injection #9158

Merged
merged 3 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,16 @@ Only used if the `ValidityAuditStrategy` is used, and `org.hibernate.envers.audi
When set to `true`, the legacy mapping behavior is used such that the revision end timestamp is only maintained in the root entity audit table.
When set to `false`, the revision end timestamp is maintained in both the root entity and joined subclass audit tables; allowing the potential to apply database partitioning to the joined subclass tables just like the root entity audit tables.

[[envers-config-native-id]]
`*org.hibernate.envers.use_revision_entity_with_native_id*` (default: `true` )::
Boolean flag that determines the strategy of revision number generation.
Default implementation of revision entity uses native identifier generator.
+
If the current database engine does not support identity columns, users are advised to set this property to false.
+
In this case revision numbers are created by preconfigured `org.hibernate.id.enhanced.SequenceStyleGenerator`.
See: `org.hibernate.envers.DefaultRevisionEntity` and `org.hibernate.envers.enhanced.SequenceIdRevisionEntity`.
In this case revision numbers are created by a preconfigured `org.hibernate.id.enhanced.SequenceStyleGenerator`.

[[envers-config-track-entities]]
`*org.hibernate.envers.track_entities_changed_in_revision*` (default: `false` )::
Should entity types, that have been modified during each revision, be tracked.
The default implementation creates `REVCHANGES` table that stores entity names of modified persistent objects.
Expand Down Expand Up @@ -484,18 +485,49 @@ Either a `long/Long` or `java.util.Date` value representing the instant at which
When using a `java.util.Date`, instead of a `long/Long` for the revision timestamp, take care not to store it to a column data type which will lose precision.

Envers handles this information as an entity.
By default it uses its own internal class to act as the entity, mapped to the `REVINFO` table.
You can, however, supply your own approach to collecting this information which might be useful to capture additional details such as who made a change

[[envers-default-revision-entity]]
==== Default Revision Entity

By default, Envers uses its own internal class to act as the entity, mapped to the `REVINFO` table.
The entity type that's used depends on a couple configuration properties: <<envers-config-native-id,native identifiers>> and <<envers-config-track-entities, entity tracking>>. Here is a table showing the entity type used based on the configuration values:
[cols="1,1,1"]
|===
|
| native-id `false`
| native-id `true`

| track-entities `false`
| `org.hibernate.envers.DefaultRevisionEntity`
| `org.hibernate.envers.enhanced.SequenceIdRevisionEntity`

| track-entities `true`
| `org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity`
| `org.hibernate.envers.enhanced.SequenceIdTrackingModifiedEntitiesRevisionEntity`
|===

[[envers-custom-revision-entity]]
==== Custom Revision Entity

You can also supply your own approach to collecting this information which might be useful to capture additional details such as who made a change
or the IP address from which the request came.
There are two things you need to make this work:

. First, you will need to tell Envers about the entity you wish to use.
Your entity must use the `@org.hibernate.envers.RevisionEntity` annotation.
It must define the two attributes described above annotated with `@org.hibernate.envers.RevisionNumber` and `@org.hibernate.envers.RevisionTimestamp`, respectively.
You can extend from `org.hibernate.envers.DefaultRevisionEntity`, if you wish, to inherit all these required behaviors.
You can extend from any of the revision mapped superclass types, if you wish, to inherit all these required behaviors:

org.hibernate.envers.RevisionMapping
org.hibernate.envers.TrackingModifiedEntitiesRevisionMapping
org.hibernate.envers.enhanced.SequenceIdRevisionMapping
org.hibernate.envers.enhanced.SequenceIdTrackingModifiedEntitiesRevisionMapping

+
Simply add the custom revision entity as you do your normal entities and Envers will *find it*.
+
To understand which mapping you should extend based on configuration see the <<envers-default-revision-entity,default revision entity>> paragraph.
+
NOTE: It is an error for there to be multiple entities marked as `@org.hibernate.envers.RevisionEntity`.

. Second, you need to tell Envers how to create instances of your revision entity which is handled by the
Expand Down Expand Up @@ -1003,7 +1035,7 @@ If true, the result of the query will be a list of entities (which changed at re
If false, the result will be a list of three element arrays:

* the first element will be the changed entity instance.
* the second will be an entity containing revision data (if no custom entity is used, this will be an instance of `DefaultRevisionEntity`).
* the second will be an entity containing revision data (if no custom entity is used, this will be an instance of the <<envers-default-revision-entity,default revision entity type>>).
* the third will be the type of the revision (one of the values of the `RevisionType` enumeration: `ADD`, `MOD`, `DEL`).

`selectDeletedEntities`:: The second parameter specifies if revisions,
Expand Down Expand Up @@ -1330,17 +1362,31 @@ Here is a simple example:
[source,java]
----
AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity( DefaultRevisionEntity.class, true )
.forRevisionsOfEntity( Customer.class, true )
.add( AuditEntity.revisionNumber().between( 1, 25 ) );
----

This query will return all revision information entities for revisions between 1 and 25 including those which are
This query will return all information for revisions between 1 and 25 including those which are
related to deletions. If deletions are not of interest, you would pass `false` as the second argument.

Note that this query uses the `DefaultRevisionEntity` class type. The class provided will vary depending on the
configuration properties used to configure Envers or if you supply your own revision entity. Typically users who
will use this API will likely be providing a custom revision entity implementation to obtain custom information
being maintained per revision.
Note that this query produces `@RevisionEntity` instances. The obtained instance type will vary depending on the
configuration properties used to configure Envers, like showed in <<envers-default-revision-entity,this paragraph>>,
or if you supply your own revision entity.

[[envers-querying-revision-info]]
=== Directly querying revision information

You can also directly query all revision information available on the database by writing HQL or Criteria queries
which select from the revision entity used by your application. For example:

[source,java]
----
List<DefaultRevisionEntity> resultList = session.createQuery( "from DefaultRevisionEntity where id = 1", DefaultRevisionEntity.class ).getResultList();
----

This query will return all revision entity information for revision numbers equal to 1 (the first revision of each entity).
Often, users who will take advantage of this functionality will be providing a custom revision entity implementation to
obtain additional information being maintained per revision.

[[envers-conditional-auditing]]
=== Conditional auditing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ private void bindBasicEntityValues(
entityDescriptor.setEntityName( entitySource.getEntityNamingSource().getEntityName() );
entityDescriptor.setJpaEntityName( entitySource.getEntityNamingSource().getJpaEntityName() );
entityDescriptor.setClassName( entitySource.getEntityNamingSource().getClassName() );
if ( entityDescriptor.getJpaEntityName() != null && entityDescriptor.getClassName() != null ) {
metadataBuildingContext.getMetadataCollector()
.addImport( entityDescriptor.getJpaEntityName(), entityDescriptor.getClassName() );
}

entityDescriptor.setDiscriminatorValue(
entitySource.getDiscriminatorMatchValue() != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,12 @@
*/
package org.hibernate.envers;

import java.io.Serializable;
import java.util.Date;

import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;
import jakarta.persistence.Entity;

/**
* @author Adam Warski (adam at warski dot org)
* @author Chris Cranford
*/
@MappedSuperclass
public class DefaultRevisionEntity implements Serializable {
private static final long serialVersionUID = 8530213963961662300L;

@Id
@GeneratedValue
@RevisionNumber
private int id;

@RevisionTimestamp
private long timestamp;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Transient
public Date getRevisionDate() {
return new Date( timestamp );
}

public long getTimestamp() {
return timestamp;
}

public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}

@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !(o instanceof DefaultRevisionEntity) ) {
return false;
}

final DefaultRevisionEntity that = (DefaultRevisionEntity) o;
return id == that.id
&& timestamp == that.timestamp;
}

@Override
public int hashCode() {
int result;
result = id;
result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
return result;
}

@Override
public String toString() {
return "DefaultRevisionEntity(id = " + id
+ ", revisionDate = " + DateTimeFormatter.INSTANCE.format(getRevisionDate() ) + ")";
}
@Entity
public final class DefaultRevisionEntity extends RevisionMapping {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,7 @@
*/
package org.hibernate.envers;

import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.MappedSuperclass;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import jakarta.persistence.Entity;

/**
* Extension of standard {@link DefaultRevisionEntity} that allows tracking entity names changed in each revision.
Expand All @@ -23,54 +13,6 @@
*
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@MappedSuperclass
public class DefaultTrackingModifiedEntitiesRevisionEntity extends DefaultRevisionEntity {
@ElementCollection(fetch = FetchType.EAGER)
@JoinTable(name = "REVCHANGES", joinColumns = @JoinColumn(name = "REV"))
@Column(name = "ENTITYNAME")
@Fetch(FetchMode.JOIN)
@ModifiedEntityNames
private Set<String> modifiedEntityNames = new HashSet<>();

public Set<String> getModifiedEntityNames() {
return modifiedEntityNames;
}

public void setModifiedEntityNames(Set<String> modifiedEntityNames) {
this.modifiedEntityNames = modifiedEntityNames;
}

@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !(o instanceof DefaultTrackingModifiedEntitiesRevisionEntity) ) {
return false;
}
if ( !super.equals( o ) ) {
return false;
}

final DefaultTrackingModifiedEntitiesRevisionEntity that = (DefaultTrackingModifiedEntitiesRevisionEntity) o;

if ( modifiedEntityNames != null ? !modifiedEntityNames.equals( that.modifiedEntityNames )
: that.modifiedEntityNames != null ) {
return false;
}

return true;
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (modifiedEntityNames != null ? modifiedEntityNames.hashCode() : 0);
return result;
}

@Override
public String toString() {
return "DefaultTrackingModifiedEntitiesRevisionEntity(" + super.toString() + ", modifiedEntityNames = " + modifiedEntityNames + ")";
}
@Entity
public final class DefaultTrackingModifiedEntitiesRevisionEntity extends TrackingModifiedEntitiesRevisionMapping {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.envers;

import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;

import java.io.Serializable;
import java.util.Date;

/**
* @author Adam Warski (adam at warski dot org)
* @author Chris Cranford
*/
@MappedSuperclass
public class RevisionMapping implements Serializable {
private static final long serialVersionUID = 8530213963961662300L;

@Id
@GeneratedValue
@RevisionNumber
private int id;

@RevisionTimestamp
private long timestamp;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Transient
public Date getRevisionDate() {
return new Date( timestamp );
}

public long getTimestamp() {
return timestamp;
}

public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}

@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !(o instanceof RevisionMapping) ) {
return false;
}

final RevisionMapping that = (RevisionMapping) o;
return id == that.id
&& timestamp == that.timestamp;
}

@Override
public int hashCode() {
int result;
result = id;
result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
return result;
}

@Override
public String toString() {
return "DefaultRevisionEntity(id = " + id
+ ", revisionDate = " + DateTimeFormatter.INSTANCE.format(getRevisionDate() ) + ")";
}
}
Loading
Loading