44 */
55package org .hibernate .envers .configuration .internal .metadata ;
66
7- import java .lang .invoke .MethodHandles ;
8- import java .util .List ;
9- import java .util .Locale ;
10- import java .util .Objects ;
11-
127import org .hibernate .envers .boot .EnversMappingException ;
8+ import org .hibernate .envers .boot .spi .EnversMetadataBuildingContext ;
9+ import org .hibernate .envers .configuration .internal .metadata .reader .AuditedPropertiesHolder ;
10+ import org .hibernate .envers .configuration .internal .metadata .reader .ClassAuditingData ;
11+ import org .hibernate .envers .configuration .internal .metadata .reader .ComponentAuditingData ;
1312import org .hibernate .envers .configuration .internal .metadata .reader .PropertyAuditingData ;
1413import org .hibernate .envers .internal .EnversMessageLogger ;
1514import org .hibernate .mapping .Collection ;
1615import org .hibernate .mapping .Component ;
1716import org .hibernate .mapping .KeyValue ;
18- import org .hibernate .mapping .ManyToOne ;
19- import org .hibernate .mapping .OneToMany ;
2017import org .hibernate .mapping .PersistentClass ;
2118import org .hibernate .mapping .Property ;
2219import org .hibernate .mapping .Selectable ;
2320import org .hibernate .mapping .Table ;
24-
2521import org .jboss .logging .Logger ;
2622
23+ import java .lang .invoke .MethodHandles ;
24+ import java .util .List ;
25+ import java .util .Locale ;
26+ import java .util .Objects ;
27+
2728/**
2829 * Helper class that provides a way to resolve the {@code mappedBy} attribute for collections.
2930 *
@@ -37,26 +38,51 @@ public class CollectionMappedByResolver {
3738 CollectionMappedByResolver .class .getName ()
3839 );
3940
40- public static String resolveMappedBy (Collection collection , PropertyAuditingData propertyAuditingData ) {
41- final PersistentClass referencedClass = getReferenceCollectionClass ( collection );
41+ public static String resolveMappedBy (
42+ String entityName ,
43+ Collection collection ,
44+ PropertyAuditingData propertyAuditingData ,
45+ String referencedEntityName ,
46+ EnversMetadataBuildingContext buildingContext ) {
47+ final var referencedClass = getReferencedClass ( referencedEntityName , buildingContext );
4248 final ResolverContext resolverContext = new ResolverContext ( collection , propertyAuditingData );
43- return getMappedBy ( referencedClass , resolverContext );
49+ return getMappedBy ( entityName , referencedClass , resolverContext , buildingContext );
4450 }
4551
46- public static String resolveMappedBy (Table collectionTable , PersistentClass referencedClass , PropertyAuditingData propertyAuditingData ) {
47- return getMappedBy ( referencedClass , new ResolverContext ( collectionTable , propertyAuditingData ) );
52+ public static String resolveMappedBy (
53+ String entityName ,
54+ Table collectionTable ,
55+ PropertyAuditingData propertyAuditingData ,
56+ String referencedEntityName ,
57+ EnversMetadataBuildingContext buildingContext ) {
58+ final var referencedClass = getReferencedClass ( referencedEntityName , buildingContext );
59+ return getMappedBy (
60+ entityName ,
61+ referencedClass ,
62+ new ResolverContext ( collectionTable , propertyAuditingData ),
63+ buildingContext
64+ );
4865 }
4966
50- public static boolean isMappedByKey (Collection collection , String mappedBy ) {
51- final PersistentClass referencedClass = getReferenceCollectionClass ( collection );
67+ public static boolean isMappedByKey (
68+ Collection collection ,
69+ String mappedBy ,
70+ String referencedEntityName ,
71+ EnversMetadataBuildingContext buildingContext ) {
72+ final PersistentClass referencedClass = getReferencedClass ( referencedEntityName ,
73+ buildingContext ).getPersistentClass ();
5274 if ( referencedClass != null ) {
5375 final String keyMappedBy = searchMappedByKey ( referencedClass , collection );
5476 return mappedBy .equals ( keyMappedBy );
5577 }
5678 return false ;
5779 }
5880
59- private static String getMappedBy (PersistentClass referencedClass , ResolverContext resolverContext ) {
81+ private static String getMappedBy (
82+ String entityName ,
83+ ClassAuditingData referencedClass ,
84+ ResolverContext resolverContext ,
85+ EnversMetadataBuildingContext buildingContext ) {
6086 // If there's an @AuditMappedBy specified, returning it directly.
6187 final String auditMappedBy = resolverContext .propertyAuditingData .getAuditMappedBy ();
6288 if ( auditMappedBy != null ) {
@@ -70,74 +96,93 @@ private static String getMappedBy(PersistentClass referencedClass, ResolverConte
7096 LOG .debugf (
7197 "Going to search the mapped by attribute for %s in superclasses of entity: %s" ,
7298 resolverContext .propertyAuditingData .getName (),
73- referencedClass .getClassName ()
99+ referencedClass .getEntityName ()
74100 );
75101
76- PersistentClass tempClass = referencedClass ;
77- while ( mappedBy == null && tempClass .getSuperclass () != null ) {
78- LOG .debugf ( "Searching in superclass: %s" , tempClass .getSuperclass ().getClassName () );
79- mappedBy = searchMappedBy ( tempClass .getSuperclass (), resolverContext );
102+ PersistentClass tempClass = referencedClass .getPersistentClass ().getSuperclass ();
103+ while ( mappedBy == null && tempClass != null ) {
104+ final var superclassName = tempClass .getEntityName ();
105+ LOG .debugf ( "Searching in superclass: %s" , superclassName );
106+ final var auditingData = buildingContext .getClassesAuditingData ().getClassAuditingData ( superclassName );
107+ mappedBy = searchMappedBy ( auditingData , resolverContext );
80108 tempClass = tempClass .getSuperclass ();
81109 }
82110 }
83111
84112 if ( mappedBy == null ) {
85113 throw new EnversMappingException (
86114 String .format (
87- Locale .ENGLISH ,
88- "Unable to read mapped by attribute for %s in %s!" ,
115+ Locale .ROOT ,
116+ "Could not resolve mapped by property for association [%s.%s] in the referenced entity [%s],"
117+ + " please ensure that the association is audited on both sides." ,
118+ entityName ,
89119 resolverContext .propertyAuditingData .getName (),
90- referencedClass .getClassName ()
120+ referencedClass .getEntityName ()
91121 )
92122 );
93123 }
94124
95125 return mappedBy ;
96126 }
97127
98- private static String searchMappedBy (PersistentClass persistentClass , ResolverContext resolverContext ) {
128+ private static String searchMappedBy (ClassAuditingData referencedClass , ResolverContext resolverContext ) {
99129 if ( resolverContext .getCollection () != null ) {
100- return searchMappedBy ( persistentClass , resolverContext .getCollection () );
130+ return searchMappedBy ( referencedClass , resolverContext .getCollection () );
101131 }
102- return searchMappedBy ( persistentClass , resolverContext .getTable () );
132+ return searchMappedBy ( referencedClass , resolverContext .getTable () );
103133 }
104134
105- private static String searchMappedBy (PersistentClass referencedClass , Collection collectionValue ) {
106- final List <Property > assocClassProps = referencedClass .getProperties ();
135+ private static String searchMappedBy (ClassAuditingData referencedClass , Collection collectionValue ) {
136+ final var persistentClass = referencedClass .getPersistentClass ();
137+ final List <Property > assocClassProps = referencedClass .getPersistentClass ().getProperties ();
107138 for ( Property property : assocClassProps ) {
108139 final List <Selectable > assocClassSelectables = property .getValue ().getSelectables ();
109140 final List <Selectable > collectionKeySelectables = collectionValue .getKey ().getSelectables ();
110141 if ( Objects .equals ( assocClassSelectables , collectionKeySelectables ) ) {
111- return property .getName ();
142+ final var propertyName = property .getName ();
143+ // We need to check if the property is audited as well
144+ return referencedClass .contains ( propertyName ) ? propertyName : null ;
112145 }
113146 }
114147 // HHH-7625
115148 // Support ToOne relations with mappedBy that point to an @IdClass key property.
116- return searchMappedByKey ( referencedClass , collectionValue );
149+ return searchMappedByKey ( persistentClass , collectionValue );
117150 }
118151
119- private static String searchMappedBy (PersistentClass referencedClass , Table collectionTable ) {
120- return searchMappedBy ( referencedClass .getProperties (), collectionTable );
152+ private static String searchMappedBy (ClassAuditingData referencedClass , Table collectionTable ) {
153+ return searchMappedBy ( referencedClass , referencedClass . getPersistentClass () .getProperties (), collectionTable );
121154 }
122155
123- private static String searchMappedBy (List <Property > properties , Table collectionTable ) {
156+ private static String searchMappedBy (AuditedPropertiesHolder propertiesHolder , List <Property > properties , Table collectionTable ) {
124157 for ( Property property : properties ) {
125158 if ( property .getValue () instanceof Collection ) {
126159 // The equality is intentional. We want to find a collection property with the same collection table.
127160 //noinspection ObjectEquality
128- if ( ( (Collection ) property .getValue () ).getCollectionTable () == collectionTable ) {
129- return property .getName ();
161+ if ( ((Collection ) property .getValue ()).getCollectionTable () == collectionTable ) {
162+ final var propertyName = property .getName ();
163+ // We need to check if the property is audited as well
164+ return propertiesHolder .contains ( propertyName ) ? propertyName : null ;
130165 }
131166 }
132- else if ( property .getValue () instanceof Component ) {
167+ else if ( property .getValue () instanceof Component component ) {
133168 // HHH-12240
134169 // Should we find an embeddable, we should traverse it as well to see if the collection table
135170 // happens to be an attribute inside the embeddable rather than directly on the entity.
136- final Component component = (Component ) property .getValue ();
137-
138- final String mappedBy = searchMappedBy ( component .getProperties (), collectionTable );
139- if ( mappedBy != null ) {
140- return property .getName () + "_" + mappedBy ;
171+ final var componentName = property .getName ();
172+ final var componentData = propertiesHolder .getPropertyAuditingData ( componentName );
173+ if ( componentData == null ) {
174+ // If the component is not audited, no need to check sub-properties
175+ return null ;
176+ }
177+ else {
178+ final String mappedBy = searchMappedBy (
179+ (ComponentAuditingData ) componentData ,
180+ component .getProperties (),
181+ collectionTable
182+ );
183+ if ( mappedBy != null ) {
184+ return property .getName () + "_" + mappedBy ;
185+ }
141186 }
142187 }
143188 }
@@ -161,18 +206,8 @@ private static String searchMappedByKey(PersistentClass referencedClass, Collect
161206 return null ;
162207 }
163208
164- private static PersistentClass getReferenceCollectionClass (Collection collectionValue ) {
165- PersistentClass referencedClass = null ;
166- if ( collectionValue .getElement () instanceof OneToMany ) {
167- final OneToMany oneToManyValue = (OneToMany ) collectionValue .getElement ();
168- referencedClass = oneToManyValue .getAssociatedClass ();
169- }
170- else if ( collectionValue .getElement () instanceof ManyToOne ) {
171- // Case for bidirectional relation with @JoinTable on the owning @ManyToOne side.
172- final ManyToOne manyToOneValue = (ManyToOne ) collectionValue .getElement ();
173- referencedClass = manyToOneValue .getMetadata ().getEntityBinding ( manyToOneValue .getReferencedEntityName () );
174- }
175- return referencedClass ;
209+ private static ClassAuditingData getReferencedClass (String className , EnversMetadataBuildingContext buildingContext ) {
210+ return buildingContext .getClassesAuditingData ().getClassAuditingData ( className );
176211 }
177212
178213 private static class ResolverContext {
0 commit comments