Skip to content

Commit 20e62d7

Browse files
committed
introduce DetachedObjectException
and remove obsolete code relating to reassociation from lock() and refresh()
1 parent 48d74cf commit 20e62d7

File tree

16 files changed

+168
-250
lines changed

16 files changed

+168
-250
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate;
6+
7+
/**
8+
* Thrown if a detached instance of an entity class is passed to
9+
* a {@link Session} method that expects a managed instance.
10+
*
11+
* @author Gavin King
12+
*
13+
* @since 7.0
14+
*/
15+
@Incubating
16+
public class DetachedObjectException extends HibernateException {
17+
public DetachedObjectException(String message) {
18+
super( message );
19+
}
20+
}

hibernate-core/src/main/java/org/hibernate/collection/spi/AbstractPersistentCollection.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import java.io.Serializable;
88
import java.util.ArrayList;
99
import java.util.Collection;
10-
import java.util.Collections;
1110
import java.util.HashSet;
1211
import java.util.Iterator;
1312
import java.util.List;
@@ -22,8 +21,6 @@
2221
import org.hibernate.HibernateException;
2322
import org.hibernate.LazyInitializationException;
2423
import org.hibernate.engine.spi.CollectionEntry;
25-
import org.hibernate.engine.spi.EntityEntry;
26-
import org.hibernate.engine.spi.PersistenceContext;
2724
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2825
import org.hibernate.engine.spi.Status;
2926
import org.hibernate.engine.spi.TypedValue;
@@ -37,6 +34,7 @@
3734

3835
import org.checkerframework.checker.nullness.qual.Nullable;
3936

37+
import static java.util.Collections.emptyIterator;
4038
import static java.util.Collections.emptyList;
4139
import static org.hibernate.collection.internal.CollectionLogger.COLLECTION_LOGGER;
4240
import static org.hibernate.engine.internal.ForeignKeys.getEntityIdentifier;
@@ -686,15 +684,17 @@ public final boolean unsetSession(SharedSessionContractImplementor currentSessio
686684
if ( allowLoadOutsideTransaction
687685
&& !initialized
688686
&& session.getLoadQueryInfluencers().hasEnabledFilters() ) {
689-
COLLECTION_LOGGER.enabledFiltersWhenDetachFromSession( collectionInfoString( getRole(), getKey() ) );
687+
COLLECTION_LOGGER.enabledFiltersWhenDetachFromSession(
688+
collectionInfoString( getRole(), getKey() ) );
690689
}
691690
session = null;
692691
}
693692
return true;
694693
}
695694
else {
696695
if ( session != null ) {
697-
COLLECTION_LOGGER.logCannotUnsetUnexpectedSessionInCollection( unexpectedSessionStateMessage( currentSession ) );
696+
COLLECTION_LOGGER.logCannotUnsetUnexpectedSessionInCollection(
697+
unexpectedSessionStateMessage( currentSession ) );
698698
}
699699
return false;
700700
}
@@ -704,20 +704,23 @@ private void logDiscardedQueuedOperations() {
704704
try {
705705
if ( wasTransactionRolledBack() ) {
706706
// It was due to a rollback.
707-
if ( COLLECTION_LOGGER.isDebugEnabled()) {
708-
COLLECTION_LOGGER.queuedOperationWhenDetachFromSessionOnRollback( collectionInfoString( getRole(), getKey() ) );
707+
if ( COLLECTION_LOGGER.isDebugEnabled() ) {
708+
COLLECTION_LOGGER.queuedOperationWhenDetachFromSessionOnRollback(
709+
collectionInfoString( getRole(), getKey() ) );
709710
}
710711
}
711712
else {
712713
// We don't know why the collection is being detached.
713714
// Just log the info.
714-
COLLECTION_LOGGER.queuedOperationWhenDetachFromSession( collectionInfoString( getRole(), getKey() ) );
715+
COLLECTION_LOGGER.queuedOperationWhenDetachFromSession(
716+
collectionInfoString( getRole(), getKey() ) );
715717
}
716718
}
717719
catch (Exception e) {
718720
// We don't know why the collection is being detached.
719721
// Just log the info.
720-
COLLECTION_LOGGER.queuedOperationWhenDetachFromSession( collectionInfoString( getRole(), getKey() ) );
722+
COLLECTION_LOGGER.queuedOperationWhenDetachFromSession(
723+
collectionInfoString( getRole(), getKey() ) );
721724
}
722725
}
723726

@@ -756,7 +759,8 @@ else if ( this.session != null ) {
756759
}
757760
}
758761
if ( hasQueuedOperations() ) {
759-
COLLECTION_LOGGER.queuedOperationWhenAttachToSession( collectionInfoString( getRole(), getKey() ) );
762+
COLLECTION_LOGGER.queuedOperationWhenAttachToSession(
763+
collectionInfoString( getRole(), getKey() ) );
760764
}
761765
this.session = session;
762766
return true;
@@ -872,7 +876,7 @@ public void remove() {
872876
};
873877
}
874878
else {
875-
return Collections.emptyIterator();
879+
return emptyIterator();
876880
}
877881
}
878882

@@ -1070,8 +1074,8 @@ public <A> A[] toArray(A[] array) {
10701074
public final boolean equals(Object object) {
10711075
return object == this
10721076
|| object instanceof Set<?> that
1073-
&& that.size() == this.size()
1074-
&& containsAll( that );
1077+
&& that.size() == this.size()
1078+
&& containsAll( that );
10751079
}
10761080

10771081
@Override
@@ -1306,11 +1310,11 @@ protected static <E> Collection<E> getOrphans(
13061310
// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
13071311
final java.util.Set<Object> currentIds = new HashSet<>();
13081312
final java.util.Set<Object> currentSaving = new IdentitySet<>();
1309-
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
1313+
final var persistenceContext = session.getPersistenceContextInternal();
13101314
for ( Object current : currentElements ) {
13111315
if ( current != null && isNotTransient( entityName, current, null, session ) ) {
1312-
final EntityEntry ee = persistenceContext.getEntry( current );
1313-
if ( ee != null && ee.getStatus() == Status.SAVING ) {
1316+
final var entityEntry = persistenceContext.getEntry( current );
1317+
if ( entityEntry != null && entityEntry.getStatus() == Status.SAVING ) {
13141318
currentSaving.add( current );
13151319
}
13161320
else {
@@ -1335,7 +1339,7 @@ protected static <E> Collection<E> getOrphans(
13351339

13361340
private static boolean mayUseIdDirect(Type idType) {
13371341
if ( idType instanceof BasicType<?> basicType ) {
1338-
final Class<?> javaType = basicType.getJavaType();
1342+
final var javaType = basicType.getJavaType();
13391343
return javaType == String.class
13401344
|| javaType == Integer.class
13411345
|| javaType == Long.class

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,9 @@ private static <T> void cascadeLogicalOneToOneOrphanRemoval(
322322
if ( child == null || loadedValue != null && child != loadedValue ) {
323323
EntityEntry valueEntry = persistenceContext.getEntry( loadedValue );
324324
if ( valueEntry == null && isHibernateProxy( loadedValue ) ) {
325-
// un-proxy and re-associate for cascade operation
325+
// unproxy and reassociate for cascade operation
326326
// useful for @OneToOne defined as FetchType.LAZY
327+
//TODO: what should really happen here???
327328
loadedValue = persistenceContext.unproxyAndReassociate( loadedValue );
328329
valueEntry = persistenceContext.getEntry( loadedValue );
329330
// HHH-11965

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

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.function.Supplier;
2424

2525
import org.hibernate.AssertionFailure;
26+
import org.hibernate.DetachedObjectException;
2627
import org.hibernate.Hibernate;
2728
import org.hibernate.HibernateException;
2829
import org.hibernate.LockMode;
@@ -699,11 +700,11 @@ public boolean reassociateIfUninitializedProxy(Object value) throws MappingExcep
699700
return true;
700701
}
701702
// or an uninitialized enhanced entity ("bytecode proxy")
702-
if ( isPersistentAttributeInterceptable( value ) ) {
703-
final var bytecodeProxy = asPersistentAttributeInterceptable( value );
703+
else if ( isPersistentAttributeInterceptable( value ) ) {
704704
final var interceptor =
705705
(BytecodeLazyAttributeInterceptor)
706-
bytecodeProxy.$$_hibernate_getInterceptor();
706+
asPersistentAttributeInterceptable( value )
707+
.$$_hibernate_getInterceptor();
707708
if ( interceptor != null ) {
708709
interceptor.setSession( getSession() );
709710
}
@@ -713,6 +714,32 @@ public boolean reassociateIfUninitializedProxy(Object value) throws MappingExcep
713714
return false;
714715
}
715716

717+
@Override
718+
public boolean isUninitializedProxy(Object value) throws MappingException {
719+
if ( !Hibernate.isInitialized( value ) ) {
720+
// could be a proxy
721+
final var lazyInitializer = extractLazyInitializer( value );
722+
if ( lazyInitializer != null ) {
723+
if ( lazyInitializer.getSession() != session ) {
724+
throw new DetachedObjectException( "Given proxy does not belong to this persistence context" );
725+
}
726+
return true;
727+
}
728+
// or an uninitialized enhanced entity ("bytecode proxy")
729+
else if ( isPersistentAttributeInterceptable( value ) ) {
730+
final var interceptor =
731+
(BytecodeLazyAttributeInterceptor)
732+
asPersistentAttributeInterceptable( value )
733+
.$$_hibernate_getInterceptor();
734+
if ( interceptor != null && interceptor.getLinkedSession() != session ) {
735+
throw new DetachedObjectException( "Given proxy does not belong to this persistence context" );
736+
}
737+
return true;
738+
}
739+
}
740+
return false;
741+
}
742+
716743
@Override
717744
public void reassociateProxy(Object value, Object id) throws MappingException {
718745
final var lazyInitializer = extractLazyInitializer( value );
@@ -755,7 +782,7 @@ public Object unproxy(Object maybeProxy) throws HibernateException {
755782
final var lazyInitializer = extractLazyInitializer( maybeProxy );
756783
if ( lazyInitializer != null ) {
757784
if ( lazyInitializer.isUninitialized() ) {
758-
throw new PersistentObjectException( "object was an uninitialized proxy for "
785+
throw new PersistentObjectException( "Object was an uninitialized proxy for "
759786
+ lazyInitializer.getEntityName() );
760787
}
761788
//unwrap the object and return
@@ -766,6 +793,34 @@ public Object unproxy(Object maybeProxy) throws HibernateException {
766793
}
767794
}
768795

796+
@Override
797+
public Object unproxyEvenIfUninitialized(final Object maybeProxy) throws HibernateException {
798+
final var lazyInitializer = extractLazyInitializer( maybeProxy );
799+
if ( lazyInitializer != null ) {
800+
if ( lazyInitializer.getSession() != session ) {
801+
throw new DetachedObjectException( "Given proxy does not belong to this persistence context" );
802+
}
803+
//initialize + unwrap the object and return it
804+
return lazyInitializer.getImplementation();
805+
}
806+
else if ( isPersistentAttributeInterceptable( maybeProxy ) ) {
807+
final var interceptor =
808+
asPersistentAttributeInterceptable( maybeProxy )
809+
.$$_hibernate_getInterceptor();
810+
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) {
811+
if ( lazinessInterceptor.getLinkedSession() != session ) {
812+
throw new DetachedObjectException( "Given proxy does not belong to this persistence context" );
813+
}
814+
lazinessInterceptor.forceInitialize( maybeProxy, null );
815+
}
816+
return maybeProxy;
817+
}
818+
else {
819+
return maybeProxy;
820+
}
821+
}
822+
823+
769824
@Override
770825
public Object unproxyAndReassociate(final Object maybeProxy) throws HibernateException {
771826
final var lazyInitializer = extractLazyInitializer( maybeProxy );

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,12 @@ EntityEntry addReferenceEntry(
296296
*/
297297
Object unproxy(Object maybeProxy);
298298

299+
@Incubating
300+
Object unproxyEvenIfUninitialized(final Object maybeProxy);
301+
302+
@Incubating
303+
boolean isUninitializedProxy(Object value);
304+
299305
/**
300306
* Possibly unproxy the given reference and reassociate it with the current session.
301307
*

hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,18 +182,19 @@ private void deleteDetachedEntity(
182182

183183
new OnUpdateVisitor( source, id, entity ).process( entity, persister );
184184

185-
final var persistenceContext = source.getPersistenceContextInternal();
186-
final var entityEntry = persistenceContext.addEntity(
187-
entity,
188-
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
189-
persister.getValues( entity ),
190-
key,
191-
version,
192-
LockMode.NONE,
193-
true,
194-
persister,
195-
false
196-
);
185+
final var entityEntry =
186+
source.getPersistenceContextInternal()
187+
.addEntity(
188+
entity,
189+
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
190+
persister.getValues( entity ),
191+
key,
192+
version,
193+
LockMode.NONE,
194+
true,
195+
persister,
196+
false
197+
);
197198
persister.afterReassociate( entity, source );
198199

199200
delete( event, transientEntities, source, entity, persister, id, version, entityEntry );
@@ -524,7 +525,7 @@ protected void cascadeAfterDelete(
524525
final var persistenceContext = session.getPersistenceContextInternal();
525526
persistenceContext.incrementCascadeLevel();
526527
try {
527-
// cascade-delete to many-to-one AFTER the parent was deleted
528+
// cascade delete to many-to-one AFTER the parent was deleted
528529
Cascade.cascade(
529530
CascadingActions.REMOVE,
530531
CascadePoint.BEFORE_INSERT_AFTER_DELETE,

0 commit comments

Comments
 (0)