HHH-11595 Introduce `CollectionAuditTable` support

This commit is contained in:
Chris Cranford 2021-12-18 21:34:24 -05:00 committed by Chris Cranford
parent d9fa35c6f1
commit 99c13e5965
6 changed files with 89 additions and 7 deletions

View File

@ -350,6 +350,9 @@ It may be tedious to add this annotation to every audited entity, so if possible
If you have a mapping with secondary tables, audit tables for them will be generated in the same way (by adding the prefix and suffix). If you have a mapping with secondary tables, audit tables for them will be generated in the same way (by adding the prefix and suffix).
If you wish to overwrite this behavior, you can use the `@SecondaryAuditTable` and `@SecondaryAuditTables` annotations. If you wish to overwrite this behavior, you can use the `@SecondaryAuditTable` and `@SecondaryAuditTables` annotations.
If you have a mapping with collection tables, the audit table for them will be generated in the same way (by using the prefix and suffix).
If you wish to overwrite this behavior, you can use the `@CollectionAuditTable` annotations.
If you'd like to override auditing behavior of some fields/properties inherited from `@MappedSuperclass` or in an embedded component, If you'd like to override auditing behavior of some fields/properties inherited from `@MappedSuperclass` or in an embedded component,
you can apply the `@AuditOverride` annotation on the subtype or usage site of the component. you can apply the `@AuditOverride` annotation on the subtype or usage site of the component.

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.hibernate.Incubating;
/**
* Allows for the customization of an Envers audit collection table.
*
* @author Chris Cranford
*/
@Incubating
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface CollectionAuditTable {
/**
* The name of the table
*/
String name();
/**
* The schema of the table. Defaults to the schema of the mapping.
*/
String schema() default "";
/**
* The catalog of the table. Defaults to the catalog of the mapping.
*/
String catalog() default "";
}

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.envers.configuration.internal.metadata; package org.hibernate.envers.configuration.internal.metadata;
import org.hibernate.envers.CollectionAuditTable;
import org.hibernate.envers.boot.model.CompositeIdentifier; import org.hibernate.envers.boot.model.CompositeIdentifier;
import org.hibernate.envers.boot.model.RootPersistentEntity; import org.hibernate.envers.boot.model.RootPersistentEntity;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
@ -23,7 +24,6 @@ import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.OneToMany; import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -194,17 +194,19 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
); );
} }
// Hibernate uses a middle table for mapping this relation, so we get its name directly. // Hibernate uses a middle table for mapping this relation, so we get its name directly.
CollectionAuditTable collectionAuditTable = context.getPropertyAuditingData().getCollectionAuditTable();
if ( collectionAuditTable != null ) {
return collectionAuditTable.name();
}
return collection.getCollectionTable().getName(); return collection.getCollectionTable().getName();
} }
private RootPersistentEntity createMiddleEntity(CollectionMetadataContext context, String tableName, String entityName) { private RootPersistentEntity createMiddleEntity(CollectionMetadataContext context, String tableName, String entityName) {
final AuditJoinTableData joinTable = context.getPropertyAuditingData().getJoinTable();
final Table collectionTable = context.getCollection().getCollectionTable();
final AuditTableData auditTableData = new AuditTableData( final AuditTableData auditTableData = new AuditTableData(
entityName, entityName,
tableName, tableName,
getSchemaName( joinTable.getSchema(), collectionTable ), resolveSchema( context ),
getCatalogName( joinTable.getCatalog(), collectionTable ) resolveCatalog( context )
); );
final RootPersistentEntity entity = new RootPersistentEntity( auditTableData, null ); final RootPersistentEntity entity = new RootPersistentEntity( auditTableData, null );
@ -229,6 +231,25 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
return entity; return entity;
} }
private String resolveSchema(CollectionMetadataContext context) {
final CollectionAuditTable collectionAuditTable = context.getPropertyAuditingData().getCollectionAuditTable();
if ( collectionAuditTable != null && !StringTools.isEmpty( collectionAuditTable.schema() ) ) {
return collectionAuditTable.schema();
}
final AuditJoinTableData joinTable = context.getPropertyAuditingData().getJoinTable();
return getSchemaName( joinTable.getSchema(), context.getCollection().getCollectionTable() );
}
private String resolveCatalog(CollectionMetadataContext context) {
final CollectionAuditTable collectionAuditTable = context.getPropertyAuditingData().getCollectionAuditTable();
if ( collectionAuditTable != null && !StringTools.isEmpty( collectionAuditTable.catalog() ) ) {
return collectionAuditTable.catalog();
}
final AuditJoinTableData joinTable = context.getPropertyAuditingData().getJoinTable();
return getCatalogName( joinTable.getCatalog(), context.getCollection().getCollectionTable() );
}
private boolean isRevisionTypeInId(CollectionMetadataContext context) { private boolean isRevisionTypeInId(CollectionMetadataContext context) {
return isEmbeddableElementType( context ) || isLobMapElementType( context ); return isEmbeddableElementType( context ) || isLobMapElementType( context );
} }

View File

@ -32,6 +32,7 @@ import org.hibernate.envers.AuditMappedBy;
import org.hibernate.envers.AuditOverride; import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides; import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited; import org.hibernate.envers.Audited;
import org.hibernate.envers.CollectionAuditTable;
import org.hibernate.envers.NotAudited; import org.hibernate.envers.NotAudited;
import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.RelationTargetNotFoundAction; import org.hibernate.envers.RelationTargetNotFoundAction;
@ -540,6 +541,7 @@ public class AuditedPropertiesReader {
propertyData.setAccessType( accessType ); propertyData.setAccessType( accessType );
addPropertyJoinTables( property, propertyData ); addPropertyJoinTables( property, propertyData );
addPropertyCollectionAuditTable( property, propertyData );
addPropertyAuditingOverrides( property, propertyData ); addPropertyAuditingOverrides( property, propertyData );
if ( !processPropertyAuditingOverrides( property, propertyData ) ) { if ( !processPropertyAuditingOverrides( property, propertyData ) ) {
// not audited due to AuditOverride annotation // not audited due to AuditOverride annotation
@ -677,6 +679,13 @@ public class AuditedPropertiesReader {
} }
} }
private void addPropertyCollectionAuditTable(XProperty property, PropertyAuditingData propertyAuditingData) {
final CollectionAuditTable collectionAuditTableAnn = property.getAnnotation( CollectionAuditTable.class );
if ( collectionAuditTableAnn != null ) {
propertyAuditingData.setCollectionAuditTable( collectionAuditTableAnn );
}
}
/** /**
* Add the {@link AuditOverride} annotations. * Add the {@link AuditOverride} annotations.
* *

View File

@ -14,6 +14,7 @@ import jakarta.persistence.EnumType;
import org.hibernate.envers.AuditOverride; import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides; import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.CollectionAuditTable;
import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.RelationTargetNotFoundAction; import org.hibernate.envers.RelationTargetNotFoundAction;
import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.entities.PropertyData;
@ -33,6 +34,7 @@ public class PropertyAuditingData {
private String beanName; private String beanName;
private String mapKey; private String mapKey;
private EnumType mapKeyEnumType; private EnumType mapKeyEnumType;
private CollectionAuditTable collectionAuditTable;
private AuditJoinTableData joinTable; private AuditJoinTableData joinTable;
private String accessType; private String accessType;
private final List<AuditOverrideData> auditJoinTableOverrides = new ArrayList<>( 0 ); private final List<AuditOverrideData> auditJoinTableOverrides = new ArrayList<>( 0 );
@ -329,6 +331,14 @@ public class PropertyAuditingData {
this.virtualPropertyType = virtualPropertyType; this.virtualPropertyType = virtualPropertyType;
} }
public CollectionAuditTable getCollectionAuditTable() {
return collectionAuditTable;
}
public void setCollectionAuditTable(CollectionAuditTable collectionAuditTable) {
this.collectionAuditTable = collectionAuditTable;
}
public PropertyData resolvePropertyData() { public PropertyData resolvePropertyData() {
if ( propertyType != null && virtualPropertyType != null ) { if ( propertyType != null && virtualPropertyType != null ) {
return new PropertyData( return new PropertyData(