6
6
package org .hibernate .reactive .engine .impl ;
7
7
8
8
import java .lang .invoke .MethodHandles ;
9
+ import java .util .ArrayList ;
9
10
import java .util .Collection ;
10
11
import java .util .Iterator ;
12
+ import java .util .List ;
11
13
import java .util .Objects ;
12
14
import java .util .concurrent .CompletionStage ;
13
15
32
34
import org .hibernate .reactive .session .ReactiveSession ;
33
35
import org .hibernate .type .AssociationType ;
34
36
import org .hibernate .type .CollectionType ;
37
+ import org .hibernate .type .ComponentType ;
35
38
import org .hibernate .type .CompositeType ;
36
39
import org .hibernate .type .EntityType ;
37
- import org .hibernate .type .ForeignKeyDirection ;
38
40
import org .hibernate .type .Type ;
39
41
40
42
import static org .hibernate .pretty .MessageHelper .infoString ;
41
43
import static org .hibernate .reactive .util .impl .CompletionStages .loop ;
42
44
import static org .hibernate .reactive .util .impl .CompletionStages .voidFuture ;
45
+ import static org .hibernate .type .ForeignKeyDirection .TO_PARENT ;
43
46
44
47
/**
45
48
* Delegate responsible for, in conjunction with the various
@@ -90,7 +93,7 @@ public static CompletionStage<?> fetchLazyAssociationsBeforeCascade(
90
93
CompletionStage <?> beforeDelete = voidFuture ();
91
94
if ( persister .hasCascades () ) {
92
95
CascadeStyle [] cascadeStyles = persister .getPropertyCascadeStyles ();
93
- Object [] state = persister .getPropertyValues ( entity );
96
+ Object [] state = persister .getValues ( entity );
94
97
for (int i = 0 ; i < cascadeStyles .length ; i ++) {
95
98
if ( cascadeStyles [i ].doCascade ( action .delegate () ) ) {
96
99
Object fetchable = state [i ];
@@ -105,13 +108,11 @@ public static CompletionStage<?> fetchLazyAssociationsBeforeCascade(
105
108
106
109
/**
107
110
* Cascade an action from the parent entity instance to all its children.
108
- *
109
- * which is specific to each CascadingAction type
110
111
*/
111
112
public CompletionStage <Void > cascade () throws HibernateException {
112
113
return voidFuture ().thenCompose (v -> {
113
114
CacheMode cacheMode = eventSource .getCacheMode ();
114
- if (action ==CascadingActions .DELETE ) {
115
+ if ( action ==CascadingActions .DELETE ) {
115
116
eventSource .setCacheMode ( CacheMode .GET );
116
117
}
117
118
eventSource .getPersistenceContextInternal ().incrementCascadeLevel ();
@@ -135,14 +136,14 @@ private CompletionStage<Void> cascadeInternal() throws HibernateException {
135
136
final String [] propertyNames = persister .getPropertyNames ();
136
137
final CascadeStyle [] cascadeStyles = persister .getPropertyCascadeStyles ();
137
138
final boolean hasUninitializedLazyProperties = persister .hasUninitializedLazyProperties ( parent );
138
- final int componentPathStackDepth = 0 ;
139
139
for ( int i = 0 ; i < types .length ; i ++) {
140
140
final CascadeStyle style = cascadeStyles [ i ];
141
141
final String propertyName = propertyNames [ i ];
142
142
final boolean isUninitializedProperty =
143
143
hasUninitializedLazyProperties &&
144
144
!persister .getBytecodeEnhancementMetadata ().isAttributeLoaded ( parent , propertyName );
145
145
146
+ final Type type = types [i ];
146
147
if ( style .doCascade ( action .delegate () ) ) {
147
148
final Object child ;
148
149
if ( isUninitializedProperty ) {
@@ -156,28 +157,28 @@ private CompletionStage<Void> cascadeInternal() throws HibernateException {
156
157
// parent was not in the PersistenceContext
157
158
continue ;
158
159
}
159
- if ( types [ i ] .isCollectionType () ) {
160
+ if ( type .isCollectionType () ) {
160
161
// CollectionType#getCollection gets the PersistentCollection
161
162
// that corresponds to the uninitialized collection from the
162
163
// PersistenceContext. If not present, an uninitialized
163
164
// PersistentCollection will be added to the PersistenceContext.
164
165
// The action may initialize it later, if necessary.
165
166
// This needs to be done even when action.performOnLazyProperty() returns false.
166
- final CollectionType collectionType = (CollectionType ) types [ i ] ;
167
+ final CollectionType collectionType = (CollectionType ) type ;
167
168
child = collectionType .getCollection (
168
169
collectionType .getKeyOfOwner ( parent , eventSource ),
169
170
eventSource ,
170
171
parent ,
171
172
null
172
173
);
173
174
}
174
- else if ( types [ i ] .isComponentType () ) {
175
+ else if ( type .isComponentType () ) {
175
176
// Hibernate does not support lazy embeddables, so this shouldn't happen.
176
177
throw new UnsupportedOperationException (
177
178
"Lazy components are not supported."
178
179
);
179
180
}
180
- else if ( action .performOnLazyProperty () && types [ i ] .isEntityType () ) {
181
+ else if ( action .performOnLazyProperty () && type .isEntityType () ) {
181
182
// Only need to initialize a lazy entity attribute when action.performOnLazyProperty()
182
183
// returns true.
183
184
LazyAttributeLoadingInterceptor interceptor = persister .getBytecodeEnhancementMetadata ()
@@ -191,12 +192,12 @@ else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) {
191
192
}
192
193
}
193
194
else {
194
- child = persister .getPropertyValue ( parent , i );
195
+ child = persister .getValue ( parent , i );
195
196
}
196
197
cascadeProperty (
197
- componentPathStackDepth ,
198
+ null ,
198
199
child ,
199
- types [ i ] ,
200
+ type ,
200
201
style ,
201
202
propertyName ,
202
203
false
@@ -209,9 +210,9 @@ else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) {
209
210
// If the property is uninitialized, then there cannot be any orphans.
210
211
if ( action .deleteOrphans () && !isUninitializedProperty ) {
211
212
cascadeLogicalOneToOneOrphanRemoval (
212
- componentPathStackDepth ,
213
- persister .getPropertyValue ( parent , i ),
214
- types [ i ] ,
213
+ null ,
214
+ persister .getValue ( parent , i ),
215
+ type ,
215
216
style ,
216
217
propertyName ,
217
218
false
@@ -241,7 +242,7 @@ private void noCascade(
241
242
* Cascade an action to the child or children
242
243
*/
243
244
private void cascadeProperty (
244
- final int componentPathStackDepth ,
245
+ List < String > componentPath ,
245
246
final Object child ,
246
247
final Type type ,
247
248
final CascadeStyle style ,
@@ -253,7 +254,7 @@ private void cascadeProperty(
253
254
final AssociationType associationType = (AssociationType ) type ;
254
255
if ( cascadeAssociationNow ( cascadePoint , associationType ) ) {
255
256
cascadeAssociation (
256
- componentPathStackDepth ,
257
+ componentPath ,
257
258
child ,
258
259
type ,
259
260
style ,
@@ -262,16 +263,22 @@ private void cascadeProperty(
262
263
}
263
264
}
264
265
else if ( type .isComponentType () ) {
266
+ if ( componentPath == null && propertyName != null ) {
267
+ componentPath = new ArrayList <>();
268
+ }
269
+ if ( componentPath != null ) {
270
+ componentPath .add ( propertyName );
271
+ }
265
272
cascadeComponent (
266
- componentPathStackDepth ,
273
+ componentPath ,
267
274
child ,
268
275
(CompositeType ) type
269
276
);
270
277
}
271
278
}
272
279
273
280
cascadeLogicalOneToOneOrphanRemoval (
274
- componentPathStackDepth ,
281
+ componentPath ,
275
282
child ,
276
283
type ,
277
284
style ,
@@ -280,7 +287,7 @@ else if ( type.isComponentType() ) {
280
287
}
281
288
282
289
private void cascadeLogicalOneToOneOrphanRemoval (
283
- final int componentPathStackDepth ,
290
+ final List < String > componentPath ,
284
291
final Object child ,
285
292
final Type type ,
286
293
final CascadeStyle style ,
@@ -298,31 +305,38 @@ private void cascadeLogicalOneToOneOrphanRemoval(
298
305
final EntityEntry entry = persistenceContext .getEntry ( parent );
299
306
if ( entry != null && entry .getStatus () != Status .SAVING ) {
300
307
Object loadedValue ;
301
- if ( componentPathStackDepth == 0 ) {
308
+ if ( componentPath == null ) {
302
309
// association defined on entity
303
310
loadedValue = entry .getLoadedValue ( propertyName );
304
311
}
305
312
else {
306
313
// association defined on component
307
- // todo : this is currently unsupported because of the fact that
308
- // we do not know the loaded state of this value properly
309
- // and doing so would be very difficult given how components and
310
- // entities are loaded (and how 'loaded state' is put into the
311
- // EntityEntry). Solutions here are to either:
312
- // 1) properly account for components as a 2-phase load construct
313
- // 2) just assume the association was just now orphaned and
314
- // issue the orphan delete. This would require a special
315
- // set of SQL statements though since we do not know the
316
- // orphaned value, something a delete with a subquery to
317
- // match the owner.
318
- // final EntityType entityType = (EntityType) type;
319
- // final String getPropertyPath = composePropertyPath( entityType.getPropertyName() );
320
- loadedValue = null ;
314
+ // Since the loadedState in the EntityEntry is a flat domain type array
315
+ // We first have to extract the component object and then ask the component type
316
+ // recursively to give us the value of the sub-property of that object
317
+ final Type propertyType = entry .getPersister ().getPropertyType ( componentPath .get (0 ) );
318
+ if ( propertyType instanceof ComponentType ) {
319
+ loadedValue = entry .getLoadedValue ( componentPath .get ( 0 ) );
320
+ ComponentType componentType = (ComponentType ) propertyType ;
321
+ if ( componentPath .size () != 1 ) {
322
+ for ( int i = 1 ; i < componentPath .size (); i ++ ) {
323
+ final int subPropertyIndex = componentType .getPropertyIndex ( componentPath .get ( i ) );
324
+ loadedValue = componentType .getPropertyValue ( loadedValue , subPropertyIndex );
325
+ componentType = (ComponentType ) componentType .getSubtypes ()[subPropertyIndex ];
326
+ }
327
+ }
328
+
329
+ loadedValue = componentType .getPropertyValue ( loadedValue , componentType .getPropertyIndex ( propertyName ) );
330
+ }
331
+ else {
332
+ // Association is probably defined in an element collection, so we can't do orphan removals
333
+ loadedValue = null ;
334
+ }
321
335
}
322
336
323
337
// orphaned if the association was nulled (child == null) or receives a new value while the
324
338
// entity is managed (without first nulling and manually flushing).
325
- if ( child == null || ( loadedValue != null && child != loadedValue ) ) {
339
+ if ( child == null || loadedValue != null && child != loadedValue ) {
326
340
EntityEntry valueEntry = persistenceContext .getEntry ( loadedValue );
327
341
328
342
if ( valueEntry == null && loadedValue instanceof HibernateProxy ) {
@@ -342,8 +356,8 @@ private void cascadeLogicalOneToOneOrphanRemoval(
342
356
}
343
357
344
358
if ( valueEntry != null ) {
345
- EntityPersister persister = valueEntry .getPersister ();
346
- String entityName = persister .getEntityName ();
359
+ final EntityPersister persister = valueEntry .getPersister ();
360
+ final String entityName = persister .getEntityName ();
347
361
if ( LOG .isTraceEnabled () ) {
348
362
LOG .tracev (
349
363
"Deleting orphaned entity instance: {0}" ,
@@ -353,8 +367,7 @@ private void cascadeLogicalOneToOneOrphanRemoval(
353
367
354
368
final Object loaded = loadedValue ;
355
369
if ( type .isAssociationType ()
356
- && ( (AssociationType ) type ).getForeignKeyDirection ()
357
- .equals (ForeignKeyDirection .TO_PARENT ) ) {
370
+ && ( (AssociationType ) type ).getForeignKeyDirection ().equals (TO_PARENT ) ) {
358
371
// If FK direction is to-parent, we must remove the orphan *before* the queued update(s)
359
372
// occur. Otherwise, replacing the association on a managed entity, without manually
360
373
// nulling and flushing, causes FK constraint violations.
@@ -390,7 +403,7 @@ private boolean cascadeAssociationNow(final CascadePoint cascadePoint, Associat
390
403
}
391
404
392
405
private void cascadeComponent (
393
- final int componentPathStackDepth ,
406
+ List < String > componentPath ,
394
407
final Object child ,
395
408
final CompositeType componentType ) {
396
409
@@ -400,13 +413,14 @@ private void cascadeComponent(
400
413
for ( int i = 0 ; i < types .length ; i ++ ) {
401
414
final CascadeStyle componentPropertyStyle = componentType .getCascadeStyle ( i );
402
415
final String subPropertyName = propertyNames [i ];
403
- if ( componentPropertyStyle .doCascade ( action .delegate () ) ) {
404
- if (children == null ) {
416
+ if ( componentPropertyStyle .doCascade ( action .delegate () )
417
+ || componentPropertyStyle .hasOrphanDelete () && action .deleteOrphans () ) {
418
+ if ( children == null ) {
405
419
// Get children on demand.
406
420
children = componentType .getPropertyValues ( child , eventSource );
407
421
}
408
422
cascadeProperty (
409
- componentPathStackDepth + 1 ,
423
+ componentPath ,
410
424
children [i ],
411
425
types [i ],
412
426
componentPropertyStyle ,
@@ -418,7 +432,7 @@ private void cascadeComponent(
418
432
}
419
433
420
434
private void cascadeAssociation (
421
- final int componentPathStackDepth ,
435
+ List < String > componentPath ,
422
436
final Object child ,
423
437
final Type type ,
424
438
final CascadeStyle style ,
@@ -428,7 +442,7 @@ private void cascadeAssociation(
428
442
}
429
443
else if ( type .isCollectionType () ) {
430
444
cascadeCollection (
431
- componentPathStackDepth ,
445
+ componentPath ,
432
446
child ,
433
447
style ,
434
448
(CollectionType ) type
@@ -440,12 +454,13 @@ else if ( type.isCollectionType() ) {
440
454
* Cascade an action to a collection
441
455
*/
442
456
private void cascadeCollection (
443
- final int componentPathStackDepth ,
457
+ List < String > componentPath ,
444
458
final Object child ,
445
459
final CascadeStyle style ,
446
460
final CollectionType type ) {
447
461
final CollectionPersister persister =
448
- eventSource .getFactory ().getMetamodel ().collectionPersister ( type .getRole () );
462
+ eventSource .getFactory ().getMappingMetamodel ()
463
+ .getCollectionDescriptor ( type .getRole () );
449
464
final Type elemType = persister .getElementType ();
450
465
451
466
CascadePoint elementsCascadePoint = cascadePoint ;
@@ -456,7 +471,7 @@ private void cascadeCollection(
456
471
//cascade to current collection elements
457
472
if ( elemType .isEntityType () || elemType .isAnyType () || elemType .isComponentType () ) {
458
473
cascadeCollectionElements (
459
- componentPathStackDepth ,
474
+ componentPath ,
460
475
child ,
461
476
type ,
462
477
style ,
@@ -492,7 +507,7 @@ private void cascadeToOne(
492
507
* Cascade to the collection elements
493
508
*/
494
509
private void cascadeCollectionElements (
495
- final int componentPathStackDepth ,
510
+ List < String > componentPath ,
496
511
final Object child ,
497
512
final CollectionType collectionType ,
498
513
final CascadeStyle style ,
@@ -510,7 +525,7 @@ private void cascadeCollectionElements(
510
525
final Iterator <?> itr = action .getCascadableChildrenIterator ( eventSource , collectionType , child );
511
526
while ( itr .hasNext () ) {
512
527
cascadeProperty (
513
- componentPathStackDepth ,
528
+ componentPath ,
514
529
itr .next (),
515
530
elemType ,
516
531
style ,
@@ -527,8 +542,9 @@ private void cascadeCollectionElements(
527
542
final boolean deleteOrphans = style .hasOrphanDelete ()
528
543
&& action .deleteOrphans ()
529
544
&& elemType .isEntityType ()
545
+ && child instanceof PersistentCollection
530
546
// a newly instantiated collection can't have orphans
531
- && child instanceof PersistentCollection ;
547
+ && ! ( ( PersistentCollection <?>) child ). isNewlyInstantiated () ;
532
548
533
549
if ( deleteOrphans ) {
534
550
final boolean traceEnabled = LOG .isTraceEnabled ();
@@ -539,7 +555,7 @@ private void cascadeCollectionElements(
539
555
// 1. newly instantiated collections
540
556
// 2. arrays (we can't track orphans for detached arrays)
541
557
final String entityName = collectionType .getAssociatedEntityName ( eventSource .getFactory () );
542
- deleteOrphans ( entityName , (PersistentCollection ) child );
558
+ deleteOrphans ( entityName , (PersistentCollection <?> ) child );
543
559
544
560
if ( traceEnabled ) {
545
561
LOG .tracev ( "Done deleting orphans for collection: {0}" , collectionType .getRole () );
@@ -550,7 +566,7 @@ private void cascadeCollectionElements(
550
566
/**
551
567
* Delete any entities that were removed from the collection
552
568
*/
553
- private void deleteOrphans (String entityName , PersistentCollection pc ) throws HibernateException {
569
+ private void deleteOrphans (String entityName , PersistentCollection <?> pc ) throws HibernateException {
554
570
//TODO: suck this logic into the collection!
555
571
final Collection <?> orphans ;
556
572
if ( pc .wasInitialized () ) {
0 commit comments