Skip to content

Commit f4f0490

Browse files
committed
HHH-9518 : Exception handling around collection session access needs to be improved
1 parent 5c7360c commit f4f0490

File tree

3 files changed

+756
-15
lines changed

3 files changed

+756
-15
lines changed

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

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.hibernate.engine.spi.SessionImplementor;
3131
import org.hibernate.engine.spi.Status;
3232
import org.hibernate.engine.spi.TypedValue;
33+
import org.hibernate.internal.CoreLogging;
34+
import org.hibernate.internal.CoreMessageLogger;
3335
import org.hibernate.internal.SessionFactoryRegistry;
3436
import org.hibernate.internal.util.MarkerObject;
3537
import org.hibernate.internal.util.collections.EmptyIterator;
@@ -45,15 +47,14 @@
4547
import org.hibernate.type.Type;
4648
import org.hibernate.type.UUIDBinaryType;
4749
import org.hibernate.type.UUIDCharType;
48-
import org.jboss.logging.Logger;
4950

5051
/**
5152
* Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
5253
*
5354
* @author Gavin King
5455
*/
5556
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
56-
private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
57+
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
5758

5859
private transient SessionImplementor session;
5960
private boolean initialized;
@@ -255,7 +256,7 @@ else if ( !session.isConnected() ) {
255256
( (Session) session ).close();
256257
}
257258
catch (Exception e) {
258-
log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
259+
LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
259260
}
260261
session = originalSession;
261262
}
@@ -587,6 +588,9 @@ public final boolean unsetSession(SessionImplementor currentSession) {
587588
return true;
588589
}
589590
else {
591+
if ( this.session != null ) {
592+
LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) );
593+
}
590594
return false;
591595
}
592596
}
@@ -606,28 +610,23 @@ protected void prepareForPossibleLoadingOutsideTransaction() {
606610
}
607611
}
608612

609-
610613
@Override
611614
public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
612615
if ( session == this.session ) {
613616
return false;
614617
}
615618
else {
616-
if ( isConnectedToSession() ) {
617-
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
618-
if ( ce == null ) {
619+
if ( this.session != null ) {
620+
final String msg = generateUnexpectedSessionStateMessage( session );
621+
if ( isConnectedToSession() ) {
619622
throw new HibernateException(
620-
"Illegal attempt to associate a collection with two open sessions"
623+
"Illegal attempt to associate a collection with two open sessions. " + msg
621624
);
622625
}
623626
else {
624-
throw new HibernateException(
625-
"Illegal attempt to associate a collection with two open sessions: " +
626-
MessageHelper.collectionInfoString(
627-
ce.getLoadedPersister(), this,
628-
ce.getLoadedKey(), session
629-
)
630-
);
627+
LOG.logUnexpectedSessionInCollectionNotConnected( msg );
628+
this.session = session;
629+
return true;
631630
}
632631
}
633632
else {
@@ -637,6 +636,46 @@ public final boolean setCurrentSession(SessionImplementor session) throws Hibern
637636
}
638637
}
639638

639+
private String generateUnexpectedSessionStateMessage(SessionImplementor session) {
640+
// NOTE: If this.session != null, this.session may be operating on this collection
641+
// (e.g., by changing this.role, this.key, or even this.session) in a different thread.
642+
643+
// Grab the current role and key (it can still get changed by this.session...)
644+
// If this collection is connected to this.session, then this.role and this.key should
645+
// be consistent with the CollectionEntry in this.session (as long as this.session doesn't
646+
// change it). Don't access the CollectionEntry in this.session because that could result
647+
// in multi-threaded access to this.session.
648+
final String roleCurrent = role;
649+
final Serializable keyCurrent = key;
650+
651+
final StringBuilder sb = new StringBuilder( "Collection : " );
652+
if ( roleCurrent != null ) {
653+
sb.append( MessageHelper.collectionInfoString( roleCurrent, keyCurrent ) );
654+
}
655+
else {
656+
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
657+
if ( ce != null ) {
658+
sb.append(
659+
MessageHelper.collectionInfoString(
660+
ce.getLoadedPersister(),
661+
this,
662+
ce.getLoadedKey(),
663+
session
664+
)
665+
);
666+
}
667+
else {
668+
sb.append( "<unknown>" );
669+
}
670+
}
671+
// only include the collection contents if debug logging
672+
if ( LOG.isDebugEnabled() ) {
673+
final String collectionContents = wasInitialized() ? toString() : "<uninitialized>";
674+
sb.append( "\nCollection contents: [" ).append( collectionContents ).append( "]" );
675+
}
676+
return sb.toString();
677+
}
678+
640679
@Override
641680
public boolean needsRecreate(CollectionPersister persister) {
642681
// Workaround for situations like HHH-7072. If the collection element is a component that consists entirely

hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,4 +1734,12 @@ void cannotResolveNonNullableTransientDependencies(
17341734

17351735
@Message(value = "The ClassLoaderService can not be reused. This instance was stopped already.", id = 469)
17361736
HibernateException usingStoppedClassLoaderService();
1737+
1738+
@LogMessage(level = WARN)
1739+
@Message(value = "An unexpected session is defined for a collection, but the collection is not connected to that session. A persistent collection may only be associated with one session at a time. Overwriting session. %s", id = 470)
1740+
void logUnexpectedSessionInCollectionNotConnected(String msg);
1741+
1742+
@LogMessage(level = WARN)
1743+
@Message(value = "Cannot unset session in a collection because an unexpected session is defined. A persistent collection may only be associated with one session at a time. %s", id = 471 )
1744+
void logCannotUnsetUnexpectedSessionInCollection(String msg);
17371745
}

0 commit comments

Comments
 (0)