Skip to content

Commit fe52d75

Browse files
committed
HHH-18936 suppress TransientObjectException when the FK is marked @onDelete(CASCADE)
note that this fix does not cover the case of an association inside an @embeddable
1 parent 1022e9c commit fe52d75

File tree

11 files changed

+251
-9
lines changed

11 files changed

+251
-9
lines changed

hibernate-core/src/main/java/org/hibernate/engine/internal/Cascade.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.List;
1111

1212
import org.hibernate.HibernateException;
13+
import org.hibernate.annotations.OnDeleteAction;
1314
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
1415
import org.hibernate.collection.spi.PersistentCollection;
1516
import org.hibernate.engine.spi.CascadeStyle;
@@ -117,6 +118,8 @@ public static <T> void cascade(
117118
hasUninitializedLazyProperties
118119
&& !persister.getBytecodeEnhancementMetadata()
119120
.isAttributeLoaded( parent, propertyName );
121+
final boolean isCascadeDeleteEnabled =
122+
persister.getEntityMetamodel().getPropertyOnDeleteActions()[i] == OnDeleteAction.CASCADE;
120123

121124
if ( style.doCascade( action ) ) {
122125
final Object child;
@@ -178,7 +181,7 @@ else if ( action.performOnLazyProperty() && type instanceof EntityType ) {
178181
style,
179182
propertyName,
180183
anything,
181-
false
184+
isCascadeDeleteEnabled
182185
);
183186
}
184187
else {
@@ -193,7 +196,7 @@ else if ( action.performOnLazyProperty() && type instanceof EntityType ) {
193196
type,
194197
style,
195198
propertyName,
196-
false
199+
isCascadeDeleteEnabled
197200
);
198201
}
199202
}

hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingActions.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public void cascade(
340340
Void context,
341341
boolean isCascadeDeleteEnabled)
342342
throws HibernateException {
343-
if ( child != null && isChildTransient( session, child, entityName ) ) {
343+
if ( child != null && isChildTransient( session, child, entityName, isCascadeDeleteEnabled ) ) {
344344
throw new TransientObjectException( "Persistent instance references an unsaved transient instance of '"
345345
+ entityName + "' (save the transient instance before flushing)" );
346346
//TODO: should be TransientPropertyValueException
@@ -387,7 +387,7 @@ public String toString() {
387387
}
388388
};
389389

390-
private static boolean isChildTransient(EventSource session, Object child, String entityName) {
390+
private static boolean isChildTransient(EventSource session, Object child, String entityName, boolean isCascadeDeleteEnabled) {
391391
if ( isHibernateProxy( child ) ) {
392392
// a proxy is always non-transient
393393
// and ForeignKeys.isTransient()
@@ -402,7 +402,11 @@ private static boolean isChildTransient(EventSource session, Object child, Strin
402402
// we are good, even if it's not yet
403403
// inserted, since ordering problems
404404
// are detected and handled elsewhere
405-
return entry.getStatus().isDeletedOrGone();
405+
return entry.getStatus().isDeletedOrGone()
406+
// if the foreign key is 'on delete cascade'
407+
// we don't have to throw because the database
408+
// will delete the parent for us
409+
&& !isCascadeDeleteEnabled;
406410
}
407411
else {
408412
// TODO: check if it is a merged entity which has not yet been flushed

hibernate-core/src/main/java/org/hibernate/mapping/Property.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.hibernate.HibernateException;
1313
import org.hibernate.Internal;
1414
import org.hibernate.MappingException;
15+
import org.hibernate.annotations.OnDeleteAction;
1516
import org.hibernate.boot.model.relational.Database;
1617
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
1718
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
@@ -134,6 +135,10 @@ public void resetOptional(boolean optional) {
134135
}
135136
}
136137

138+
public OnDeleteAction getOnDeleteAction() {
139+
return value instanceof ToOne toOne ? toOne.getOnDeleteAction() : null;
140+
}
141+
137142
public CascadeStyle getCascadeStyle() throws MappingException {
138143
final Type type = value.getType();
139144
if ( type instanceof AnyType ) {

hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.tuple;
66

77
import org.hibernate.FetchMode;
8+
import org.hibernate.annotations.OnDeleteAction;
89
import org.hibernate.engine.spi.CascadeStyle;
910
import org.hibernate.engine.spi.SessionFactoryImplementor;
1011
import org.hibernate.persister.walking.spi.AttributeSource;
@@ -92,6 +93,11 @@ public CascadeStyle getCascadeStyle() {
9293
return attributeInformation.getCascadeStyle();
9394
}
9495

96+
@Override
97+
public OnDeleteAction getOnDeleteAction() {
98+
return attributeInformation.getOnDeleteAction();
99+
}
100+
95101
@Override
96102
public FetchMode getFetchMode() {
97103
return attributeInformation.getFetchMode();

hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.tuple;
66

77
import org.hibernate.FetchMode;
8+
import org.hibernate.annotations.OnDeleteAction;
89
import org.hibernate.engine.spi.CascadeStyle;
910

1011
/**
@@ -19,6 +20,7 @@ public class BaselineAttributeInformation {
1920
private final boolean nullable;
2021
private final boolean dirtyCheckable;
2122
private final boolean versionable;
23+
private final OnDeleteAction onDeleteAction;
2224
private final CascadeStyle cascadeStyle;
2325
private final FetchMode fetchMode;
2426

@@ -30,6 +32,7 @@ public BaselineAttributeInformation(
3032
boolean dirtyCheckable,
3133
boolean versionable,
3234
CascadeStyle cascadeStyle,
35+
OnDeleteAction onDeleteAction,
3336
FetchMode fetchMode) {
3437
this.lazy = lazy;
3538
this.insertable = insertable;
@@ -38,6 +41,7 @@ public BaselineAttributeInformation(
3841
this.dirtyCheckable = dirtyCheckable;
3942
this.versionable = versionable;
4043
this.cascadeStyle = cascadeStyle;
44+
this.onDeleteAction = onDeleteAction;
4145
this.fetchMode = fetchMode;
4246
}
4347

@@ -73,6 +77,10 @@ public FetchMode getFetchMode() {
7377
return fetchMode;
7478
}
7579

80+
public OnDeleteAction getOnDeleteAction() {
81+
return onDeleteAction;
82+
}
83+
7684
public static class Builder {
7785
private boolean lazy;
7886
private boolean insertable;
@@ -81,6 +89,7 @@ public static class Builder {
8189
private boolean dirtyCheckable;
8290
private boolean versionable;
8391
private CascadeStyle cascadeStyle;
92+
private OnDeleteAction onDeleteAction;
8493
private FetchMode fetchMode;
8594

8695
public Builder setLazy(boolean lazy) {
@@ -118,6 +127,11 @@ public Builder setCascadeStyle(CascadeStyle cascadeStyle) {
118127
return this;
119128
}
120129

130+
public Builder setOnDeleteAction(OnDeleteAction onDeleteAction) {
131+
this.onDeleteAction = onDeleteAction;
132+
return this;
133+
}
134+
121135
public Builder setFetchMode(FetchMode fetchMode) {
122136
this.fetchMode = fetchMode;
123137
return this;
@@ -132,6 +146,7 @@ public BaselineAttributeInformation createInformation() {
132146
dirtyCheckable,
133147
versionable,
134148
cascadeStyle,
149+
onDeleteAction,
135150
fetchMode
136151
);
137152
}

hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.tuple;
66

77
import org.hibernate.FetchMode;
8+
import org.hibernate.annotations.OnDeleteAction;
89
import org.hibernate.engine.spi.CascadeStyle;
910

1011
/**
@@ -32,5 +33,7 @@ public interface NonIdentifierAttribute extends Attribute {
3233

3334
CascadeStyle getCascadeStyle();
3435

36+
OnDeleteAction getOnDeleteAction();
37+
3538
FetchMode getFetchMode();
3639
}

hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public static VersionProperty buildVersionProperty(
9999
.setDirtyCheckable( property.isUpdateable() && !lazy )
100100
.setVersionable( property.isOptimisticLocked() )
101101
.setCascadeStyle( property.getCascadeStyle() )
102+
.setOnDeleteAction( property.getOnDeleteAction() )
102103
.createInformation()
103104
);
104105
}
@@ -169,6 +170,7 @@ public static NonIdentifierAttribute buildEntityBasedAttribute(
169170
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
170171
.setVersionable( property.isOptimisticLocked() )
171172
.setCascadeStyle( property.getCascadeStyle() )
173+
.setOnDeleteAction( property.getOnDeleteAction() )
172174
.setFetchMode( property.getValue().getFetchMode() )
173175
.createInformation()
174176
);
@@ -188,6 +190,7 @@ public static NonIdentifierAttribute buildEntityBasedAttribute(
188190
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
189191
.setVersionable( property.isOptimisticLocked() )
190192
.setCascadeStyle( property.getCascadeStyle() )
193+
.setOnDeleteAction( property.getOnDeleteAction() )
191194
.setFetchMode( property.getValue().getFetchMode() )
192195
.createInformation()
193196
);
@@ -209,6 +212,7 @@ public static NonIdentifierAttribute buildEntityBasedAttribute(
209212
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
210213
.setVersionable( property.isOptimisticLocked() )
211214
.setCascadeStyle( property.getCascadeStyle() )
215+
.setOnDeleteAction( property.getOnDeleteAction() )
212216
.setFetchMode( property.getValue().getFetchMode() )
213217
.createInformation()
214218
);

hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.tuple;
66

77
import org.hibernate.FetchMode;
8+
import org.hibernate.annotations.OnDeleteAction;
89
import org.hibernate.engine.spi.CascadeStyle;
910
import org.hibernate.type.Type;
1011

@@ -37,6 +38,7 @@ public StandardProperty(
3738
boolean checkable,
3839
boolean versionable,
3940
CascadeStyle cascadeStyle,
41+
OnDeleteAction onDeleteAction,
4042
FetchMode fetchMode) {
4143
super(
4244
null,
@@ -52,6 +54,7 @@ public StandardProperty(
5254
.setDirtyCheckable( checkable )
5355
.setVersionable( versionable )
5456
.setCascadeStyle( cascadeStyle )
57+
.setOnDeleteAction( onDeleteAction )
5558
.setFetchMode( fetchMode )
5659
.createInformation()
5760
);

hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.HibernateException;
1919
import org.hibernate.MappingException;
2020
import org.hibernate.annotations.NotFoundAction;
21+
import org.hibernate.annotations.OnDeleteAction;
2122
import org.hibernate.boot.spi.MetadataImplementor;
2223
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
2324
import org.hibernate.bytecode.internal.BytecodeEnhancementMetadataNonPojoImpl;
@@ -63,6 +64,7 @@
6364
import static org.hibernate.internal.util.ReflectHelper.isFinalClass;
6465
import static org.hibernate.internal.util.collections.ArrayHelper.toIntArray;
6566
import static org.hibernate.internal.util.collections.CollectionHelper.toSmallSet;
67+
import static org.hibernate.tuple.PropertyFactory.buildIdentifierAttribute;
6668

6769
/**
6870
* Centralizes metamodel information about an entity.
@@ -102,6 +104,7 @@ public class EntityMetamodel implements Serializable {
102104
private final boolean[] propertyInsertability;
103105
private final boolean[] propertyNullability;
104106
private final boolean[] propertyVersionability;
107+
private final OnDeleteAction[] propertyOnDeleteActions;
105108
private final CascadeStyle[] cascadeStyles;
106109
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107110

@@ -162,7 +165,7 @@ public EntityMetamodel(
162165
EntityPersister persister,
163166
RuntimeModelCreationContext creationContext,
164167
Function<String, Generator> generatorSupplier) {
165-
this.sessionFactory = creationContext.getSessionFactory();
168+
sessionFactory = creationContext.getSessionFactory();
166169

167170
// Improves performance of EntityKey#equals by avoiding content check in String#equals
168171
name = persistentClass.getEntityName().intern();
@@ -174,18 +177,18 @@ public EntityMetamodel(
174177
subclassId = persistentClass.getSubclassId();
175178

176179
final Generator idgenerator = generatorSupplier.apply( rootName );
177-
identifierAttribute = PropertyFactory.buildIdentifierAttribute( persistentClass, idgenerator );
180+
identifierAttribute = buildIdentifierAttribute( persistentClass, idgenerator );
178181

179182
versioned = persistentClass.isVersioned();
180183

181184
final boolean collectionsInDefaultFetchGroupEnabled =
182185
creationContext.getSessionFactoryOptions().isCollectionsInDefaultFetchGroupEnabled();
186+
final boolean supportsCascadeDelete = creationContext.getDialect().supportsCascadeDelete();
183187

184188
if ( persistentClass.hasPojoRepresentation() ) {
185189
final Component identifierMapperComponent = persistentClass.getIdentifierMapper();
186190
final CompositeType nonAggregatedCidMapper;
187191
final Set<String> idAttributeNames;
188-
189192
if ( identifierMapperComponent != null ) {
190193
nonAggregatedCidMapper = identifierMapperComponent.getType();
191194
idAttributeNames = new HashSet<>( );
@@ -226,6 +229,7 @@ public EntityMetamodel(
226229
propertyNullability = new boolean[propertySpan];
227230
propertyVersionability = new boolean[propertySpan];
228231
propertyLaziness = new boolean[propertySpan];
232+
propertyOnDeleteActions = new OnDeleteAction[propertySpan];
229233
cascadeStyles = new CascadeStyle[propertySpan];
230234
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231235

@@ -318,7 +322,7 @@ public EntityMetamodel(
318322
nonlazyPropertyUpdateability[i] = attribute.isUpdateable() && !lazy;
319323
propertyCheckability[i] = propertyUpdateability[i]
320324
|| propertyType.isAssociationType() && ( (AssociationType) propertyType ).isAlwaysDirtyChecked();
321-
325+
propertyOnDeleteActions[i] = supportsCascadeDelete ? attribute.getOnDeleteAction() : null;
322326
cascadeStyles[i] = attribute.getCascadeStyle();
323327
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
324328

@@ -887,4 +891,8 @@ public boolean isInstrumented() {
887891
public BytecodeEnhancementMetadata getBytecodeEnhancementMetadata() {
888892
return bytecodeEnhancementMetadata;
889893
}
894+
895+
public OnDeleteAction[] getPropertyOnDeleteActions() {
896+
return propertyOnDeleteActions;
897+
}
890898
}

0 commit comments

Comments
 (0)