15
15
*/
16
16
package org .springframework .data .jpa .repository .support ;
17
17
18
- import java .util .ArrayList ;
19
- import java .util .Collections ;
20
- import java .util .Iterator ;
21
- import java .util .List ;
22
- import java .util .Optional ;
23
- import java .util .Set ;
24
-
25
18
import jakarta .persistence .IdClass ;
19
+ import jakarta .persistence .PersistenceUnitUtil ;
26
20
import jakarta .persistence .metamodel .Attribute ;
27
21
import jakarta .persistence .metamodel .EntityType ;
28
22
import jakarta .persistence .metamodel .IdentifiableType ;
29
23
import jakarta .persistence .metamodel .ManagedType ;
30
24
import jakarta .persistence .metamodel .Metamodel ;
31
25
import jakarta .persistence .metamodel .SingularAttribute ;
32
26
import jakarta .persistence .metamodel .Type ;
33
- import jakarta .persistence .metamodel .Type .PersistenceType ;
27
+
28
+ import java .util .ArrayList ;
29
+ import java .util .Collections ;
30
+ import java .util .Iterator ;
31
+ import java .util .List ;
32
+ import java .util .Optional ;
33
+ import java .util .Set ;
34
34
35
35
import org .springframework .beans .BeanWrapper ;
36
- import org .springframework .beans .BeanWrapperImpl ;
37
36
import org .springframework .core .annotation .AnnotationUtils ;
38
37
import org .springframework .data .jpa .provider .PersistenceProvider ;
39
38
import org .springframework .data .jpa .util .JpaMetamodel ;
40
39
import org .springframework .data .util .DirectFieldAccessFallbackBeanWrapper ;
41
- import org .springframework .data .util .ProxyUtils ;
42
40
import org .springframework .lang .Nullable ;
43
41
import org .springframework .util .Assert ;
44
42
51
49
* @author Christoph Strobl
52
50
* @author Mark Paluch
53
51
* @author Jens Schauder
52
+ * @author Greg Turnquist
54
53
*/
55
54
public class JpaMetamodelEntityInformation <T , ID > extends JpaEntityInformationSupport <T , ID > {
56
55
57
56
private final IdMetadata <T > idMetadata ;
58
57
private final Optional <SingularAttribute <? super T , ?>> versionAttribute ;
59
58
private final Metamodel metamodel ;
60
59
private final @ Nullable String entityName ;
60
+ private final PersistenceUnitUtil persistenceUnitUtil ;
61
61
62
62
/**
63
63
* Creates a new {@link JpaMetamodelEntityInformation} for the given domain class and {@link Metamodel}.
64
- *
64
+ *
65
65
* @param domainClass must not be {@literal null}.
66
66
* @param metamodel must not be {@literal null}.
67
+ * @param persistenceUnitUtil must not be {@literal null}.
67
68
*/
68
- public JpaMetamodelEntityInformation (Class <T > domainClass , Metamodel metamodel ) {
69
+ public JpaMetamodelEntityInformation (Class <T > domainClass , Metamodel metamodel ,
70
+ PersistenceUnitUtil persistenceUnitUtil ) {
69
71
70
72
super (domainClass );
71
73
@@ -88,6 +90,9 @@ public JpaMetamodelEntityInformation(Class<T> domainClass, Metamodel metamodel)
88
90
89
91
this .idMetadata = new IdMetadata <>(identifiableType , PersistenceProvider .fromMetamodel (metamodel ));
90
92
this .versionAttribute = findVersionAttribute (identifiableType , metamodel );
93
+
94
+ Assert .notNull (persistenceUnitUtil , "PersistenceUnitUtil must not be null" );
95
+ this .persistenceUnitUtil = persistenceUnitUtil ;
91
96
}
92
97
93
98
@ Override
@@ -147,27 +152,25 @@ public ID getId(T entity) {
147
152
return (ID ) persistenceProvider .getIdentifierFrom (entity );
148
153
}
149
154
150
- // if not a proxy use Spring mechanics to access the id.
151
- BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper (entity );
152
-
155
+ // If it's a simple type, then immediately delegate to the provider
153
156
if (idMetadata .hasSimpleId ()) {
154
- return (ID ) entityWrapper . getPropertyValue ( idMetadata . getSimpleIdAttribute (). getName () );
157
+ return (ID ) persistenceUnitUtil . getIdentifier ( entity );
155
158
}
156
159
157
- BeanWrapper idWrapper = new IdentifierDerivingDirectFieldAccessFallbackBeanWrapper (idMetadata .getType (), metamodel );
160
+ // otherwise, check if the complex id type has any partially filled fields
161
+ BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper (entity );
158
162
boolean partialIdValueFound = false ;
159
163
160
164
for (SingularAttribute <? super T , ?> attribute : idMetadata ) {
165
+
161
166
Object propertyValue = entityWrapper .getPropertyValue (attribute .getName ());
162
167
163
168
if (propertyValue != null ) {
164
169
partialIdValueFound = true ;
165
170
}
166
-
167
- idWrapper .setPropertyValue (attribute .getName (), propertyValue );
168
171
}
169
172
170
- return partialIdValueFound ? (ID ) idWrapper . getWrappedInstance ( ) : null ;
173
+ return partialIdValueFound ? (ID ) persistenceUnitUtil . getIdentifier ( entity ) : null ;
171
174
}
172
175
173
176
@ Override
@@ -209,7 +212,7 @@ public Object getCompositeIdAttributeValue(Object id, String idAttribute) {
209
212
@ Override
210
213
public boolean isNew (T entity ) {
211
214
212
- if (! versionAttribute .isPresent ()
215
+ if (versionAttribute .isEmpty ()
213
216
|| versionAttribute .map (Attribute ::getJavaType ).map (Class ::isPrimitive ).orElse (false )) {
214
217
return super .isNew (entity );
215
218
}
@@ -237,9 +240,9 @@ private static class IdMetadata<T> implements Iterable<SingularAttribute<? super
237
240
238
241
this .type = source ;
239
242
this .idClassAttributes = persistenceProvider .getIdClassAttributes (source );
240
- this .attributes = ( Set < SingularAttribute <? super T , ?>>) ( source .hasSingleIdAttribute ()
243
+ this .attributes = source .hasSingleIdAttribute ()
241
244
? Collections .singleton (source .getId (source .getIdType ().getJavaType ()))
242
- : source .getIdClassAttributes ()) ;
245
+ : source .getIdClassAttributes ();
243
246
}
244
247
245
248
boolean hasSimpleId () {
@@ -298,121 +301,4 @@ private static Class<?> lookupIdClass(IdentifiableType<?> type) {
298
301
return attributes .iterator ();
299
302
}
300
303
}
301
-
302
- /**
303
- * Custom extension of {@link DirectFieldAccessFallbackBeanWrapper} that allows to derive the identifier if composite
304
- * keys with complex key attribute types (e.g. types that are annotated with {@code @Entity} themselves) are used.
305
- *
306
- * @author Thomas Darimont
307
- */
308
- private static class IdentifierDerivingDirectFieldAccessFallbackBeanWrapper
309
- extends DirectFieldAccessFallbackBeanWrapper {
310
-
311
- private final Metamodel metamodel ;
312
- private final JpaMetamodel jpaMetamodel ;
313
-
314
- IdentifierDerivingDirectFieldAccessFallbackBeanWrapper (Class <?> type , Metamodel metamodel ) {
315
- super (type );
316
- this .metamodel = metamodel ;
317
- this .jpaMetamodel = JpaMetamodel .of (metamodel );
318
- }
319
-
320
- /**
321
- * In addition to the functionality described in {@link BeanWrapperImpl} it is checked whether we have a nested
322
- * entity that is part of the id key. If this is the case, we need to derive the identifier of the nested entity.
323
- */
324
- @ Override
325
- @ SuppressWarnings ({ "unchecked" , "rawtypes" })
326
- public void setPropertyValue (String propertyName , @ Nullable Object value ) {
327
-
328
- if (!isIdentifierDerivationNecessary (value )) {
329
- super .setPropertyValue (propertyName , value );
330
- return ;
331
- }
332
-
333
- // Derive the identifier from the nested entity that is part of the composite key.
334
- JpaMetamodelEntityInformation nestedEntityInformation = new JpaMetamodelEntityInformation (
335
- ProxyUtils .getUserClass (value ), this .metamodel );
336
-
337
- if (!nestedEntityInformation .getJavaType ().isAnnotationPresent (IdClass .class )) {
338
-
339
- Object nestedIdPropertyValue = new DirectFieldAccessFallbackBeanWrapper (value )
340
- .getPropertyValue (nestedEntityInformation .getRequiredIdAttribute ().getName ());
341
- super .setPropertyValue (propertyName , nestedIdPropertyValue );
342
- return ;
343
- }
344
-
345
- // We have an IdClass property, we need to inspect the current value in order to map potentially multiple id
346
- // properties correctly.
347
-
348
- BeanWrapper sourceIdValueWrapper = new DirectFieldAccessFallbackBeanWrapper (value );
349
- BeanWrapper targetIdClassTypeWrapper = new BeanWrapperImpl (nestedEntityInformation .getIdType ());
350
-
351
- for (String idAttributeName : (Iterable <String >) nestedEntityInformation .getIdAttributeNames ()) {
352
- targetIdClassTypeWrapper .setPropertyValue (idAttributeName ,
353
- extractActualIdPropertyValue (sourceIdValueWrapper , idAttributeName ));
354
- }
355
-
356
- super .setPropertyValue (propertyName , targetIdClassTypeWrapper .getWrappedInstance ());
357
- }
358
-
359
- @ Nullable
360
- private Object extractActualIdPropertyValue (BeanWrapper sourceIdValueWrapper , String idAttributeName ) {
361
-
362
- Object idPropertyValue = sourceIdValueWrapper .getPropertyValue (idAttributeName );
363
-
364
- if (idPropertyValue != null ) {
365
-
366
- Class <?> idPropertyValueType = idPropertyValue .getClass ();
367
-
368
- if (!jpaMetamodel .isJpaManaged (idPropertyValueType )) {
369
- return idPropertyValue ;
370
- }
371
-
372
- return new DirectFieldAccessFallbackBeanWrapper (idPropertyValue )
373
- .getPropertyValue (tryFindSingularIdAttributeNameOrUseFallback (idPropertyValueType , idAttributeName ));
374
- }
375
-
376
- return null ;
377
- }
378
-
379
- private String tryFindSingularIdAttributeNameOrUseFallback (Class <?> idPropertyValueType ,
380
- String fallbackIdTypePropertyName ) {
381
-
382
- ManagedType <?> idPropertyType = metamodel .managedType (idPropertyValueType );
383
- for (SingularAttribute <?, ?> sa : idPropertyType .getSingularAttributes ()) {
384
- if (sa .isId ()) {
385
- return sa .getName ();
386
- }
387
- }
388
-
389
- return fallbackIdTypePropertyName ;
390
- }
391
-
392
- /**
393
- * @param value
394
- * @return {@literal true} if the given value is not {@literal null} and a mapped persistable entity otherwise
395
- * {@literal false}
396
- */
397
- private boolean isIdentifierDerivationNecessary (@ Nullable Object value ) {
398
-
399
- if (value == null ) {
400
- return false ;
401
- }
402
-
403
- Class <?> userClass = ProxyUtils .getUserClass (value );
404
-
405
- if (!this .jpaMetamodel .isJpaManaged (userClass )) {
406
- return false ;
407
- }
408
-
409
- ManagedType <?> managedType = this .metamodel .managedType (userClass );
410
-
411
- if (managedType == null ) {
412
- throw new IllegalStateException ("ManagedType must not be null; We checked that it exists before." );
413
- }
414
-
415
- return managedType .getPersistenceType () == PersistenceType .ENTITY ;
416
- }
417
- }
418
304
}
0 commit comments