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 { 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( throw new HibernateException(

View File

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

View File

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

View File

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

View File

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

View File

@ -11,11 +11,9 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Callable; 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.classloading.spi.ClassLoadingException;
import org.hibernate.boot.registry.selector.spi.StrategySelector; 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.cfg.Environment;
import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.HSQLDialect;
import org.hibernate.envers.RevisionListener; import org.hibernate.envers.RevisionListener;
@ -75,7 +73,6 @@ public class Configuration {
private final boolean globalLegacyRelationTargetNotFound; private final boolean globalLegacyRelationTargetNotFound;
private final boolean trackEntitiesChanged; private final boolean trackEntitiesChanged;
private final JavaReflectionManager reflectionManager;
private boolean trackEntitiesOverride; private boolean trackEntitiesOverride;
private final String auditTablePrefix; private final String auditTablePrefix;
@ -98,7 +95,7 @@ public class Configuration {
private final RevisionInfoConfiguration revisionInfo; private final RevisionInfoConfiguration revisionInfo;
public Configuration(Properties properties, EnversService enversService, MetadataImplementor metadata) { public Configuration(Properties properties, EnversService enversService, InFlightMetadataCollector metadata) {
this.enversService = enversService; this.enversService = enversService;
final ConfigurationProperties configProps = new ConfigurationProperties( properties ); final ConfigurationProperties configProps = new ConfigurationProperties( properties );
@ -185,14 +182,7 @@ public class Configuration {
revisionPropertyBasePath = originalIdPropertyName + "." + revisionFieldName + "."; revisionPropertyBasePath = originalIdPropertyName + "." + revisionFieldName + ".";
revisionNumberPath = revisionPropertyBasePath + "id"; revisionNumberPath = revisionPropertyBasePath + "id";
// todo: there are places that need bits built from the revinfo entity configuration this.revisionInfo = new RevisionInfoConfiguration( this, metadata );
// 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;
} }
public boolean isGenerateRevisionsForCollections() { 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 java.util.Set;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.envers.Audited; import org.hibernate.envers.Audited;
import org.hibernate.envers.DefaultRevisionEntity; import org.hibernate.envers.DefaultRevisionEntity;
import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity; 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.envers.internal.revisioninfo.RevisionTimestampValueResolver;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; 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 org.hibernate.service.ServiceRegistry;
import static org.hibernate.envers.configuration.internal.ModelsHelper.getMemberDetails;
/** /**
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
@ -80,11 +82,11 @@ public class RevisionInfoConfiguration {
private final Class<?> revisionInfoClass; private final Class<?> revisionInfoClass;
private final boolean useDefaultRevisionInfoMapping; private final boolean useDefaultRevisionInfoMapping;
public RevisionInfoConfiguration(Configuration config, MetadataImplementor metadata, ReflectionManager reflectionManager) { public RevisionInfoConfiguration(Configuration config, InFlightMetadataCollector metadata) {
this.configuration = config; this.configuration = config;
// Generate the resolver metadata // Generate the resolver metadata
RevisionEntityResolver resolver = new RevisionEntityResolver( metadata, reflectionManager ); final RevisionEntityResolver resolver = new RevisionEntityResolver( metadata );
// initialize attributes from resolver // initialize attributes from resolver
this.revisionInfoClass = resolver.revisionInfoClass; this.revisionInfoClass = resolver.revisionInfoClass;
@ -262,8 +264,7 @@ public class RevisionInfoConfiguration {
private class RevisionEntityResolver { private class RevisionEntityResolver {
private final MetadataImplementor metadata; private final InFlightMetadataCollector metadata;
private final ReflectionManager reflectionManager;
private boolean revisionEntityFound; private boolean revisionEntityFound;
private boolean revisionNumberFound; private boolean revisionNumberFound;
@ -282,9 +283,8 @@ public class RevisionInfoConfiguration {
private String revisionPropSqlType; private String revisionPropSqlType;
private RevisionTimestampValueResolver timestampValueResolver; private RevisionTimestampValueResolver timestampValueResolver;
public RevisionEntityResolver(MetadataImplementor metadata, ReflectionManager reflectionManager) { public RevisionEntityResolver(InFlightMetadataCollector metadata) {
this.metadata = metadata; this.metadata = metadata;
this.reflectionManager = reflectionManager;
this.revisionInfoEntityName = getDefaultEntityName(); this.revisionInfoEntityName = getDefaultEntityName();
this.revisionInfoIdData = createPropertyData( "id", "field" ); this.revisionInfoIdData = createPropertyData( "id", "field" );
this.revisionInfoTimestampData = createPropertyData( "timestamp", "field" ); this.revisionInfoTimestampData = createPropertyData( "timestamp", "field" );
@ -312,8 +312,10 @@ public class RevisionInfoConfiguration {
continue; continue;
} }
XClass clazz = reflectionManager.toXClass( persistentClass.getMappedClass() ); final ClassDetails classDetails = metadata.getClassDetailsRegistry().resolveClassDetails(
final RevisionEntity revisionEntity = clazz.getAnnotation( RevisionEntity.class ); persistentClass.getClassName()
);
final RevisionEntity revisionEntity = classDetails.getDirectAnnotationUsage( RevisionEntity.class );
if ( revisionEntity == null ) { if ( revisionEntity == null ) {
// not annotated, skip // not annotated, skip
continue; continue;
@ -324,13 +326,13 @@ public class RevisionInfoConfiguration {
} }
// Verify that the revision entity isn't audited // 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" ); throw new EnversMappingException( "The @RevisionEntity entity cannot be audited" );
} }
revisionEntityFound = true; revisionEntityFound = true;
resolveConfiguration( clazz ); resolveConfiguration( classDetails );
if ( !revisionNumberFound || !revisionTimestampFound ) { if ( !revisionNumberFound || !revisionTimestampFound ) {
// A revision number and timestamp fields must be annotated or the revision entity mapping // A revision number and timestamp fields must be annotated or the revision entity mapping
@ -436,115 +438,116 @@ public class RevisionInfoConfiguration {
|| modifiedEntityNamesFound; || modifiedEntityNamesFound;
} }
private void resolveConfiguration(XClass clazz) { private void resolveConfiguration(ClassDetails classDetails) {
final XClass superclazz = clazz.getSuperclass(); final ClassDetails superclass = classDetails.getSuperClass();
if ( !Object.class.getName().equals( superclazz.getName() ) ) { if ( !Object.class.getName().equals( superclass.getName() ) ) {
// traverse to the top of the entity hierarchy // traverse to the top of the entity hierarchy
resolveConfiguration( superclazz ); resolveConfiguration( superclass );
} }
resolveConfigurationFromProperties( clazz, "field" ); resolveConfigurationFromProperties( classDetails, "field" );
resolveConfigurationFromProperties( clazz, "property" ); resolveConfigurationFromProperties( classDetails, "property" );
} }
private void resolveConfigurationFromProperties(XClass clazz, String accessType) { private void resolveConfigurationFromProperties(ClassDetails classDetails, String accessType) {
for ( XProperty property : clazz.getDeclaredProperties( accessType ) ) { for ( MemberDetails member : getMemberDetails( classDetails, accessType ) ) {
final RevisionNumber revisionNumber = property.getAnnotation( RevisionNumber.class ); final RevisionNumber revisionNumber = member.getDirectAnnotationUsage( RevisionNumber.class );
if ( revisionNumber != null ) { 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 ) { 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 ) { 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 ) { if ( revisionNumberFound ) {
throw new EnversMappingException( "Only one property can be defined with @RevisionNumber" ); throw new EnversMappingException( "Only one property can be defined with @RevisionNumber" );
} }
final XClass propertyType = property.getType(); final TypeDetails type = memberDetails.getType();
if ( isAnyType( propertyType, Integer.class, Integer.TYPE ) ) { if ( isAnyType( type, Integer.class, Integer.TYPE ) ) {
revisionInfoIdData = createPropertyData( property, accessType ); revisionInfoIdData = createPropertyData( memberDetails, accessType );
revisionNumberFound = true; revisionNumberFound = true;
} }
else if ( isAnyType( propertyType, Long.class, Long.TYPE ) ) { else if ( isAnyType( type, Long.class, Long.TYPE ) ) {
revisionInfoIdData = createPropertyData( property, accessType ); revisionInfoIdData = createPropertyData( memberDetails, accessType );
revisionPropType = "long"; revisionPropType = "long";
revisionNumberFound = true; revisionNumberFound = true;
} }
else { 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 // 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 // to generate the same mapping for the relation from an audit table's revision number to the
// revision entity's revision number field. // revision entity's revision number field.
final Column column = property.getAnnotation( Column.class ); final Column column = memberDetails.getDirectAnnotationUsage( Column.class );
if ( column != null ) { if ( column != null ) {
revisionPropSqlType = column.columnDefinition(); revisionPropSqlType = column.columnDefinition();
} }
} }
private void resolveRevisionTimestampFromProperty(XProperty property, String accessType) { private void resolveRevisionTimestampFromProperty(MemberDetails memberDetails, String accessType) {
if ( revisionTimestampFound ) { if ( revisionTimestampFound ) {
throw new EnversMappingException( "Only one property can be defined with @RevisionTimestamp" ); throw new EnversMappingException( "Only one property can be defined with @RevisionTimestamp" );
} }
final XClass propertyType = property.getType(); final TypeDetails type = memberDetails.getType();
if ( isAnyType( propertyType, Long.class, Long.TYPE, Date.class, LocalDateTime.class, Instant.class, java.sql.Date.class ) ) { if ( isAnyType( type, Long.class, Long.TYPE, Date.class, LocalDateTime.class, Instant.class, java.sql.Date.class ) ) {
revisionInfoTimestampData = createPropertyData( property, accessType ); revisionInfoTimestampData = createPropertyData( memberDetails, accessType );
revisionTimestampFound = true; revisionTimestampFound = true;
} }
else { 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 ) { if ( modifiedEntityNamesFound ) {
throw new EnversMappingException( "Only one property can be defined with @ModifiedEntityNames" ); throw new EnversMappingException( "Only one property can be defined with @ModifiedEntityNames" );
} }
final XClass propertyType = property.getType(); final TypeDetails type = memberDetails.getType();
if ( isAnyType( propertyType, Set.class ) ) { if ( isAnyType( type, Set.class ) ) {
final XClass elementType = property.getElementClass(); final TypeDetails elementType = memberDetails.getElementType();
if ( isAnyType( elementType, String.class ) ) { if ( isAnyType( elementType, String.class ) ) {
modifiedEntityNamesData = createPropertyData( property, accessType ); modifiedEntityNamesData = createPropertyData( memberDetails, accessType );
modifiedEntityNamesFound = true; modifiedEntityNamesFound = true;
return; return;
} }
} }
throwUnexpectedAnnotatedType( property, ModifiedEntityNames.class, "Set<String>" ); throwUnexpectedAnnotatedType( memberDetails, ModifiedEntityNames.class, "Set<String>" );
} }
private PropertyData createPropertyData(XProperty property, String accessType) { private PropertyData createPropertyData(MemberDetails memberDetails, String accessType) {
return createPropertyData( property.getName(), accessType ); return createPropertyData( memberDetails.resolveAttributeName(), accessType );
} }
private PropertyData createPropertyData(String name, String accessType) { private PropertyData createPropertyData(String name, String accessType) {
return new PropertyData( name, name, 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 ) { for ( Class<?> type : types ) {
if ( isType( clazz, type ) ) { if ( isType( typeDetails, type ) ) {
return true; return true;
} }
} }
return false; return false;
} }
private boolean isType(XClass clazz, Class<?> type) { private boolean isType(TypeDetails typeDetails, Class<?> type) {
return reflectionManager.equals( clazz, 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) { private Class<? extends RevisionListener> getRevisionListenerClass(Class<? extends RevisionListener> defaultListener) {
@ -554,12 +557,12 @@ public class RevisionInfoConfiguration {
return defaultListener; return defaultListener;
} }
private void throwUnexpectedAnnotatedType(XProperty property, Class<?> annotation, String allowedTypes) { private void throwUnexpectedAnnotatedType(MemberDetails memberDetails, Class<?> annotation, String allowedTypes) {
throw new EnversMappingException( throw new EnversMappingException(
String.format( String.format(
Locale.ENGLISH, Locale.ENGLISH,
"The field '%s' annotated with '@%s' must be of type: %s", "The field '%s' annotated with '@%s' must be of type: %s",
property.getName(), memberDetails.resolveAttributeName(),
annotation.getName(), annotation.getName(),
allowedTypes allowedTypes
) )

View File

@ -8,7 +8,6 @@ package org.hibernate.envers.configuration.internal.metadata.reader;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.envers.AuditTable; import org.hibernate.envers.AuditTable;
import org.hibernate.envers.Audited; import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.RelationTargetAuditMode;
@ -16,6 +15,7 @@ import org.hibernate.envers.SecondaryAuditTable;
import org.hibernate.envers.SecondaryAuditTables; import org.hibernate.envers.SecondaryAuditTables;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.mapping.PersistentClass; 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. * 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; this.metadataBuildingContext = metadataBuildingContext;
} }
private RelationTargetAuditMode getDefaultAudited(XClass clazz) { private RelationTargetAuditMode getDefaultAudited(ClassDetails classDetails) {
final Audited defaultAudited = clazz.getAnnotation( Audited.class ); final Audited defaultAudited = classDetails.getDirectAnnotationUsage( Audited.class );
if ( defaultAudited != null ) { if ( defaultAudited != null ) {
return defaultAudited.targetAuditMode(); return defaultAudited.targetAuditMode();
@ -42,8 +42,8 @@ public final class AnnotationsMetadataReader {
} }
} }
private void addAuditTable(ClassAuditingData auditData, XClass clazz) { private void addAuditTable(ClassAuditingData auditData, ClassDetails classDetails) {
final AuditTable auditTable = clazz.getAnnotation( AuditTable.class ); final AuditTable auditTable = classDetails.getDirectAnnotationUsage( AuditTable.class );
if ( auditTable != null ) { if ( auditTable != null ) {
auditData.setAuditTable( auditTable ); 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 // Getting information on secondary tables
final SecondaryAuditTable secondaryVersionsTable1 = clazz.getAnnotation( SecondaryAuditTable.class ); final SecondaryAuditTable secondaryVersionsTable1 = classDetails.getDirectAnnotationUsage( SecondaryAuditTable.class );
if ( secondaryVersionsTable1 != null ) { if ( secondaryVersionsTable1 != null ) {
auditData.getSecondaryTableDictionary().put( auditData.getSecondaryTableDictionary().put(
secondaryVersionsTable1.secondaryTableName(), 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 ) { if ( secondaryAuditTables != null ) {
for ( SecondaryAuditTable secondaryAuditTable2 : secondaryAuditTables.value() ) { for ( SecondaryAuditTable secondaryAuditTable2 : secondaryAuditTables.value() ) {
auditData.getSecondaryTableDictionary().put( auditData.getSecondaryTableDictionary().put(
@ -75,21 +75,23 @@ public final class AnnotationsMetadataReader {
public ClassAuditingData getAuditData(PersistentClass persistentClass) { public ClassAuditingData getAuditData(PersistentClass persistentClass) {
final ClassAuditingData auditData = new ClassAuditingData( 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 ) { if ( auditMode != null ) {
auditData.setDefaultAudited( true ); auditData.setDefaultAudited( true );
} }
new AuditedPropertiesReader( new AuditedPropertiesReader(
metadataBuildingContext, metadataBuildingContext,
PersistentPropertiesSource.forClass( persistentClass, xclass ), PersistentPropertiesSource.forClass( persistentClass, classDetails ),
auditData auditData
).read(); ).read();
addAuditTable( auditData, xclass ); addAuditTable( auditData, classDetails );
addAuditSecondaryTables( auditData, xclass ); addAuditSecondaryTables( auditData, classDetails );
return auditData; return auditData;
} }

View File

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

View File

@ -6,13 +6,12 @@
*/ */
package org.hibernate.envers.configuration.internal.metadata.reader; 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.Audited;
import org.hibernate.envers.boot.internal.ModifiedColumnNameResolver; import org.hibernate.envers.boot.internal.ModifiedColumnNameResolver;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.MappedSuperclass; import jakarta.persistence.MappedSuperclass;
@ -56,13 +55,13 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
@Override @Override
protected boolean checkAudited( protected boolean checkAudited(
XProperty property, MemberDetails memberDetails,
PropertyAuditingData propertyData, PropertyAuditingData propertyData,
String propertyName, String propertyName,
Audited allClassAudited, Audited allClassAudited,
String modifiedFlagSuffix) { String modifiedFlagSuffix) {
// Checking if this property is explicitly audited or if all properties are. // 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 ) { if ( aud != null ) {
propertyData.setRelationTargetAuditMode( aud.targetAuditMode() ); propertyData.setRelationTargetAuditMode( aud.targetAuditMode() );
propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) ); propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) );
@ -74,7 +73,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
else { else {
// get declaring class for property // 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 // check component data to make sure that no audit overrides were not defined at
// the parent class or the embeddable at the property level. // the parent class or the embeddable at the property level.
@ -88,7 +87,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
if ( "".equals( auditOverride.getName() ) ) { if ( "".equals( auditOverride.getName() ) ) {
classNotAuditedOverride = true; classNotAuditedOverride = true;
} }
if ( property.getName().equals( auditOverride.getName() ) ) { if ( memberDetails.resolveAttributeName().equals( auditOverride.getName() ) ) {
return false; return false;
} }
} }
@ -96,7 +95,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
if ( "".equals( auditOverride.getName() ) ) { if ( "".equals( auditOverride.getName() ) ) {
classAuditedOverride = true; classAuditedOverride = true;
} }
if ( property.getName().equals( auditOverride.getName() ) ) { if ( memberDetails.resolveAttributeName().equals( auditOverride.getName() ) ) {
return true; 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. // 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; return false;
} }
// make sure that if the class or property are explicitly 'isAudited=true', use that. // 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; 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 // 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 // as not being audited if we have reached htis point; allowing components and any @Embeddable
// class being audited by default. // class being audited by default.
if ( declaringClass.isAnnotationPresent( MappedSuperclass.class ) ) { if ( declaringClass.hasDirectAnnotationUsage( MappedSuperclass.class ) ) {
return false; 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 java.util.Iterator;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.envers.boot.EnversMappingException; import org.hibernate.envers.boot.EnversMappingException;
import org.hibernate.envers.boot.registry.classloading.ClassLoaderAccessHelper;
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.models.spi.ClassDetails;
/** /**
* A source of data on persistent properties of a class or component. * A source of data on persistent properties of a class or component.
@ -28,7 +27,7 @@ public interface PersistentPropertiesSource {
Property getProperty(String propertyName); Property getProperty(String propertyName);
XClass getXClass(); ClassDetails getClassDetails();
boolean isDynamicComponent(); boolean isDynamicComponent();
@ -38,10 +37,10 @@ public interface PersistentPropertiesSource {
* Get a persistent properties source for a persistent class. * Get a persistent properties source for a persistent class.
* *
* @param persistentClass the persistent class * @param persistentClass the persistent class
* @param clazz the class * @param classDetails the class details
* @return the properties source * @return the properties source
*/ */
static PersistentPropertiesSource forClass(PersistentClass persistentClass, XClass clazz) { static PersistentPropertiesSource forClass(PersistentClass persistentClass, ClassDetails classDetails) {
return new PersistentPropertiesSource() { return new PersistentPropertiesSource() {
@Override @Override
public Iterator<Property> getPropertyIterator() { public Iterator<Property> getPropertyIterator() {
@ -54,8 +53,8 @@ public interface PersistentPropertiesSource {
} }
@Override @Override
public XClass getXClass() { public ClassDetails getClassDetails() {
return clazz; return classDetails;
} }
@Override @Override
@ -80,9 +79,9 @@ public interface PersistentPropertiesSource {
*/ */
static PersistentPropertiesSource forComponent(EnversMetadataBuildingContext context, Component component, boolean dynamic) { static PersistentPropertiesSource forComponent(EnversMetadataBuildingContext context, Component component, boolean dynamic) {
try { try {
Class<?> componentClass = ClassLoaderAccessHelper.loadClass( context, component.getComponentClassName() ); final ClassDetails classDetails = context.getClassDetailsRegistry()
XClass clazz = context.getReflectionManager().toXClass( componentClass ); .resolveClassDetails( component.getComponentClassName() );
return forComponent( component, clazz, dynamic ); return forComponent( component, classDetails, dynamic );
} }
catch (ClassLoadingException e) { catch (ClassLoadingException e) {
throw new EnversMappingException( 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. * Get a persistent properties source for a component with its class already resolved.
* *
* @param component the component * @param component the component
* @param clazz the class * @param classDetails the class details
* @param dynamic whether the component is dynamic or not * @param dynamic whether the component is dynamic or not
* @return the properties source * @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() { return new PersistentPropertiesSource() {
@Override @Override
public Iterator<Property> getPropertyIterator() { public Iterator<Property> getPropertyIterator() {
@ -114,8 +113,8 @@ public interface PersistentPropertiesSource {
} }
@Override @Override
public XClass getXClass() { public ClassDetails getClassDetails() {
return clazz; return classDetails;
} }
@Override @Override

View File

@ -10,8 +10,6 @@ import java.lang.reflect.Field;
import java.util.Locale; import java.util.Locale;
import java.util.Map; 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.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.envers.exception.AuditException; 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. * Locate class with a given name.
* *