Skip to content

Commit 5f7d562

Browse files
committed
produce more helpful TransientPropertyValueExceptions
1 parent 83a88f6 commit 5f7d562

File tree

7 files changed

+171
-69
lines changed

7 files changed

+171
-69
lines changed

hibernate-core/src/main/java/org/hibernate/TransientPropertyValueException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ public String getPropertyName() {
7777

7878
@Override
7979
public String getMessage() {
80-
return super.getMessage() + " for entity "
80+
return super.getMessage() + " ["
8181
+ qualify( propertyOwnerEntityName, propertyName )
82-
+ " -> " + transientEntityName;
82+
+ " -> " + transientEntityName + "]";
8383
}
8484
}

hibernate-core/src/main/java/org/hibernate/action/internal/UnresolvedEntityInsertActions.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,17 @@ public void checkNoUnresolvedActionsAfterOperation() throws PropertyValueExcepti
115115
final Object firstTransientDependency =
116116
nonNullableTransientDependencies.getNonNullableTransientEntities().iterator().next();
117117
final String firstPropertyPath =
118-
nonNullableTransientDependencies.getNonNullableTransientPropertyPaths( firstTransientDependency ).iterator().next();
119-
118+
nonNullableTransientDependencies.getNonNullableTransientPropertyPaths( firstTransientDependency )
119+
.iterator().next();
120+
final String entityName = firstDependentAction.getEntityName();
121+
final String transientEntityName =
122+
firstDependentAction.getSession().guessEntityName( firstTransientDependency );
120123
throw new TransientPropertyValueException(
121-
"Not-null property references a transient value - transient instance must be saved before current operation",
122-
firstDependentAction.getSession().guessEntityName( firstTransientDependency ),
123-
firstDependentAction.getEntityName(),
124+
"Instance of '" + entityName
125+
+ "' references an unsaved transient instance of '" + transientEntityName
126+
+ "' (persist the transient instance)",
127+
transientEntityName,
128+
entityName,
124129
firstPropertyPath
125130
);
126131
}

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

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ else if ( action.performOnLazyProperty() && type instanceof EntityType ) {
162162
action,
163163
cascadePoint,
164164
eventSource,
165+
persister.getEntityName(),
165166
null,
166167
parent,
167168
child,
@@ -203,6 +204,7 @@ private static <T> void cascadeProperty(
203204
final CascadingAction<T> action,
204205
final CascadePoint cascadePoint,
205206
final EventSource eventSource,
207+
final String entityName,
206208
List<String> componentPath,
207209
final Object parent,
208210
final Object child,
@@ -219,6 +221,8 @@ private static <T> void cascadeProperty(
219221
action,
220222
cascadePoint,
221223
eventSource,
224+
entityName,
225+
propertyName,
222226
componentPath,
223227
parent,
224228
child,
@@ -240,6 +244,7 @@ else if ( type instanceof ComponentType componentType ) {
240244
action,
241245
cascadePoint,
242246
eventSource,
247+
entityName,
243248
componentPath,
244249
parent,
245250
child,
@@ -385,6 +390,7 @@ private static <T> void cascadeComponent(
385390
final CascadingAction<T> action,
386391
final CascadePoint cascadePoint,
387392
final EventSource eventSource,
393+
final String entityName,
388394
final List<String> componentPath,
389395
final Object parent,
390396
final Object child,
@@ -398,7 +404,7 @@ private static <T> void cascadeComponent(
398404
final String subPropertyName = propertyNames[i];
399405
final Type subPropertyType = types[i];
400406
if ( action.appliesTo( subPropertyType, componentPropertyStyle )
401-
|| componentPropertyStyle.hasOrphanDelete() && action.deleteOrphans() ) {
407+
|| componentPropertyStyle.hasOrphanDelete() && action.deleteOrphans() ) {
402408
if ( children == null ) {
403409
// Get children on demand.
404410
children = componentType.getPropertyValues( child, eventSource );
@@ -407,6 +413,7 @@ private static <T> void cascadeComponent(
407413
action,
408414
cascadePoint,
409415
eventSource,
416+
entityName,
410417
componentPath,
411418
parent,
412419
children[i],
@@ -424,6 +431,8 @@ private static <T> void cascadeAssociation(
424431
final CascadingAction<T> action,
425432
final CascadePoint cascadePoint,
426433
final EventSource eventSource,
434+
final String entityName,
435+
final String propertyName,
427436
final List<String> componentPath,
428437
final Object parent,
429438
final Object child,
@@ -432,13 +441,27 @@ private static <T> void cascadeAssociation(
432441
final T anything,
433442
final boolean isCascadeDeleteEnabled) {
434443
if ( type instanceof EntityType || type instanceof AnyType ) {
435-
cascadeToOne( action, eventSource, parent, child, type, style, anything, isCascadeDeleteEnabled );
444+
cascadeToOne(
445+
action,
446+
eventSource,
447+
parent,
448+
child,
449+
type,
450+
style,
451+
anything,
452+
isCascadeDeleteEnabled,
453+
entityName,
454+
propertyName,
455+
componentPath
456+
);
436457
}
437458
else if ( type instanceof CollectionType collectionType ) {
438459
cascadeCollection(
439460
action,
440461
cascadePoint,
441462
eventSource,
463+
entityName,
464+
propertyName,
442465
componentPath,
443466
parent,
444467
child,
@@ -456,6 +479,8 @@ private static <T> void cascadeCollection(
456479
final CascadingAction<T> action,
457480
final CascadePoint cascadePoint,
458481
final EventSource eventSource,
482+
final String entityName,
483+
final String propertyName,
459484
final List<String> componentPath,
460485
final Object parent,
461486
final Object child,
@@ -474,6 +499,8 @@ private static <T> void cascadeCollection(
474499
? CascadePoint.AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION
475500
: cascadePoint,
476501
eventSource,
502+
entityName,
503+
propertyName,
477504
componentPath,
478505
parent,
479506
child,
@@ -497,17 +524,27 @@ private static <T> void cascadeToOne(
497524
final Type type,
498525
final CascadeStyle style,
499526
final T anything,
500-
final boolean isCascadeDeleteEnabled) {
501-
final String entityName =
502-
type instanceof EntityType entityType
503-
? entityType.getAssociatedEntityName()
504-
: null;
527+
final boolean isCascadeDeleteEnabled,
528+
final String parentEntityName,
529+
final String propertyName,
530+
final List<String> componentPath) {
505531
if ( style.reallyDoCascade( action ) ) {
506532
//not really necessary, but good for consistency...
507533
final PersistenceContext persistenceContext = eventSource.getPersistenceContextInternal();
508534
persistenceContext.addChildParent( child, parent );
509535
try {
510-
action.cascade( eventSource, child, entityName, anything, isCascadeDeleteEnabled );
536+
action.cascade(
537+
eventSource,
538+
child,
539+
type instanceof EntityType entityType
540+
? entityType.getAssociatedEntityName()
541+
: null,
542+
parentEntityName,
543+
propertyName,
544+
componentPath,
545+
anything,
546+
isCascadeDeleteEnabled
547+
);
511548
}
512549
finally {
513550
persistenceContext.removeChildParent( child );
@@ -522,6 +559,8 @@ private static <T> void cascadeCollectionElements(
522559
final CascadingAction<T> action,
523560
final CascadePoint cascadePoint,
524561
final EventSource eventSource,
562+
final String entityName,
563+
final String propertyName,
525564
final List<String> componentPath,
526565
final Object parent,
527566
final Object child,
@@ -546,12 +585,14 @@ private static <T> void cascadeCollectionElements(
546585
action,
547586
cascadePoint,
548587
eventSource,
588+
entityName,
549589
componentPath,
550590
parent,
551591
iterator.next(),
552592
elemType,
553593
style,
554-
collectionType.getRole().substring( collectionType.getRole().lastIndexOf('.') + 1 ),
594+
propertyName,
595+
// collectionType.getRole().substring( collectionType.getRole().lastIndexOf('.') + 1 ),
555596
anything,
556597
isCascadeDeleteEnabled
557598
);
@@ -583,8 +624,8 @@ private static <T> void cascadeCollectionElements(
583624
// we can do the cast since orphan-delete does not apply to:
584625
// 1. newly instantiated collections
585626
// 2. arrays (we can't track orphans for detached arrays)
586-
final String entityName = collectionType.getAssociatedEntityName( eventSource.getFactory() );
587-
deleteOrphans( eventSource, entityName, persistentCollection );
627+
final String elementEntityName = collectionType.getAssociatedEntityName( eventSource.getFactory() );
628+
deleteOrphans( eventSource, elementEntityName, persistentCollection );
588629

589630
if ( traceEnabled ) {
590631
LOG.tracev( "Done deleting orphans for collection: {0}", collectionType.getRole() );

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import org.hibernate.AssertionFailure;
2323
import org.hibernate.HibernateException;
2424
import org.hibernate.PropertyValueException;
25-
import org.hibernate.TransientObjectException;
25+
import org.hibernate.TransientPropertyValueException;
2626
import org.hibernate.action.internal.AbstractEntityInsertAction;
2727
import org.hibernate.action.internal.BulkOperationCleanupAction;
2828
import org.hibernate.action.internal.CollectionRecreateAction;
@@ -497,11 +497,15 @@ public void executeActions() throws HibernateException {
497497
final NonNullableTransientDependencies transientEntities = insertAction.findNonNullableTransientEntities();
498498
final Object transientEntity = transientEntities.getNonNullableTransientEntities().iterator().next();
499499
final String path = transientEntities.getNonNullableTransientPropertyPaths(transientEntity).iterator().next();
500-
//TODO: should be TransientPropertyValueException
501-
throw new TransientObjectException( "Persistent instance of '" + insertAction.getEntityName()
502-
+ "' with id [" + insertAction.getId()
503-
+ "] references an unsaved transient instance via attribute '" + path
504-
+ "' (save the transient instance before flushing)" );
500+
final String transientEntityName = session.bestGuessEntityName( transientEntity );
501+
final String entityName = insertAction.getEntityName();
502+
throw new TransientPropertyValueException(
503+
"Persistent instance of '" + entityName + "' with id [" + insertAction.getId()
504+
+ "] references an unsaved transient instance of '" + transientEntityName
505+
+ "' (persist the transient instance before flushing)",
506+
entityName,
507+
transientEntityName,
508+
path );
505509
}
506510

507511
for ( OrderedActions action : ORDERED_OPERATIONS ) {

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
package org.hibernate.engine.spi;
66

77
import java.util.Iterator;
8+
import java.util.List;
89

9-
import org.hibernate.HibernateException;
10+
import org.checkerframework.checker.nullness.qual.Nullable;
1011
import org.hibernate.engine.internal.CascadePoint;
1112
import org.hibernate.event.spi.EventSource;
1213
import org.hibernate.persister.entity.EntityPersister;
@@ -27,17 +28,38 @@ public interface CascadingAction<T> {
2728
*
2829
* @param session The session within which the cascade is occurring.
2930
* @param child The child to which cascading should be performed.
30-
* @param entityName The child's entity name
31-
* @param anything Anything ;) Typically some form of cascade-local cache
32-
* which is specific to each CascadingAction type
31+
* @param childEntityName The name of the child entity
32+
* @param parentEntityName The name of the parent entity
33+
* @param propertyName The name of the attribute of the parent entity being cascaded
34+
* @param attributePath The full path of the attribute of the parent entity being cascaded
35+
* @param anything Anything ;) Typically some form of cascade-local cache
36+
* which is specific to each {@link CascadingAction} type
3337
* @param isCascadeDeleteEnabled Are cascading deletes enabled.
3438
*/
3539
void cascade(
3640
EventSource session,
3741
Object child,
38-
String entityName,
42+
String childEntityName,
43+
String parentEntityName,
44+
String propertyName,
45+
@Nullable List<String> attributePath,
3946
T anything,
40-
boolean isCascadeDeleteEnabled) throws HibernateException;
47+
boolean isCascadeDeleteEnabled);
48+
49+
/**
50+
* @deprecated No longer called. Will be removed.
51+
*/
52+
@Deprecated(since = "7", forRemoval = true)
53+
default void cascade(
54+
EventSource session,
55+
Object child,
56+
String childEntityName,
57+
T anything,
58+
boolean isCascadeDeleteEnabled) {
59+
cascade( session, child, childEntityName,
60+
"", "", null,
61+
anything, isCascadeDeleteEnabled );
62+
}
4163

4264
/**
4365
* Given a collection, get an iterator of the children upon which the

0 commit comments

Comments
 (0)