HHH-18541 Migrate hibernate-envers from hcann to hibernate-models

This commit is contained in:
Marco Belladelli 2024-08-29 09:54:06 +02:00 committed by Steve Ebersole
parent f1d7d86c2d
commit 00680b37fd
14 changed files with 381 additions and 426 deletions

View File

@ -65,7 +65,7 @@ public class ConverterHelper {
}
}
else {
throw new HibernateException( "Unexpected java.lang.reflect.Member type from org.hibernate.annotations.common.reflection.java.JavaXMember : " + member );
throw new HibernateException( "Unexpected java.lang.reflect.Member type from org.hibernate.models.spi.MemberDetails : " + member );
}
throw new HibernateException(

View File

@ -17,7 +17,7 @@ dependencies {
implementation jakartaLibs.jaxbApi
implementation jakartaLibs.jaxb
implementation libs.jandex
implementation libs.hcann
implementation libs.hibernateModels
compileOnly libs.ant

View File

@ -6,19 +6,19 @@
*/
package org.hibernate.envers.boot.internal;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.EffectiveMappingDefaults;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.envers.configuration.Configuration;
import org.hibernate.envers.configuration.internal.MappingCollector;
import org.hibernate.envers.configuration.internal.metadata.AuditEntityConfigurationRegistry;
import org.hibernate.envers.configuration.internal.metadata.AuditEntityNameRegister;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.service.ServiceRegistry;
/**
@ -102,8 +102,13 @@ public class EnversMetadataBuildingContextImpl implements EnversMetadataBuilding
}
@Override
public ReflectionManager getReflectionManager() {
return configuration.getReflectionManager();
public SourceModelBuildingContext getSourceModelBuildingContext() {
return metadataCollector.getSourceModelBuildingContext();
}
@Override
public ClassDetailsRegistry getClassDetailsRegistry() {
return metadataCollector.getClassDetailsRegistry();
}
@Override

View File

@ -98,6 +98,7 @@ public class EnversServiceImpl implements EnversService, Configurable, Stoppable
initialized = true;
final InFlightMetadataCollector metadataCollector = (InFlightMetadataCollector) metadata;
this.serviceRegistry = metadata.getMetadataBuildingOptions().getServiceRegistry();
this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
@ -105,12 +106,12 @@ public class EnversServiceImpl implements EnversService, Configurable, Stoppable
final Properties properties = new Properties();
properties.putAll( cfgService.getSettings() );
this.configuration = new Configuration( properties, this, metadata );
this.configuration = new Configuration( properties, this, metadataCollector );
this.auditProcessManager = new AuditProcessManager( configuration.getRevisionInfo().getRevisionInfoGenerator() );
final EnversMetadataBuildingContext metadataBuildingContext = new EnversMetadataBuildingContextImpl(
configuration,
(InFlightMetadataCollector) metadata,
metadataCollector,
effectiveMappingDefaults,
mappingCollector
);

View File

@ -6,12 +6,13 @@
*/
package org.hibernate.envers.boot.spi;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.envers.configuration.Configuration;
import org.hibernate.envers.configuration.internal.MappingCollector;
import org.hibernate.envers.configuration.internal.metadata.AuditEntityConfigurationRegistry;
import org.hibernate.envers.configuration.internal.metadata.AuditEntityNameRegister;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.service.ServiceRegistry;
/**
@ -26,7 +27,9 @@ public interface EnversMetadataBuildingContext extends MetadataBuildingContext {
ServiceRegistry getServiceRegistry();
ReflectionManager getReflectionManager();
SourceModelBuildingContext getSourceModelBuildingContext();
ClassDetailsRegistry getClassDetailsRegistry();
AuditEntityNameRegister getAuditEntityNameRegistry();

View File

@ -11,11 +11,9 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.envers.RevisionListener;
@ -75,7 +73,6 @@ public class Configuration {
private final boolean globalLegacyRelationTargetNotFound;
private final boolean trackEntitiesChanged;
private final JavaReflectionManager reflectionManager;
private boolean trackEntitiesOverride;
private final String auditTablePrefix;
@ -98,7 +95,7 @@ public class Configuration {
private final RevisionInfoConfiguration revisionInfo;
public Configuration(Properties properties, EnversService enversService, MetadataImplementor metadata) {
public Configuration(Properties properties, EnversService enversService, InFlightMetadataCollector metadata) {
this.enversService = enversService;
final ConfigurationProperties configProps = new ConfigurationProperties( properties );
@ -185,14 +182,7 @@ public class Configuration {
revisionPropertyBasePath = originalIdPropertyName + "." + revisionFieldName + ".";
revisionNumberPath = revisionPropertyBasePath + "id";
// todo: there are places that need bits built from the revinfo entity configuration
// this exists here as a way to pass it down in an immutable way to any consumer of this class
this.reflectionManager = new JavaReflectionManager();
this.revisionInfo = new RevisionInfoConfiguration( this, metadata, reflectionManager );
}
public JavaReflectionManager getReflectionManager() {
return reflectionManager;
this.revisionInfo = new RevisionInfoConfiguration( this, metadata );
}
public boolean isGenerateRevisionsForCollections() {

View File

@ -0,0 +1,92 @@
/*
* 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.configuration.internal;
import java.util.List;
import org.hibernate.envers.configuration.internal.metadata.reader.PersistentPropertiesSource;
import org.hibernate.models.internal.ClassTypeDetailsImpl;
import org.hibernate.models.internal.ModifierUtils;
import org.hibernate.models.internal.dynamic.DynamicFieldDetails;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.MethodDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.models.spi.TypeDetails;
/**
* Simple helper class to streamline hibernate-models interactions.
*
* @author Marco Belladelli
*/
public class ModelsHelper {
/**
* Provides a list of the provided class' {@link MemberDetails} based on the provided access type.
*
* @param classDetails The class details to extract the members from
* @param accessType The access type to use, accepted values are {@code field}, {@code property} and {@code record}
*
* @return The list of member details
*/
public static List<? extends MemberDetails> getMemberDetails(ClassDetails classDetails, String accessType) {
return switch ( accessType ) {
case "field" -> classDetails.getFields();
case "property" -> classDetails.getMethods();
case "record" -> classDetails.getRecordComponents();
default -> throw new IllegalArgumentException( "Unknown access type " + accessType );
};
}
/**
* Retrieves the {@link MemberDetails member} of the class, being either the field or the getter,
* with the provided name if one exists, {@code null} otherwise.
*
* @param classDetails The class details containing the desired member
* @param name The name of the member to find
*
* @return The requested member, null if not found
*/
public static MemberDetails getMember(ClassDetails classDetails, String name) {
final FieldDetails field = classDetails.findFieldByName( name );
if ( field != null ) {
return field;
}
for ( MethodDetails method : classDetails.getMethods() ) {
if ( method.resolveAttributeName().equals( name ) ) {
return method;
}
}
return null;
}
/**
* Instantiates a {@link DynamicFieldDetails} from the provided properties source with the specified name
*
* @param propertiesSource The property source containing the virtual field
* @param propertyName The property name of the dynamic field
* @param sourceModelBuildingContext Context object for models
*
* @return The newly created dynamic field details
*/
public static FieldDetails dynamicFieldDetails(
PersistentPropertiesSource propertiesSource,
String propertyName,
SourceModelBuildingContext sourceModelBuildingContext) {
return new DynamicFieldDetails(
propertyName,
new ClassTypeDetailsImpl( propertiesSource.getClassDetails(), TypeDetails.Kind.CLASS ),
propertiesSource.getClassDetails(),
ModifierUtils.DYNAMIC_ATTRIBUTE_MODIFIERS,
false,
false,
sourceModelBuildingContext
);
}
}

View File

@ -13,10 +13,7 @@ import java.util.Locale;
import java.util.Set;
import jakarta.persistence.Column;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.envers.Audited;
import org.hibernate.envers.DefaultRevisionEntity;
import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity;
@ -48,8 +45,13 @@ import org.hibernate.envers.internal.revisioninfo.RevisionInfoQueryCreator;
import org.hibernate.envers.internal.revisioninfo.RevisionTimestampValueResolver;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.service.ServiceRegistry;
import static org.hibernate.envers.configuration.internal.ModelsHelper.getMemberDetails;
/**
* @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
@ -80,11 +82,11 @@ public class RevisionInfoConfiguration {
private final Class<?> revisionInfoClass;
private final boolean useDefaultRevisionInfoMapping;
public RevisionInfoConfiguration(Configuration config, MetadataImplementor metadata, ReflectionManager reflectionManager) {
public RevisionInfoConfiguration(Configuration config, InFlightMetadataCollector metadata) {
this.configuration = config;
// Generate the resolver metadata
RevisionEntityResolver resolver = new RevisionEntityResolver( metadata, reflectionManager );
final RevisionEntityResolver resolver = new RevisionEntityResolver( metadata );
// initialize attributes from resolver
this.revisionInfoClass = resolver.revisionInfoClass;
@ -262,8 +264,7 @@ public class RevisionInfoConfiguration {
private class RevisionEntityResolver {
private final MetadataImplementor metadata;
private final ReflectionManager reflectionManager;
private final InFlightMetadataCollector metadata;
private boolean revisionEntityFound;
private boolean revisionNumberFound;
@ -282,9 +283,8 @@ public class RevisionInfoConfiguration {
private String revisionPropSqlType;
private RevisionTimestampValueResolver timestampValueResolver;
public RevisionEntityResolver(MetadataImplementor metadata, ReflectionManager reflectionManager) {
public RevisionEntityResolver(InFlightMetadataCollector metadata) {
this.metadata = metadata;
this.reflectionManager = reflectionManager;
this.revisionInfoEntityName = getDefaultEntityName();
this.revisionInfoIdData = createPropertyData( "id", "field" );
this.revisionInfoTimestampData = createPropertyData( "timestamp", "field" );
@ -312,8 +312,10 @@ public class RevisionInfoConfiguration {
continue;
}
XClass clazz = reflectionManager.toXClass( persistentClass.getMappedClass() );
final RevisionEntity revisionEntity = clazz.getAnnotation( RevisionEntity.class );
final ClassDetails classDetails = metadata.getClassDetailsRegistry().resolveClassDetails(
persistentClass.getClassName()
);
final RevisionEntity revisionEntity = classDetails.getDirectAnnotationUsage( RevisionEntity.class );
if ( revisionEntity == null ) {
// not annotated, skip
continue;
@ -324,13 +326,13 @@ public class RevisionInfoConfiguration {
}
// Verify that the revision entity isn't audited
if ( clazz.getAnnotation( Audited.class ) != null ) {
if ( classDetails.hasDirectAnnotationUsage( Audited.class ) ) {
throw new EnversMappingException( "The @RevisionEntity entity cannot be audited" );
}
revisionEntityFound = true;
resolveConfiguration( clazz );
resolveConfiguration( classDetails );
if ( !revisionNumberFound || !revisionTimestampFound ) {
// A revision number and timestamp fields must be annotated or the revision entity mapping
@ -436,115 +438,116 @@ public class RevisionInfoConfiguration {
|| modifiedEntityNamesFound;
}
private void resolveConfiguration(XClass clazz) {
final XClass superclazz = clazz.getSuperclass();
if ( !Object.class.getName().equals( superclazz.getName() ) ) {
private void resolveConfiguration(ClassDetails classDetails) {
final ClassDetails superclass = classDetails.getSuperClass();
if ( !Object.class.getName().equals( superclass.getName() ) ) {
// traverse to the top of the entity hierarchy
resolveConfiguration( superclazz );
resolveConfiguration( superclass );
}
resolveConfigurationFromProperties( clazz, "field" );
resolveConfigurationFromProperties( clazz, "property" );
resolveConfigurationFromProperties( classDetails, "field" );
resolveConfigurationFromProperties( classDetails, "property" );
}
private void resolveConfigurationFromProperties(XClass clazz, String accessType) {
for ( XProperty property : clazz.getDeclaredProperties( accessType ) ) {
final RevisionNumber revisionNumber = property.getAnnotation( RevisionNumber.class );
private void resolveConfigurationFromProperties(ClassDetails classDetails, String accessType) {
for ( MemberDetails member : getMemberDetails( classDetails, accessType ) ) {
final RevisionNumber revisionNumber = member.getDirectAnnotationUsage( RevisionNumber.class );
if ( revisionNumber != null ) {
resolveRevisionNumberFromProperty( property, accessType );
resolveRevisionNumberFromProperty( member, accessType );
}
final RevisionTimestamp revisionTimestamp = property.getAnnotation( RevisionTimestamp.class );
final RevisionTimestamp revisionTimestamp = member.getDirectAnnotationUsage( RevisionTimestamp.class );
if ( revisionTimestamp != null ) {
resolveRevisionTimestampFromProperty( property, accessType );
resolveRevisionTimestampFromProperty( member, accessType );
}
final ModifiedEntityNames modifiedEntityNames = property.getAnnotation( ModifiedEntityNames.class );
final ModifiedEntityNames modifiedEntityNames = member.getDirectAnnotationUsage( ModifiedEntityNames.class );
if ( modifiedEntityNames != null ) {
resolveModifiedEntityNamesFromProperty( property, accessType );
resolveModifiedEntityNamesFromProperty( member, accessType );
}
}
}
private void resolveRevisionNumberFromProperty(XProperty property, String accessType) {
private void resolveRevisionNumberFromProperty(MemberDetails memberDetails, String accessType) {
if ( revisionNumberFound ) {
throw new EnversMappingException( "Only one property can be defined with @RevisionNumber" );
}
final XClass propertyType = property.getType();
if ( isAnyType( propertyType, Integer.class, Integer.TYPE ) ) {
revisionInfoIdData = createPropertyData( property, accessType );
final TypeDetails type = memberDetails.getType();
if ( isAnyType( type, Integer.class, Integer.TYPE ) ) {
revisionInfoIdData = createPropertyData( memberDetails, accessType );
revisionNumberFound = true;
}
else if ( isAnyType( propertyType, Long.class, Long.TYPE ) ) {
revisionInfoIdData = createPropertyData( property, accessType );
else if ( isAnyType( type, Long.class, Long.TYPE ) ) {
revisionInfoIdData = createPropertyData( memberDetails, accessType );
revisionPropType = "long";
revisionNumberFound = true;
}
else {
throwUnexpectedAnnotatedType( property, RevisionNumber.class, "int, Integer, long, or Long" );
throwUnexpectedAnnotatedType( memberDetails, RevisionNumber.class, "int, Integer, long, or Long" );
}
// Getting the @Column definition of the revision number property, to later use that information
// to generate the same mapping for the relation from an audit table's revision number to the
// revision entity's revision number field.
final Column column = property.getAnnotation( Column.class );
final Column column = memberDetails.getDirectAnnotationUsage( Column.class );
if ( column != null ) {
revisionPropSqlType = column.columnDefinition();
}
}
private void resolveRevisionTimestampFromProperty(XProperty property, String accessType) {
private void resolveRevisionTimestampFromProperty(MemberDetails memberDetails, String accessType) {
if ( revisionTimestampFound ) {
throw new EnversMappingException( "Only one property can be defined with @RevisionTimestamp" );
}
final XClass propertyType = property.getType();
if ( isAnyType( propertyType, Long.class, Long.TYPE, Date.class, LocalDateTime.class, Instant.class, java.sql.Date.class ) ) {
revisionInfoTimestampData = createPropertyData( property, accessType );
final TypeDetails type = memberDetails.getType();
if ( isAnyType( type, Long.class, Long.TYPE, Date.class, LocalDateTime.class, Instant.class, java.sql.Date.class ) ) {
revisionInfoTimestampData = createPropertyData( memberDetails, accessType );
revisionTimestampFound = true;
}
else {
throwUnexpectedAnnotatedType( property, RevisionTimestamp.class, "long, Long, Date, LocalDateTime, Instant, or java.sql.Date" );
throwUnexpectedAnnotatedType( memberDetails, RevisionTimestamp.class, "long, Long, Date, LocalDateTime, Instant, or java.sql.Date" );
}
}
private void resolveModifiedEntityNamesFromProperty(XProperty property, String accessType) {
private void resolveModifiedEntityNamesFromProperty(MemberDetails memberDetails, String accessType) {
if ( modifiedEntityNamesFound ) {
throw new EnversMappingException( "Only one property can be defined with @ModifiedEntityNames" );
}
final XClass propertyType = property.getType();
if ( isAnyType( propertyType, Set.class ) ) {
final XClass elementType = property.getElementClass();
final TypeDetails type = memberDetails.getType();
if ( isAnyType( type, Set.class ) ) {
final TypeDetails elementType = memberDetails.getElementType();
if ( isAnyType( elementType, String.class ) ) {
modifiedEntityNamesData = createPropertyData( property, accessType );
modifiedEntityNamesData = createPropertyData( memberDetails, accessType );
modifiedEntityNamesFound = true;
return;
}
}
throwUnexpectedAnnotatedType( property, ModifiedEntityNames.class, "Set<String>" );
throwUnexpectedAnnotatedType( memberDetails, ModifiedEntityNames.class, "Set<String>" );
}
private PropertyData createPropertyData(XProperty property, String accessType) {
return createPropertyData( property.getName(), accessType );
private PropertyData createPropertyData(MemberDetails memberDetails, String accessType) {
return createPropertyData( memberDetails.resolveAttributeName(), accessType );
}
private PropertyData createPropertyData(String name, String accessType) {
return new PropertyData( name, name, accessType );
}
private boolean isAnyType(XClass clazz, Class<?>... types) {
private boolean isAnyType(TypeDetails typeDetails, Class<?>... types) {
for ( Class<?> type : types ) {
if ( isType( clazz, type ) ) {
if ( isType( typeDetails, type ) ) {
return true;
}
}
return false;
}
private boolean isType(XClass clazz, Class<?> type) {
return reflectionManager.equals( clazz, type );
private boolean isType(TypeDetails typeDetails, Class<?> type) {
final String className = typeDetails != null ? typeDetails.determineRawClass().getClassName() : null;
return className != null && className.equals( type.getName() );
}
private Class<? extends RevisionListener> getRevisionListenerClass(Class<? extends RevisionListener> defaultListener) {
@ -554,12 +557,12 @@ public class RevisionInfoConfiguration {
return defaultListener;
}
private void throwUnexpectedAnnotatedType(XProperty property, Class<?> annotation, String allowedTypes) {
private void throwUnexpectedAnnotatedType(MemberDetails memberDetails, Class<?> annotation, String allowedTypes) {
throw new EnversMappingException(
String.format(
Locale.ENGLISH,
"The field '%s' annotated with '@%s' must be of type: %s",
property.getName(),
memberDetails.resolveAttributeName(),
annotation.getName(),
allowedTypes
)

View File

@ -8,7 +8,6 @@ package org.hibernate.envers.configuration.internal.metadata.reader;
import java.lang.annotation.Annotation;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.envers.AuditTable;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
@ -16,6 +15,7 @@ import org.hibernate.envers.SecondaryAuditTable;
import org.hibernate.envers.SecondaryAuditTables;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.models.spi.ClassDetails;
/**
* A helper class to read versioning meta-data from annotations on a persistent class.
@ -31,8 +31,8 @@ public final class AnnotationsMetadataReader {
this.metadataBuildingContext = metadataBuildingContext;
}
private RelationTargetAuditMode getDefaultAudited(XClass clazz) {
final Audited defaultAudited = clazz.getAnnotation( Audited.class );
private RelationTargetAuditMode getDefaultAudited(ClassDetails classDetails) {
final Audited defaultAudited = classDetails.getDirectAnnotationUsage( Audited.class );
if ( defaultAudited != null ) {
return defaultAudited.targetAuditMode();
@ -42,8 +42,8 @@ public final class AnnotationsMetadataReader {
}
}
private void addAuditTable(ClassAuditingData auditData, XClass clazz) {
final AuditTable auditTable = clazz.getAnnotation( AuditTable.class );
private void addAuditTable(ClassAuditingData auditData, ClassDetails classDetails) {
final AuditTable auditTable = classDetails.getDirectAnnotationUsage( AuditTable.class );
if ( auditTable != null ) {
auditData.setAuditTable( auditTable );
}
@ -52,9 +52,9 @@ public final class AnnotationsMetadataReader {
}
}
private void addAuditSecondaryTables(ClassAuditingData auditData, XClass clazz) {
private void addAuditSecondaryTables(ClassAuditingData auditData, ClassDetails classDetails) {
// Getting information on secondary tables
final SecondaryAuditTable secondaryVersionsTable1 = clazz.getAnnotation( SecondaryAuditTable.class );
final SecondaryAuditTable secondaryVersionsTable1 = classDetails.getDirectAnnotationUsage( SecondaryAuditTable.class );
if ( secondaryVersionsTable1 != null ) {
auditData.getSecondaryTableDictionary().put(
secondaryVersionsTable1.secondaryTableName(),
@ -62,7 +62,7 @@ public final class AnnotationsMetadataReader {
);
}
final SecondaryAuditTables secondaryAuditTables = clazz.getAnnotation( SecondaryAuditTables.class );
final SecondaryAuditTables secondaryAuditTables = classDetails.getDirectAnnotationUsage( SecondaryAuditTables.class );
if ( secondaryAuditTables != null ) {
for ( SecondaryAuditTable secondaryAuditTable2 : secondaryAuditTables.value() ) {
auditData.getSecondaryTableDictionary().put(
@ -75,21 +75,23 @@ public final class AnnotationsMetadataReader {
public ClassAuditingData getAuditData(PersistentClass persistentClass) {
final ClassAuditingData auditData = new ClassAuditingData( persistentClass );
final XClass xclass = metadataBuildingContext.getReflectionManager().toXClass( persistentClass.getMappedClass() );
final ClassDetails classDetails = metadataBuildingContext.getClassDetailsRegistry().resolveClassDetails(
persistentClass.getClassName()
);
final RelationTargetAuditMode auditMode = getDefaultAudited( xclass );
final RelationTargetAuditMode auditMode = getDefaultAudited( classDetails );
if ( auditMode != null ) {
auditData.setDefaultAudited( true );
}
new AuditedPropertiesReader(
metadataBuildingContext,
PersistentPropertiesSource.forClass( persistentClass, xclass ),
PersistentPropertiesSource.forClass( persistentClass, classDetails ),
auditData
).read();
addAuditTable( auditData, xclass );
addAuditSecondaryTables( auditData, xclass );
addAuditTable( auditData, classDetails );
addAuditSecondaryTables( auditData, classDetails );
return auditData;
}

View File

@ -17,9 +17,6 @@ import java.util.Set;
import java.util.function.Function;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.envers.AuditJoinTable;
import org.hibernate.envers.AuditMappedBy;
import org.hibernate.envers.AuditOverride;
@ -33,12 +30,15 @@ import org.hibernate.envers.boot.EnversMappingException;
import org.hibernate.envers.boot.internal.ModifiedColumnNameResolver;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.envers.internal.tools.MappingTools;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.spi.NavigablePath;
import jakarta.persistence.ElementCollection;
@ -46,8 +46,11 @@ import jakarta.persistence.Lob;
import jakarta.persistence.MapKey;
import jakarta.persistence.MapKeyEnumerated;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Transient;
import jakarta.persistence.Version;
import static org.hibernate.envers.configuration.internal.ModelsHelper.dynamicFieldDetails;
import static org.hibernate.envers.configuration.internal.ModelsHelper.getMember;
import static org.hibernate.envers.internal.tools.Tools.newHashMap;
import static org.hibernate.envers.internal.tools.Tools.newHashSet;
@ -79,12 +82,12 @@ public class AuditedPropertiesReader {
// Mapping class field to corresponding <properties> element.
private final Map<String, String> propertiesGroupMapping;
private final Set<XProperty> overriddenAuditedProperties;
private final Set<XProperty> overriddenNotAuditedProperties;
private final Map<XProperty, AuditJoinTable> overriddenAuditedPropertiesJoinTables;
private final Set<MemberDetails> overriddenAuditedProperties;
private final Set<MemberDetails> overriddenNotAuditedProperties;
private final Map<MemberDetails, AuditJoinTable> overriddenAuditedPropertiesJoinTables;
private final Set<XClass> overriddenAuditedClasses;
private final Set<XClass> overriddenNotAuditedClasses;
private final Set<ClassDetails> overriddenAuditedClasses;
private final Set<ClassDetails> overriddenNotAuditedClasses;
public AuditedPropertiesReader(
EnversMetadataBuildingContext metadataBuildingContext,
@ -129,7 +132,7 @@ public class AuditedPropertiesReader {
else {
// Retrieve classes and properties that are explicitly marked for auditing process by any superclass
// of currently mapped entity or itself.
final XClass clazz = persistentPropertiesSource.getXClass();
final ClassDetails classDetails = persistentPropertiesSource.getClassDetails();
if ( persistentPropertiesSource.hasCompositeUserType() ) {
for ( String propertyName : fieldAccessedPersistentProperties ) {
final Property property = persistentPropertiesSource.getProperty( propertyName );
@ -153,9 +156,9 @@ public class AuditedPropertiesReader {
}
}
else {
readAuditOverrides( clazz );
readAuditOverrides( classDetails );
// Adding all properties from the given class.
addPropertiesFromClass( clazz );
addPropertiesFromClass( classDetails );
}
}
}
@ -164,16 +167,16 @@ public class AuditedPropertiesReader {
* Recursively constructs sets of audited and not audited properties and classes which behavior has been overridden
* using {@link AuditOverride} annotation.
*
* @param clazz Class that is being processed. Currently mapped entity shall be passed during first invocation.
* @param classDetails Class that is being processed. Currently mapped entity shall be passed during first invocation.
*/
private void readAuditOverrides(XClass clazz) {
private void readAuditOverrides(ClassDetails classDetails) {
/* TODO: Code to remove with @Audited.auditParents - start. */
final ReflectionManager reflectionManager = metadataBuildingContext.getReflectionManager();
final Audited allClassAudited = clazz.getAnnotation( Audited.class );
final Audited allClassAudited = classDetails.getDirectAnnotationUsage( Audited.class );
if ( allClassAudited != null && allClassAudited.auditParents().length > 0 ) {
for ( Class c : allClassAudited.auditParents() ) {
final XClass parentClass = reflectionManager.toXClass( c );
checkSuperclass( clazz, parentClass );
final ClassDetails parentClass = metadataBuildingContext.getClassDetailsRegistry()
.resolveClassDetails( c.getName() );
checkSuperclass( classDetails, parentClass );
if ( !overriddenNotAuditedClasses.contains( parentClass ) ) {
// If the class has not been marked as not audited by the subclass.
overriddenAuditedClasses.add( parentClass );
@ -181,15 +184,16 @@ public class AuditedPropertiesReader {
}
}
/* TODO: Code to remove with @Audited.auditParents - finish. */
final List<AuditOverride> auditOverrides = computeAuditOverrides( clazz );
final List<AuditOverride> auditOverrides = computeAuditOverrides( classDetails );
for ( AuditOverride auditOverride : auditOverrides ) {
if ( auditOverride.forClass() != void.class ) {
final XClass overrideClass = reflectionManager.toXClass( auditOverride.forClass() );
checkSuperclass( clazz, overrideClass );
final ClassDetails overrideClass = metadataBuildingContext.getClassDetailsRegistry()
.resolveClassDetails( auditOverride.forClass().getName() );
checkSuperclass( classDetails, overrideClass );
final String propertyName = auditOverride.name();
if ( !StringTools.isEmpty( propertyName ) ) {
// Override @Audited annotation on property level.
final XProperty property = getProperty( overrideClass, propertyName );
final MemberDetails property = getProperty( overrideClass, propertyName );
if ( auditOverride.isAudited() ) {
if ( !overriddenNotAuditedProperties.contains( property ) ) {
// If the property has not been marked as not audited by the subclass.
@ -221,20 +225,20 @@ public class AuditedPropertiesReader {
}
}
}
final XClass superclass = clazz.getSuperclass();
if ( !clazz.isInterface() && !Object.class.getName().equals( superclass.getName() ) ) {
final ClassDetails superclass = classDetails.getSuperClass();
if ( !classDetails.isInterface() && !Object.class.getName().equals( superclass.getName() ) ) {
readAuditOverrides( superclass );
}
}
/**
* @param clazz Source class.
* @param classDetails Source class.
*
* @return List of @AuditOverride annotations applied at class level.
*/
private List<AuditOverride> computeAuditOverrides(XClass clazz) {
final AuditOverrides auditOverrides = clazz.getAnnotation( AuditOverrides.class );
final AuditOverride auditOverride = clazz.getAnnotation( AuditOverride.class );
private List<AuditOverride> computeAuditOverrides(ClassDetails classDetails) {
final AuditOverrides auditOverrides = classDetails.getDirectAnnotationUsage( AuditOverrides.class );
final AuditOverride auditOverride = classDetails.getDirectAnnotationUsage( AuditOverride.class );
if ( auditOverrides == null && auditOverride != null ) {
return Arrays.asList( auditOverride );
}
@ -244,7 +248,7 @@ public class AuditedPropertiesReader {
else if ( auditOverrides != null && auditOverride != null ) {
throw new EnversMappingException(
"@AuditOverrides annotation should encapsulate all @AuditOverride declarations. " +
"Please revise Envers annotations applied to class " + clazz.getName() + "."
"Please revise Envers annotations applied to class " + classDetails.getName() + "."
);
}
return Collections.emptyList();
@ -256,8 +260,8 @@ public class AuditedPropertiesReader {
* @param child Subclass.
* @param parent Superclass.
*/
private void checkSuperclass(XClass child, XClass parent) {
if ( !parent.isAssignableFrom( child ) ) {
private void checkSuperclass(ClassDetails child, ClassDetails parent) {
if ( !child.isImplementor( parent.toJavaClass() ) ) {
throw new EnversMappingException(
"Class " + parent.getName() + " is not assignable from " + child.getName() + ". " +
"Please revise Envers annotations applied to " + child.getName() + " type."
@ -268,20 +272,20 @@ public class AuditedPropertiesReader {
/**
* Checks whether class contains property with a given name. If not {@link EnversMappingException} is thrown.
*
* @param clazz Class.
* @param classDetails Class.
* @param propertyName Property name.
*
* @return Property object.
*/
private XProperty getProperty(XClass clazz, String propertyName) {
final XProperty property = ReflectionTools.getProperty( clazz, propertyName );
if ( property == null ) {
private MemberDetails getProperty(ClassDetails classDetails, String propertyName) {
final MemberDetails member = getMember( classDetails, propertyName );
if ( member == null ) {
throw new EnversMappingException(
"Property '" + propertyName + "' not found in class " + clazz.getName() + ". " +
"Please revise Envers annotations applied to class " + persistentPropertiesSource.getXClass() + "."
"Property '" + propertyName + "' not found in class " + classDetails.getName() + ". " +
"Please revise Envers annotations applied to class " + persistentPropertiesSource.getClassDetails() + "."
);
}
return property;
return member;
}
private void readPersistentPropertiesAccess() {
@ -313,7 +317,7 @@ public class AuditedPropertiesReader {
}
/**
* @param clazz Class which properties are currently being added.
* @param classDetails Class which properties are currently being added.
*
* @return {@link Audited} annotation of specified class. If processed type hasn't been explicitly marked, method
* checks whether given class exists in {@link AuditedPropertiesReader#overriddenAuditedClasses} collection.
@ -321,27 +325,27 @@ public class AuditedPropertiesReader {
* {@code null}. If processed type exists in {@link AuditedPropertiesReader#overriddenNotAuditedClasses}
* collection, the result is also {@code null}.
*/
private Audited computeAuditConfiguration(XClass clazz) {
Audited allClassAudited = clazz.getAnnotation( Audited.class );
private Audited computeAuditConfiguration(ClassDetails classDetails) {
Audited allClassAudited = classDetails.getDirectAnnotationUsage( Audited.class );
// If processed class is not explicitly marked with @Audited annotation, check whether auditing is
// forced by any of its child entities configuration (@AuditedOverride.forClass).
if ( allClassAudited == null && overriddenAuditedClasses.contains( clazz ) ) {
if ( allClassAudited == null && overriddenAuditedClasses.contains( classDetails ) ) {
// Declared audited parent copies @Audited.modStore and @Audited.targetAuditMode configuration from
// currently mapped entity.
allClassAudited = persistentPropertiesSource.getXClass().getAnnotation( Audited.class );
allClassAudited = persistentPropertiesSource.getClassDetails().getDirectAnnotationUsage( Audited.class );
if ( allClassAudited == null ) {
// If parent class declares @Audited on the field/property level.
allClassAudited = DEFAULT_AUDITED;
}
}
else if ( allClassAudited != null && overriddenNotAuditedClasses.contains( clazz ) ) {
else if ( allClassAudited != null && overriddenNotAuditedClasses.contains( classDetails ) ) {
return null;
}
return allClassAudited;
}
private void addPropertiesFromDynamicComponent(PersistentPropertiesSource propertiesSource) {
Audited audited = computeAuditConfiguration( propertiesSource.getXClass() );
Audited audited = computeAuditConfiguration( propertiesSource.getClassDetails() );
if ( !fieldAccessedPersistentProperties.isEmpty() ) {
throw new EnversMappingException(
"Audited dynamic component cannot have properties with access=\"field\" for properties: " +
@ -354,20 +358,18 @@ public class AuditedPropertiesReader {
String accessType = entry.getValue();
if ( !auditedPropertiesHolder.contains( property ) ) {
final Value propertyValue = persistentPropertiesSource.getProperty( property ).getValue();
final SourceModelBuildingContext buildingContext = metadataBuildingContext.getSourceModelBuildingContext();
final FieldDetails fieldDetails = dynamicFieldDetails( propertiesSource, property, buildingContext );
if ( propertyValue instanceof Component ) {
this.addFromComponentProperty(
new DynamicProperty( propertiesSource, property ),
fieldDetails,
accessType,
(Component) propertyValue,
audited
);
}
else {
this.addFromNotComponentProperty(
new DynamicProperty( propertiesSource, property ),
accessType,
audited
);
this.addFromNotComponentProperty( fieldDetails, accessType, audited );
}
}
}
@ -376,29 +378,29 @@ public class AuditedPropertiesReader {
/**
* Recursively adds all audited properties of entity class and its superclasses.
*
* @param clazz Currently processed class.
* @param classDetails Currently processed class.
*/
private void addPropertiesFromClass(XClass clazz) {
final Audited allClassAudited = computeAuditConfiguration( clazz );
private void addPropertiesFromClass(ClassDetails classDetails) {
final Audited allClassAudited = computeAuditConfiguration( classDetails );
//look in the class
addFromProperties(
clazz.getDeclaredProperties( "field" ),
classDetails.forEachField( (i, field) -> addFromProperty(
field,
it -> "field",
fieldAccessedPersistentProperties,
allClassAudited
);
addFromProperties(
clazz.getDeclaredProperties( "property" ),
) );
classDetails.forEachMethod( (i, method) -> addFromProperty(
method,
propertyAccessedPersistentProperties::get,
propertyAccessedPersistentProperties.keySet(),
allClassAudited
);
) );
if ( isClassHierarchyTraversalNeeded( allClassAudited ) ) {
final XClass superclazz = clazz.getSuperclass();
if ( !clazz.isInterface() && !"java.lang.Object".equals( superclazz.getName() ) ) {
addPropertiesFromClass( superclazz );
final ClassDetails superclass = classDetails.getSuperClass();
if ( !classDetails.isInterface() && !"java.lang.Object".equals( superclass.getName() ) ) {
addPropertiesFromClass( superclass );
}
}
}
@ -407,53 +409,56 @@ public class AuditedPropertiesReader {
return allClassAudited != null || !auditedPropertiesHolder.isEmpty();
}
private void addFromProperties(
Iterable<XProperty> properties,
private void addFromProperty(
MemberDetails memberDetails,
Function<String, String> accessTypeProvider,
Set<String> persistentProperties,
Audited allClassAudited) {
for ( XProperty property : properties ) {
final String accessType = accessTypeProvider.apply( property.getName() );
if ( !memberDetails.isPersistable() || memberDetails.hasDirectAnnotationUsage( Transient.class ) ) {
return;
}
// If this is not a persistent property, with the same access type as currently checked,
// it's not audited as well.
// If the property was already defined by the subclass, is ignored by superclasses
if ( persistentProperties.contains( property.getName() )
&& !auditedPropertiesHolder.contains( property.getName() ) ) {
final Value propertyValue = persistentPropertiesSource.getProperty( property.getName() ).getValue();
if ( propertyValue instanceof Component ) {
this.addFromComponentProperty( property, accessType, (Component) propertyValue, allClassAudited );
}
else {
this.addFromNotComponentProperty( property, accessType, allClassAudited );
}
final String attributeName = memberDetails.resolveAttributeName();
final String accessType = accessTypeProvider.apply( attributeName );
// If this is not a persistent property, with the same access type as currently checked,
// it's not audited as well.
// If the property was already defined by the subclass, is ignored by superclasses
if ( persistentProperties.contains( attributeName )
&& !auditedPropertiesHolder.contains( attributeName ) ) {
final Value propertyValue = persistentPropertiesSource.getProperty( attributeName ).getValue();
if ( propertyValue instanceof Component ) {
this.addFromComponentProperty( memberDetails, accessType, (Component) propertyValue, allClassAudited );
}
else if ( propertiesGroupMapping.containsKey( property.getName() ) ) {
// Retrieve embedded component name based on class field.
final String embeddedName = propertiesGroupMapping.get( property.getName() );
if ( !auditedPropertiesHolder.contains( embeddedName ) ) {
// Manage properties mapped within <properties> tag.
final Value propertyValue = persistentPropertiesSource.getProperty( embeddedName ).getValue();
this.addFromPropertiesGroup(
embeddedName,
property,
accessType,
(Component) propertyValue,
allClassAudited
);
}
else {
this.addFromNotComponentProperty( memberDetails, accessType, allClassAudited );
}
}
else if ( propertiesGroupMapping.containsKey( attributeName ) ) {
// Retrieve embedded component name based on class field.
final String embeddedName = propertiesGroupMapping.get( attributeName );
if ( !auditedPropertiesHolder.contains( embeddedName ) ) {
// Manage properties mapped within <properties> tag.
final Value propertyValue = persistentPropertiesSource.getProperty( embeddedName ).getValue();
this.addFromPropertiesGroup(
embeddedName,
memberDetails,
accessType,
(Component) propertyValue,
allClassAudited
);
}
}
}
private void addFromPropertiesGroup(
String embeddedName,
XProperty property,
MemberDetails memberDetails,
String accessType,
Component propertyValue,
Audited allClassAudited) {
final ComponentAuditingData componentData = new ComponentAuditingData();
final boolean isAudited = fillPropertyData( property, componentData, accessType, allClassAudited );
final boolean isAudited = fillPropertyData( memberDetails, componentData, accessType, allClassAudited );
if ( isAudited ) {
// EntityPersister.getPropertyNames() returns name of embedded component instead of class field.
componentData.setName( embeddedName );
@ -477,17 +482,18 @@ public class AuditedPropertiesReader {
}
private void addFromComponentProperty(
XProperty property,
MemberDetails memberDetails,
String accessType,
Component propertyValue,
Audited allClassAudited) {
final ComponentAuditingData componentData = new ComponentAuditingData();
final boolean isAudited = fillPropertyData( property, componentData, accessType, allClassAudited );
final boolean isAudited = fillPropertyData( memberDetails, componentData, accessType, allClassAudited );
final PersistentPropertiesSource componentPropertiesSource;
if ( propertyValue.isDynamic() ) {
final XClass xClass = metadataBuildingContext.getReflectionManager().toXClass( Map.class );
componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, xClass, true );
final ClassDetails mapClassDetails = metadataBuildingContext.getClassDetailsRegistry()
.getClassDetails( Map.class.getName() );
componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, mapClassDetails, true );
}
else {
componentPropertiesSource = PersistentPropertiesSource.forComponent( metadataBuildingContext, propertyValue );
@ -497,23 +503,23 @@ public class AuditedPropertiesReader {
metadataBuildingContext,
componentPropertiesSource,
componentData,
propertyNamePrefix + MappingTools.createComponentPrefix( property.getName() )
propertyNamePrefix + MappingTools.createComponentPrefix( memberDetails.resolveAttributeName() )
);
audPropReader.read( allClassAudited );
if ( isAudited ) {
// Now we know that the property is audited
auditedPropertiesHolder.addPropertyAuditingData( property.getName(), componentData );
auditedPropertiesHolder.addPropertyAuditingData( memberDetails.resolveAttributeName(), componentData );
}
}
private void addFromNotComponentProperty(XProperty property, String accessType, Audited allClassAudited) {
private void addFromNotComponentProperty(MemberDetails memberDetails, String accessType, Audited allClassAudited) {
final PropertyAuditingData propertyData = new PropertyAuditingData();
final boolean isAudited = fillPropertyData( property, propertyData, accessType, allClassAudited );
final boolean isAudited = fillPropertyData( memberDetails, propertyData, accessType, allClassAudited );
if ( isAudited ) {
// Now we know that the property is audited
auditedPropertiesHolder.addPropertyAuditingData( property.getName(), propertyData );
auditedPropertiesHolder.addPropertyAuditingData( memberDetails.resolveAttributeName(), propertyData );
}
}
@ -521,59 +527,59 @@ public class AuditedPropertiesReader {
/**
* Checks if a property is audited and if yes, fills all of its data.
*
* @param property Property to check.
* @param memberDetails Property to check.
* @param propertyData Property data, on which to set this property's modification store.
* @param accessType Access type for the property.
*
* @return False if this property is not audited.
*/
private boolean fillPropertyData(
XProperty property,
MemberDetails memberDetails,
PropertyAuditingData propertyData,
String accessType,
Audited allClassAudited) {
// check if a property is declared as not audited to exclude it
// useful if a class is audited but some properties should be excluded
final NotAudited unVer = property.getAnnotation( NotAudited.class );
final NotAudited unVer = memberDetails.getDirectAnnotationUsage( NotAudited.class );
if ( ( unVer != null
&& !overriddenAuditedProperties.contains( property ) )
|| overriddenNotAuditedProperties.contains( property ) ) {
&& !overriddenAuditedProperties.contains( memberDetails ) )
|| overriddenNotAuditedProperties.contains( memberDetails ) ) {
return false;
}
else {
// if the optimistic locking field has to be unversioned and the current property
// is the optimistic locking field, don't audit it
if ( metadataBuildingContext.getConfiguration().isDoNotAuditOptimisticLockingField() ) {
final Version jpaVer = property.getAnnotation( Version.class );
final Version jpaVer = memberDetails.getDirectAnnotationUsage( Version.class );
if ( jpaVer != null ) {
return false;
}
}
}
final String propertyName = propertyNamePrefix + property.getName();
final String propertyName = propertyNamePrefix + memberDetails.resolveAttributeName();
final String modifiedFlagsSuffix = metadataBuildingContext.getConfiguration().getModifiedFlagsSuffix();
if ( !this.checkAudited( property, propertyData,propertyName, allClassAudited, modifiedFlagsSuffix ) ) {
if ( !this.checkAudited( memberDetails, propertyData,propertyName, allClassAudited, modifiedFlagsSuffix ) ) {
return false;
}
validateLobMappingSupport( property );
validateLobMappingSupport( memberDetails );
propertyData.setName( propertyName );
propertyData.setBeanName( property.getName() );
propertyData.setBeanName( memberDetails.resolveAttributeName() );
propertyData.setAccessType( accessType );
addPropertyJoinTables( property, propertyData );
addPropertyCollectionAuditTable( property, propertyData );
addPropertyAuditingOverrides( property, propertyData );
if ( !processPropertyAuditingOverrides( property, propertyData ) ) {
addPropertyJoinTables( memberDetails, propertyData );
addPropertyCollectionAuditTable( memberDetails, propertyData );
addPropertyAuditingOverrides( memberDetails, propertyData );
if ( !processPropertyAuditingOverrides( memberDetails, propertyData ) ) {
// not audited due to AuditOverride annotation
return false;
}
addPropertyMapKey( property, propertyData );
setPropertyAuditMappedBy( property, propertyData );
setPropertyRelationMappedBy( property, propertyData );
addPropertyMapKey( memberDetails, propertyData );
setPropertyAuditMappedBy( memberDetails, propertyData );
setPropertyRelationMappedBy( memberDetails, propertyData );
return true;
}
@ -588,8 +594,9 @@ public class AuditedPropertiesReader {
final PersistentPropertiesSource componentPropertiesSource;
if ( propertyValue.isDynamic() ) {
final XClass xClass = metadataBuildingContext.getReflectionManager().toXClass( Map.class );
componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, xClass, true );
final ClassDetails mapClassDetails = metadataBuildingContext.getClassDetailsRegistry()
.getClassDetails( Map.class.getName() );
componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, mapClassDetails, true );
}
else {
componentPropertiesSource = PersistentPropertiesSource.forComponent( metadataBuildingContext, propertyValue );
@ -654,12 +661,12 @@ public class AuditedPropertiesReader {
return true;
}
private void validateLobMappingSupport(XProperty property) {
private void validateLobMappingSupport(MemberDetails memberDetails) {
// HHH-9834 - Sanity check
try {
if ( property.isAnnotationPresent( ElementCollection.class ) ) {
if ( property.isAnnotationPresent( Lob.class ) ) {
if ( !property.getCollectionClass().isAssignableFrom( Map.class ) ) {
if ( memberDetails.hasDirectAnnotationUsage( ElementCollection.class ) ) {
if ( memberDetails.hasDirectAnnotationUsage( Lob.class ) ) {
if ( !memberDetails.getType().isImplementor( Map.class ) ) {
throw new EnversMappingException(
"@ElementCollection combined with @Lob is only supported for Map collection types."
);
@ -672,8 +679,8 @@ public class AuditedPropertiesReader {
String.format(
Locale.ENGLISH,
"Invalid mapping in [%s] for property [%s]",
property.getDeclaringClass().getName(),
property.getName()
memberDetails.getDeclaringType().getName(),
memberDetails.resolveAttributeName()
),
e
);
@ -681,23 +688,23 @@ public class AuditedPropertiesReader {
}
protected boolean checkAudited(
XProperty property,
MemberDetails memberDetails,
PropertyAuditingData propertyData, String propertyName,
Audited allClassAudited, String modifiedFlagSuffix) {
// Checking if this property is explicitly audited or if all properties are.
Audited aud = ( property.isAnnotationPresent( Audited.class ) )
? property.getAnnotation( Audited.class )
Audited aud = ( memberDetails.hasDirectAnnotationUsage( Audited.class ) )
? memberDetails.getDirectAnnotationUsage( Audited.class )
: allClassAudited;
if ( aud == null
&& overriddenAuditedProperties.contains( property )
&& !overriddenNotAuditedProperties.contains( property ) ) {
&& overriddenAuditedProperties.contains( memberDetails )
&& !overriddenNotAuditedProperties.contains( memberDetails ) ) {
// Assigning @Audited defaults. If anyone needs to customize those values in the future,
// appropriate fields shall be added to @AuditOverride annotation.
aud = DEFAULT_AUDITED;
}
if ( aud != null ) {
propertyData.setRelationTargetAuditMode( aud.targetAuditMode() );
propertyData.setRelationTargetNotFoundAction( getRelationNotFoundAction( property, allClassAudited ) );
propertyData.setRelationTargetNotFoundAction( getRelationNotFoundAction( memberDetails, allClassAudited ) );
propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) );
propertyData.setModifiedFlagName( ModifiedColumnNameResolver.getName( propertyName, modifiedFlagSuffix ) );
if ( !StringTools.isEmpty( aud.modifiedColumnName() ) ) {
@ -748,15 +755,15 @@ public class AuditedPropertiesReader {
return aud.withModifiedFlag();
}
private void setPropertyRelationMappedBy(XProperty property, PropertyAuditingData propertyData) {
final OneToMany oneToMany = property.getAnnotation( OneToMany.class );
private void setPropertyRelationMappedBy(MemberDetails memberDetails, PropertyAuditingData propertyData) {
final OneToMany oneToMany = memberDetails.getDirectAnnotationUsage( OneToMany.class );
if ( oneToMany != null && StringHelper.isNotEmpty( oneToMany.mappedBy() ) ) {
propertyData.setRelationMappedBy( oneToMany.mappedBy() );
}
}
private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) {
final AuditMappedBy auditMappedBy = property.getAnnotation( AuditMappedBy.class );
private void setPropertyAuditMappedBy(MemberDetails memberDetails, PropertyAuditingData propertyData) {
final AuditMappedBy auditMappedBy = memberDetails.getDirectAnnotationUsage( AuditMappedBy.class );
if ( auditMappedBy != null ) {
propertyData.setAuditMappedBy( auditMappedBy.mappedBy() );
if ( StringHelper.isNotEmpty( auditMappedBy.positionMappedBy() ) ) {
@ -765,20 +772,20 @@ public class AuditedPropertiesReader {
}
}
private void addPropertyMapKey(XProperty property, PropertyAuditingData propertyData) {
final MapKey mapKey = property.getAnnotation( MapKey.class );
private void addPropertyMapKey(MemberDetails memberDetails, PropertyAuditingData propertyData) {
final MapKey mapKey = memberDetails.getDirectAnnotationUsage( MapKey.class );
if ( mapKey != null ) {
propertyData.setMapKey( mapKey.name() );
}
else {
final MapKeyEnumerated mapKeyEnumerated = property.getAnnotation( MapKeyEnumerated.class );
final MapKeyEnumerated mapKeyEnumerated = memberDetails.getDirectAnnotationUsage( MapKeyEnumerated.class );
if ( mapKeyEnumerated != null ) {
propertyData.setMapKeyEnumType( mapKeyEnumerated.value() );
}
}
}
private void addPropertyJoinTables(XProperty property, PropertyAuditingData propertyData) {
private void addPropertyJoinTables(MemberDetails memberDetails, PropertyAuditingData propertyData) {
// The AuditJoinTable annotation source will follow the following priority rules
// 1. Use the override if one is specified
// 2. Use the site annotation if one is specified
@ -788,12 +795,12 @@ public class AuditedPropertiesReader {
// the join-table specified there should have a higher priority in the event the
// super-class defines an equivalent @AuditJoinTable at the site/property level.
final AuditJoinTable overrideJoinTable = overriddenAuditedPropertiesJoinTables.get( property );
final AuditJoinTable overrideJoinTable = overriddenAuditedPropertiesJoinTables.get( memberDetails );
if ( overrideJoinTable != null ) {
propertyData.setJoinTable( new AuditJoinTableData( overrideJoinTable ) );
}
else {
final AuditJoinTable propertyJoinTable = property.getAnnotation( AuditJoinTable.class );
final AuditJoinTable propertyJoinTable = memberDetails.getDirectAnnotationUsage( AuditJoinTable.class );
if ( propertyJoinTable != null ) {
propertyData.setJoinTable( new AuditJoinTableData( propertyJoinTable ) );
}
@ -803,8 +810,8 @@ public class AuditedPropertiesReader {
}
}
private void addPropertyCollectionAuditTable(XProperty property, PropertyAuditingData propertyAuditingData) {
final CollectionAuditTable collectionAuditTableAnn = property.getAnnotation( CollectionAuditTable.class );
private void addPropertyCollectionAuditTable(MemberDetails memberDetails, PropertyAuditingData propertyAuditingData) {
final CollectionAuditTable collectionAuditTableAnn = memberDetails.getDirectAnnotationUsage( CollectionAuditTable.class );
if ( collectionAuditTableAnn != null ) {
propertyAuditingData.setCollectionAuditTable( collectionAuditTableAnn );
}
@ -813,15 +820,15 @@ public class AuditedPropertiesReader {
/**
* Add the {@link AuditOverride} annotations.
*
* @param property the property being processed
* @param memberDetails the property being processed
* @param propertyData the Envers auditing data for this property
*/
private void addPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) {
final AuditOverride annotationOverride = property.getAnnotation( AuditOverride.class );
private void addPropertyAuditingOverrides(MemberDetails memberDetails, PropertyAuditingData propertyData) {
final AuditOverride annotationOverride = memberDetails.getDirectAnnotationUsage( AuditOverride.class );
if ( annotationOverride != null ) {
propertyData.addAuditingOverride( annotationOverride );
}
final AuditOverrides annotationOverrides = property.getAnnotation( AuditOverrides.class );
final AuditOverrides annotationOverrides = memberDetails.getDirectAnnotationUsage( AuditOverrides.class );
if ( annotationOverrides != null ) {
propertyData.addAuditingOverrides( annotationOverrides );
}
@ -830,16 +837,16 @@ public class AuditedPropertiesReader {
/**
* Process the {@link AuditOverride} annotations for this property.
*
* @param property the property for which the {@link AuditOverride}
* @param memberDetails the property for which the {@link AuditOverride}
* annotations are being processed
* @param propertyData the Envers auditing data for this property
*
* @return {@code false} if isAudited() of the override annotation was set to
*/
private boolean processPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) {
private boolean processPropertyAuditingOverrides(MemberDetails memberDetails, PropertyAuditingData propertyData) {
// Only components register audit overrides, classes will have no entries.
for ( AuditOverrideData override : auditedPropertiesHolder.getAuditingOverrides() ) {
if ( property.getName().equals( override.getName() ) ) {
if ( memberDetails.resolveAttributeName().equals( override.getName() ) ) {
// the override applies to this property
if ( !override.isAudited() ) {
return false;
@ -872,24 +879,24 @@ public class AuditedPropertiesReader {
return true;
}
protected boolean isOverriddenNotAudited(XProperty property) {
return overriddenNotAuditedProperties.contains( property );
protected boolean isOverriddenNotAudited(MemberDetails memberDetails) {
return overriddenNotAuditedProperties.contains( memberDetails );
}
protected boolean isOverriddenNotAudited(XClass clazz) {
return overriddenNotAuditedClasses.contains( clazz );
protected boolean isOverriddenNotAudited(ClassDetails classDetails) {
return overriddenNotAuditedClasses.contains( classDetails );
}
protected boolean isOverriddenAudited(XProperty property) {
return overriddenAuditedProperties.contains( property );
protected boolean isOverriddenAudited(MemberDetails memberDetails) {
return overriddenAuditedProperties.contains( memberDetails );
}
protected boolean isOverriddenAudited(XClass clazz) {
return overriddenAuditedClasses.contains( clazz );
protected boolean isOverriddenAudited(ClassDetails classDetails) {
return overriddenAuditedClasses.contains( classDetails );
}
private RelationTargetNotFoundAction getRelationNotFoundAction(XProperty property, Audited classAudited) {
final Audited propertyAudited = property.getAnnotation( Audited.class );
private RelationTargetNotFoundAction getRelationNotFoundAction(MemberDetails memberDetails, Audited classAudited) {
final Audited propertyAudited = memberDetails.getDirectAnnotationUsage( Audited.class );
// class isn't annotated, check property
if ( classAudited == null ) {

View File

@ -6,13 +6,12 @@
*/
package org.hibernate.envers.configuration.internal.metadata.reader;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.Audited;
import org.hibernate.envers.boot.internal.ModifiedColumnNameResolver;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.MappedSuperclass;
@ -56,13 +55,13 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
@Override
protected boolean checkAudited(
XProperty property,
MemberDetails memberDetails,
PropertyAuditingData propertyData,
String propertyName,
Audited allClassAudited,
String modifiedFlagSuffix) {
// Checking if this property is explicitly audited or if all properties are.
final Audited aud = property.getAnnotation( Audited.class );
final Audited aud = memberDetails.getDirectAnnotationUsage( Audited.class );
if ( aud != null ) {
propertyData.setRelationTargetAuditMode( aud.targetAuditMode() );
propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) );
@ -74,7 +73,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
else {
// get declaring class for property
final XClass declaringClass = property.getDeclaringClass();
final ClassDetails declaringClass = memberDetails.getDeclaringType();
// check component data to make sure that no audit overrides were not defined at
// the parent class or the embeddable at the property level.
@ -88,7 +87,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
if ( "".equals( auditOverride.getName() ) ) {
classNotAuditedOverride = true;
}
if ( property.getName().equals( auditOverride.getName() ) ) {
if ( memberDetails.resolveAttributeName().equals( auditOverride.getName() ) ) {
return false;
}
}
@ -96,7 +95,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
if ( "".equals( auditOverride.getName() ) ) {
classAuditedOverride = true;
}
if ( property.getName().equals( auditOverride.getName() ) ) {
if ( memberDetails.resolveAttributeName().equals( auditOverride.getName() ) ) {
return true;
}
}
@ -105,12 +104,12 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
}
// make sure taht if the class or property are explicitly 'isAudited=false', use that.
if ( classNotAuditedOverride || isOverriddenNotAudited( property) || isOverriddenNotAudited( declaringClass ) ) {
if ( classNotAuditedOverride || isOverriddenNotAudited( memberDetails ) || isOverriddenNotAudited( declaringClass ) ) {
return false;
}
// make sure that if the class or property are explicitly 'isAudited=true', use that.
if ( classAuditedOverride || isOverriddenAudited( property ) || isOverriddenAudited( declaringClass ) ) {
if ( classAuditedOverride || isOverriddenAudited( memberDetails ) || isOverriddenAudited( declaringClass ) ) {
return true;
}
@ -122,7 +121,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
// assumption here is if a component reader is looking at a @MappedSuperclass, it should be treated
// as not being audited if we have reached htis point; allowing components and any @Embeddable
// class being audited by default.
if ( declaringClass.isAnnotationPresent( MappedSuperclass.class ) ) {
if ( declaringClass.hasDirectAnnotationUsage( MappedSuperclass.class ) ) {
return false;
}
}

View File

@ -1,114 +0,0 @@
/*
* 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.configuration.internal.metadata.reader;
import java.lang.annotation.Annotation;
import java.util.Collection;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
/**
* This class pretends to be property but in fact it represents entry in the map (for dynamic component)
*
* @author Lukasz Zuchowski (author at zuchos dot com)
* @author Chris Cranford
*/
public class DynamicProperty implements XProperty {
private final PersistentPropertiesSource source;
private final String propertyName;
public DynamicProperty(PersistentPropertiesSource source, String propertyName) {
this.source = source;
this.propertyName = propertyName;
}
@Override
public XClass getDeclaringClass() {
return source.getXClass();
}
@Override
public String getName() {
return propertyName;
}
@Override
public boolean isCollection() {
return false;
}
@Override
public boolean isArray() {
return false;
}
@Override
public Class<? extends Collection<?>> getCollectionClass() {
return null;
}
@Override
public XClass getType() {
return source.getXClass();
}
@Override
public XClass getElementClass() {
return null;
}
@Override
public XClass getClassOrElementClass() {
return null;
}
@Override
public XClass getMapKey() {
return null;
}
@Override
public int getModifiers() {
return 0;
}
@Override
public void setAccessible(boolean accessible) {
}
@Override
public Object invoke(Object target, Object... parameters) {
return null;
}
@Override
public Object invoke(Object target) {
return null;
}
@Override
public boolean isTypeResolved() {
return false;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
}

View File

@ -8,14 +8,13 @@ package org.hibernate.envers.configuration.internal.metadata.reader;
import java.util.Iterator;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.envers.boot.EnversMappingException;
import org.hibernate.envers.boot.registry.classloading.ClassLoaderAccessHelper;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.models.spi.ClassDetails;
/**
* A source of data on persistent properties of a class or component.
@ -28,7 +27,7 @@ public interface PersistentPropertiesSource {
Property getProperty(String propertyName);
XClass getXClass();
ClassDetails getClassDetails();
boolean isDynamicComponent();
@ -38,10 +37,10 @@ public interface PersistentPropertiesSource {
* Get a persistent properties source for a persistent class.
*
* @param persistentClass the persistent class
* @param clazz the class
* @param classDetails the class details
* @return the properties source
*/
static PersistentPropertiesSource forClass(PersistentClass persistentClass, XClass clazz) {
static PersistentPropertiesSource forClass(PersistentClass persistentClass, ClassDetails classDetails) {
return new PersistentPropertiesSource() {
@Override
public Iterator<Property> getPropertyIterator() {
@ -54,8 +53,8 @@ public interface PersistentPropertiesSource {
}
@Override
public XClass getXClass() {
return clazz;
public ClassDetails getClassDetails() {
return classDetails;
}
@Override
@ -80,9 +79,9 @@ public interface PersistentPropertiesSource {
*/
static PersistentPropertiesSource forComponent(EnversMetadataBuildingContext context, Component component, boolean dynamic) {
try {
Class<?> componentClass = ClassLoaderAccessHelper.loadClass( context, component.getComponentClassName() );
XClass clazz = context.getReflectionManager().toXClass( componentClass );
return forComponent( component, clazz, dynamic );
final ClassDetails classDetails = context.getClassDetailsRegistry()
.resolveClassDetails( component.getComponentClassName() );
return forComponent( component, classDetails, dynamic );
}
catch (ClassLoadingException e) {
throw new EnversMappingException( e );
@ -97,11 +96,11 @@ public interface PersistentPropertiesSource {
* Get a persistent properties source for a component with its class already resolved.
*
* @param component the component
* @param clazz the class
* @param classDetails the class details
* @param dynamic whether the component is dynamic or not
* @return the properties source
*/
static PersistentPropertiesSource forComponent(Component component, XClass clazz, boolean dynamic) {
static PersistentPropertiesSource forComponent(Component component, ClassDetails classDetails, boolean dynamic) {
return new PersistentPropertiesSource() {
@Override
public Iterator<Property> getPropertyIterator() {
@ -114,8 +113,8 @@ public interface PersistentPropertiesSource {
}
@Override
public XClass getXClass() {
return clazz;
public ClassDetails getClassDetails() {
return classDetails;
}
@Override

View File

@ -10,8 +10,6 @@ import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Map;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.envers.exception.AuditException;
@ -130,36 +128,6 @@ public abstract class ReflectionTools {
);
}
/**
* @param clazz Source class.
* @param propertyName Property name.
*
* @return Property object or {@code null} if none with expected name has been found.
*/
public static XProperty getProperty(XClass clazz, String propertyName) {
XProperty property = getProperty( clazz, propertyName, "field" );
if ( property == null ) {
property = getProperty( clazz, propertyName, "property" );
}
return property;
}
/**
* @param clazz Source class.
* @param propertyName Property name.
* @param accessType Expected access type. Legal values are <i>field</i> and <i>property</i>.
*
* @return Property object or {@code null} if none with expected name and access type has been found.
*/
public static XProperty getProperty(XClass clazz, String propertyName, String accessType) {
for ( XProperty property : clazz.getDeclaredProperties( accessType ) ) {
if ( propertyName.equals( property.getName() ) ) {
return property;
}
}
return null;
}
/**
* Locate class with a given name.
*