66package org .hibernate .reactive .engine .impl ;
77
88import java .lang .invoke .MethodHandles ;
9+ import java .util .ArrayList ;
910import java .util .Collection ;
1011import java .util .Iterator ;
12+ import java .util .List ;
1113import java .util .Objects ;
1214import java .util .concurrent .CompletionStage ;
1315
3234import org .hibernate .reactive .session .ReactiveSession ;
3335import org .hibernate .type .AssociationType ;
3436import org .hibernate .type .CollectionType ;
37+ import org .hibernate .type .ComponentType ;
3538import org .hibernate .type .CompositeType ;
3639import org .hibernate .type .EntityType ;
37- import org .hibernate .type .ForeignKeyDirection ;
3840import org .hibernate .type .Type ;
3941
4042import static org .hibernate .pretty .MessageHelper .infoString ;
4143import static org .hibernate .reactive .util .impl .CompletionStages .loop ;
4244import static org .hibernate .reactive .util .impl .CompletionStages .voidFuture ;
45+ import static org .hibernate .type .ForeignKeyDirection .TO_PARENT ;
4346
4447/**
4548 * Delegate responsible for, in conjunction with the various
@@ -90,7 +93,7 @@ public static CompletionStage<?> fetchLazyAssociationsBeforeCascade(
9093 CompletionStage <?> beforeDelete = voidFuture ();
9194 if ( persister .hasCascades () ) {
9295 CascadeStyle [] cascadeStyles = persister .getPropertyCascadeStyles ();
93- Object [] state = persister .getPropertyValues ( entity );
96+ Object [] state = persister .getValues ( entity );
9497 for (int i = 0 ; i < cascadeStyles .length ; i ++) {
9598 if ( cascadeStyles [i ].doCascade ( action .delegate () ) ) {
9699 Object fetchable = state [i ];
@@ -105,13 +108,11 @@ public static CompletionStage<?> fetchLazyAssociationsBeforeCascade(
105108
106109 /**
107110 * Cascade an action from the parent entity instance to all its children.
108- *
109- * which is specific to each CascadingAction type
110111 */
111112 public CompletionStage <Void > cascade () throws HibernateException {
112113 return voidFuture ().thenCompose (v -> {
113114 CacheMode cacheMode = eventSource .getCacheMode ();
114- if (action ==CascadingActions .DELETE ) {
115+ if ( action ==CascadingActions .DELETE ) {
115116 eventSource .setCacheMode ( CacheMode .GET );
116117 }
117118 eventSource .getPersistenceContextInternal ().incrementCascadeLevel ();
@@ -135,14 +136,14 @@ private CompletionStage<Void> cascadeInternal() throws HibernateException {
135136 final String [] propertyNames = persister .getPropertyNames ();
136137 final CascadeStyle [] cascadeStyles = persister .getPropertyCascadeStyles ();
137138 final boolean hasUninitializedLazyProperties = persister .hasUninitializedLazyProperties ( parent );
138- final int componentPathStackDepth = 0 ;
139139 for ( int i = 0 ; i < types .length ; i ++) {
140140 final CascadeStyle style = cascadeStyles [ i ];
141141 final String propertyName = propertyNames [ i ];
142142 final boolean isUninitializedProperty =
143143 hasUninitializedLazyProperties &&
144144 !persister .getBytecodeEnhancementMetadata ().isAttributeLoaded ( parent , propertyName );
145145
146+ final Type type = types [i ];
146147 if ( style .doCascade ( action .delegate () ) ) {
147148 final Object child ;
148149 if ( isUninitializedProperty ) {
@@ -156,28 +157,28 @@ private CompletionStage<Void> cascadeInternal() throws HibernateException {
156157 // parent was not in the PersistenceContext
157158 continue ;
158159 }
159- if ( types [ i ] .isCollectionType () ) {
160+ if ( type .isCollectionType () ) {
160161 // CollectionType#getCollection gets the PersistentCollection
161162 // that corresponds to the uninitialized collection from the
162163 // PersistenceContext. If not present, an uninitialized
163164 // PersistentCollection will be added to the PersistenceContext.
164165 // The action may initialize it later, if necessary.
165166 // This needs to be done even when action.performOnLazyProperty() returns false.
166- final CollectionType collectionType = (CollectionType ) types [ i ] ;
167+ final CollectionType collectionType = (CollectionType ) type ;
167168 child = collectionType .getCollection (
168169 collectionType .getKeyOfOwner ( parent , eventSource ),
169170 eventSource ,
170171 parent ,
171172 null
172173 );
173174 }
174- else if ( types [ i ] .isComponentType () ) {
175+ else if ( type .isComponentType () ) {
175176 // Hibernate does not support lazy embeddables, so this shouldn't happen.
176177 throw new UnsupportedOperationException (
177178 "Lazy components are not supported."
178179 );
179180 }
180- else if ( action .performOnLazyProperty () && types [ i ] .isEntityType () ) {
181+ else if ( action .performOnLazyProperty () && type .isEntityType () ) {
181182 // Only need to initialize a lazy entity attribute when action.performOnLazyProperty()
182183 // returns true.
183184 LazyAttributeLoadingInterceptor interceptor = persister .getBytecodeEnhancementMetadata ()
@@ -191,12 +192,12 @@ else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) {
191192 }
192193 }
193194 else {
194- child = persister .getPropertyValue ( parent , i );
195+ child = persister .getValue ( parent , i );
195196 }
196197 cascadeProperty (
197- componentPathStackDepth ,
198+ null ,
198199 child ,
199- types [ i ] ,
200+ type ,
200201 style ,
201202 propertyName ,
202203 false
@@ -209,9 +210,9 @@ else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) {
209210 // If the property is uninitialized, then there cannot be any orphans.
210211 if ( action .deleteOrphans () && !isUninitializedProperty ) {
211212 cascadeLogicalOneToOneOrphanRemoval (
212- componentPathStackDepth ,
213- persister .getPropertyValue ( parent , i ),
214- types [ i ] ,
213+ null ,
214+ persister .getValue ( parent , i ),
215+ type ,
215216 style ,
216217 propertyName ,
217218 false
@@ -241,7 +242,7 @@ private void noCascade(
241242 * Cascade an action to the child or children
242243 */
243244 private void cascadeProperty (
244- final int componentPathStackDepth ,
245+ List < String > componentPath ,
245246 final Object child ,
246247 final Type type ,
247248 final CascadeStyle style ,
@@ -253,7 +254,7 @@ private void cascadeProperty(
253254 final AssociationType associationType = (AssociationType ) type ;
254255 if ( cascadeAssociationNow ( cascadePoint , associationType ) ) {
255256 cascadeAssociation (
256- componentPathStackDepth ,
257+ componentPath ,
257258 child ,
258259 type ,
259260 style ,
@@ -262,16 +263,22 @@ private void cascadeProperty(
262263 }
263264 }
264265 else if ( type .isComponentType () ) {
266+ if ( componentPath == null && propertyName != null ) {
267+ componentPath = new ArrayList <>();
268+ }
269+ if ( componentPath != null ) {
270+ componentPath .add ( propertyName );
271+ }
265272 cascadeComponent (
266- componentPathStackDepth ,
273+ componentPath ,
267274 child ,
268275 (CompositeType ) type
269276 );
270277 }
271278 }
272279
273280 cascadeLogicalOneToOneOrphanRemoval (
274- componentPathStackDepth ,
281+ componentPath ,
275282 child ,
276283 type ,
277284 style ,
@@ -280,7 +287,7 @@ else if ( type.isComponentType() ) {
280287 }
281288
282289 private void cascadeLogicalOneToOneOrphanRemoval (
283- final int componentPathStackDepth ,
290+ final List < String > componentPath ,
284291 final Object child ,
285292 final Type type ,
286293 final CascadeStyle style ,
@@ -298,31 +305,38 @@ private void cascadeLogicalOneToOneOrphanRemoval(
298305 final EntityEntry entry = persistenceContext .getEntry ( parent );
299306 if ( entry != null && entry .getStatus () != Status .SAVING ) {
300307 Object loadedValue ;
301- if ( componentPathStackDepth == 0 ) {
308+ if ( componentPath == null ) {
302309 // association defined on entity
303310 loadedValue = entry .getLoadedValue ( propertyName );
304311 }
305312 else {
306313 // 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+ }
321335 }
322336
323337 // orphaned if the association was nulled (child == null) or receives a new value while the
324338 // 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 ) {
326340 EntityEntry valueEntry = persistenceContext .getEntry ( loadedValue );
327341
328342 if ( valueEntry == null && loadedValue instanceof HibernateProxy ) {
@@ -342,8 +356,8 @@ private void cascadeLogicalOneToOneOrphanRemoval(
342356 }
343357
344358 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 ();
347361 if ( LOG .isTraceEnabled () ) {
348362 LOG .tracev (
349363 "Deleting orphaned entity instance: {0}" ,
@@ -353,8 +367,7 @@ private void cascadeLogicalOneToOneOrphanRemoval(
353367
354368 final Object loaded = loadedValue ;
355369 if ( type .isAssociationType ()
356- && ( (AssociationType ) type ).getForeignKeyDirection ()
357- .equals (ForeignKeyDirection .TO_PARENT ) ) {
370+ && ( (AssociationType ) type ).getForeignKeyDirection ().equals (TO_PARENT ) ) {
358371 // If FK direction is to-parent, we must remove the orphan *before* the queued update(s)
359372 // occur. Otherwise, replacing the association on a managed entity, without manually
360373 // nulling and flushing, causes FK constraint violations.
@@ -390,7 +403,7 @@ private boolean cascadeAssociationNow(final CascadePoint cascadePoint, Associat
390403 }
391404
392405 private void cascadeComponent (
393- final int componentPathStackDepth ,
406+ List < String > componentPath ,
394407 final Object child ,
395408 final CompositeType componentType ) {
396409
@@ -400,13 +413,14 @@ private void cascadeComponent(
400413 for ( int i = 0 ; i < types .length ; i ++ ) {
401414 final CascadeStyle componentPropertyStyle = componentType .getCascadeStyle ( i );
402415 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 ) {
405419 // Get children on demand.
406420 children = componentType .getPropertyValues ( child , eventSource );
407421 }
408422 cascadeProperty (
409- componentPathStackDepth + 1 ,
423+ componentPath ,
410424 children [i ],
411425 types [i ],
412426 componentPropertyStyle ,
@@ -418,7 +432,7 @@ private void cascadeComponent(
418432 }
419433
420434 private void cascadeAssociation (
421- final int componentPathStackDepth ,
435+ List < String > componentPath ,
422436 final Object child ,
423437 final Type type ,
424438 final CascadeStyle style ,
@@ -428,7 +442,7 @@ private void cascadeAssociation(
428442 }
429443 else if ( type .isCollectionType () ) {
430444 cascadeCollection (
431- componentPathStackDepth ,
445+ componentPath ,
432446 child ,
433447 style ,
434448 (CollectionType ) type
@@ -440,12 +454,13 @@ else if ( type.isCollectionType() ) {
440454 * Cascade an action to a collection
441455 */
442456 private void cascadeCollection (
443- final int componentPathStackDepth ,
457+ List < String > componentPath ,
444458 final Object child ,
445459 final CascadeStyle style ,
446460 final CollectionType type ) {
447461 final CollectionPersister persister =
448- eventSource .getFactory ().getMetamodel ().collectionPersister ( type .getRole () );
462+ eventSource .getFactory ().getMappingMetamodel ()
463+ .getCollectionDescriptor ( type .getRole () );
449464 final Type elemType = persister .getElementType ();
450465
451466 CascadePoint elementsCascadePoint = cascadePoint ;
@@ -456,7 +471,7 @@ private void cascadeCollection(
456471 //cascade to current collection elements
457472 if ( elemType .isEntityType () || elemType .isAnyType () || elemType .isComponentType () ) {
458473 cascadeCollectionElements (
459- componentPathStackDepth ,
474+ componentPath ,
460475 child ,
461476 type ,
462477 style ,
@@ -492,7 +507,7 @@ private void cascadeToOne(
492507 * Cascade to the collection elements
493508 */
494509 private void cascadeCollectionElements (
495- final int componentPathStackDepth ,
510+ List < String > componentPath ,
496511 final Object child ,
497512 final CollectionType collectionType ,
498513 final CascadeStyle style ,
@@ -510,7 +525,7 @@ private void cascadeCollectionElements(
510525 final Iterator <?> itr = action .getCascadableChildrenIterator ( eventSource , collectionType , child );
511526 while ( itr .hasNext () ) {
512527 cascadeProperty (
513- componentPathStackDepth ,
528+ componentPath ,
514529 itr .next (),
515530 elemType ,
516531 style ,
@@ -527,8 +542,9 @@ private void cascadeCollectionElements(
527542 final boolean deleteOrphans = style .hasOrphanDelete ()
528543 && action .deleteOrphans ()
529544 && elemType .isEntityType ()
545+ && child instanceof PersistentCollection
530546 // a newly instantiated collection can't have orphans
531- && child instanceof PersistentCollection ;
547+ && ! ( ( PersistentCollection <?>) child ). isNewlyInstantiated () ;
532548
533549 if ( deleteOrphans ) {
534550 final boolean traceEnabled = LOG .isTraceEnabled ();
@@ -539,7 +555,7 @@ private void cascadeCollectionElements(
539555 // 1. newly instantiated collections
540556 // 2. arrays (we can't track orphans for detached arrays)
541557 final String entityName = collectionType .getAssociatedEntityName ( eventSource .getFactory () );
542- deleteOrphans ( entityName , (PersistentCollection ) child );
558+ deleteOrphans ( entityName , (PersistentCollection <?> ) child );
543559
544560 if ( traceEnabled ) {
545561 LOG .tracev ( "Done deleting orphans for collection: {0}" , collectionType .getRole () );
@@ -550,7 +566,7 @@ private void cascadeCollectionElements(
550566 /**
551567 * Delete any entities that were removed from the collection
552568 */
553- private void deleteOrphans (String entityName , PersistentCollection pc ) throws HibernateException {
569+ private void deleteOrphans (String entityName , PersistentCollection <?> pc ) throws HibernateException {
554570 //TODO: suck this logic into the collection!
555571 final Collection <?> orphans ;
556572 if ( pc .wasInitialized () ) {
0 commit comments