mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-26 05:14:57 +00:00
HHH-9518 : Exception handling around collection session access needs to be improved
This commit is contained in:
parent
5c7360c69a
commit
f4f04901e2
@ -30,6 +30,8 @@
|
|||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.Status;
|
import org.hibernate.engine.spi.Status;
|
||||||
import org.hibernate.engine.spi.TypedValue;
|
import org.hibernate.engine.spi.TypedValue;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.SessionFactoryRegistry;
|
import org.hibernate.internal.SessionFactoryRegistry;
|
||||||
import org.hibernate.internal.util.MarkerObject;
|
import org.hibernate.internal.util.MarkerObject;
|
||||||
import org.hibernate.internal.util.collections.EmptyIterator;
|
import org.hibernate.internal.util.collections.EmptyIterator;
|
||||||
@ -45,7 +47,6 @@
|
|||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.type.UUIDBinaryType;
|
import org.hibernate.type.UUIDBinaryType;
|
||||||
import org.hibernate.type.UUIDCharType;
|
import org.hibernate.type.UUIDCharType;
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
|
* Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
|
||||||
@ -53,7 +54,7 @@
|
|||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
|
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
|
||||||
private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
|
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
|
||||||
|
|
||||||
private transient SessionImplementor session;
|
private transient SessionImplementor session;
|
||||||
private boolean initialized;
|
private boolean initialized;
|
||||||
@ -255,7 +256,7 @@ else if ( !session.isConnected() ) {
|
|||||||
( (Session) session ).close();
|
( (Session) session ).close();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
||||||
}
|
}
|
||||||
session = originalSession;
|
session = originalSession;
|
||||||
}
|
}
|
||||||
@ -587,6 +588,9 @@ public final boolean unsetSession(SessionImplementor currentSession) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if ( this.session != null ) {
|
||||||
|
LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) );
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -606,28 +610,23 @@ protected void prepareForPossibleLoadingOutsideTransaction() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
|
public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
|
||||||
if ( session == this.session ) {
|
if ( session == this.session ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( isConnectedToSession() ) {
|
if ( this.session != null ) {
|
||||||
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
|
final String msg = generateUnexpectedSessionStateMessage( session );
|
||||||
if ( ce == null ) {
|
if ( isConnectedToSession() ) {
|
||||||
throw new HibernateException(
|
throw new HibernateException(
|
||||||
"Illegal attempt to associate a collection with two open sessions"
|
"Illegal attempt to associate a collection with two open sessions. " + msg
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new HibernateException(
|
LOG.logUnexpectedSessionInCollectionNotConnected( msg );
|
||||||
"Illegal attempt to associate a collection with two open sessions: " +
|
this.session = session;
|
||||||
MessageHelper.collectionInfoString(
|
return true;
|
||||||
ce.getLoadedPersister(), this,
|
|
||||||
ce.getLoadedKey(), session
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -637,6 +636,46 @@ public final boolean setCurrentSession(SessionImplementor session) throws Hibern
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String generateUnexpectedSessionStateMessage(SessionImplementor session) {
|
||||||
|
// NOTE: If this.session != null, this.session may be operating on this collection
|
||||||
|
// (e.g., by changing this.role, this.key, or even this.session) in a different thread.
|
||||||
|
|
||||||
|
// Grab the current role and key (it can still get changed by this.session...)
|
||||||
|
// If this collection is connected to this.session, then this.role and this.key should
|
||||||
|
// be consistent with the CollectionEntry in this.session (as long as this.session doesn't
|
||||||
|
// change it). Don't access the CollectionEntry in this.session because that could result
|
||||||
|
// in multi-threaded access to this.session.
|
||||||
|
final String roleCurrent = role;
|
||||||
|
final Serializable keyCurrent = key;
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder( "Collection : " );
|
||||||
|
if ( roleCurrent != null ) {
|
||||||
|
sb.append( MessageHelper.collectionInfoString( roleCurrent, keyCurrent ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
|
||||||
|
if ( ce != null ) {
|
||||||
|
sb.append(
|
||||||
|
MessageHelper.collectionInfoString(
|
||||||
|
ce.getLoadedPersister(),
|
||||||
|
this,
|
||||||
|
ce.getLoadedKey(),
|
||||||
|
session
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sb.append( "<unknown>" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only include the collection contents if debug logging
|
||||||
|
if ( LOG.isDebugEnabled() ) {
|
||||||
|
final String collectionContents = wasInitialized() ? toString() : "<uninitialized>";
|
||||||
|
sb.append( "\nCollection contents: [" ).append( collectionContents ).append( "]" );
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needsRecreate(CollectionPersister persister) {
|
public boolean needsRecreate(CollectionPersister persister) {
|
||||||
// Workaround for situations like HHH-7072. If the collection element is a component that consists entirely
|
// Workaround for situations like HHH-7072. If the collection element is a component that consists entirely
|
||||||
|
@ -1734,4 +1734,12 @@ void cannotResolveNonNullableTransientDependencies(
|
|||||||
|
|
||||||
@Message(value = "The ClassLoaderService can not be reused. This instance was stopped already.", id = 469)
|
@Message(value = "The ClassLoaderService can not be reused. This instance was stopped already.", id = 469)
|
||||||
HibernateException usingStoppedClassLoaderService();
|
HibernateException usingStoppedClassLoaderService();
|
||||||
|
|
||||||
|
@LogMessage(level = WARN)
|
||||||
|
@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)
|
||||||
|
void logUnexpectedSessionInCollectionNotConnected(String msg);
|
||||||
|
|
||||||
|
@LogMessage(level = WARN)
|
||||||
|
@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 )
|
||||||
|
void logCannotUnsetUnexpectedSessionInCollection(String msg);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,694 @@
|
|||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.collection.multisession;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.ElementCollection;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.collection.internal.AbstractPersistentCollection;
|
||||||
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
|
import org.hibernate.engine.spi.CollectionEntry;
|
||||||
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gail Badner
|
||||||
|
*/
|
||||||
|
public class MultipleSessionCollectionTest extends BaseCoreFunctionalTestCase {
|
||||||
|
private static final Logger log = Logger.getLogger( MultipleSessionCollectionTest.class );
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testSaveOrUpdateOwnerWithCollectionInNewSessionBeforeFlush() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.saveOrUpdate( p );
|
||||||
|
|
||||||
|
// try to save the same entity in a new session before flushing the first session
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( p );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testSaveOrUpdateOwnerWithCollectionInNewSessionAfterFlush() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.saveOrUpdate( p );
|
||||||
|
s1.flush();
|
||||||
|
|
||||||
|
// try to save the same entity in a new session after flushing the first session
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( p );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testSaveOrUpdateOwnerWithUninitializedCollectionInNewSession() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
p = s1.get( Parent.class, p.id );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.children ) );
|
||||||
|
|
||||||
|
// try to save the same entity (with an uninitialized collection) in a new session
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( p );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to initialize collection, modify and commit in first session
|
||||||
|
assertFalse( Hibernate.isInitialized( p.children ) );
|
||||||
|
Hibernate.initialize( p.children );
|
||||||
|
p.children.add( new Child() );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( 2, pGet.children.size());
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testSaveOrUpdateOwnerWithInitializedCollectionInNewSession() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
p = s1.get( Parent.class, p.id );
|
||||||
|
Hibernate.initialize( p.children );
|
||||||
|
|
||||||
|
// try to save the same entity (with an initialized collection) in a new session
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( p );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testCopyPersistentCollectionReferenceBeforeFlush() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.persist( p );
|
||||||
|
|
||||||
|
// Copy p.children into a different Parent before flush and try to save in new session.
|
||||||
|
|
||||||
|
Parent pWithSameChildren = new Parent();
|
||||||
|
pWithSameChildren.children = p.children;
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( pWithSameChildren );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testCopyPersistentCollectionReferenceAfterFlush() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.persist( p );
|
||||||
|
s1.flush();
|
||||||
|
|
||||||
|
// Copy p.children into a different Parent after flush and try to save in new session.
|
||||||
|
|
||||||
|
Parent pWithSameChildren = new Parent();
|
||||||
|
pWithSameChildren.children = p.children;
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( pWithSameChildren );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testCopyUninitializedCollectionReferenceAfterGet() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
p = s1.get( Parent.class, p.id );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.children ) );
|
||||||
|
|
||||||
|
// Copy p.children (uninitialized) into a different Parent and try to save in new session.
|
||||||
|
|
||||||
|
Parent pWithSameChildren = new Parent();
|
||||||
|
pWithSameChildren.children = p.children;
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( pWithSameChildren );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testCopyInitializedCollectionReferenceAfterGet() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
p = s1.get( Parent.class, p.id );
|
||||||
|
Hibernate.initialize( p.children );
|
||||||
|
|
||||||
|
// Copy p.children (initialized) into a different Parent.children and try to save in new session.
|
||||||
|
|
||||||
|
Parent pWithSameChildren = new Parent();
|
||||||
|
pWithSameChildren.children = p.children;
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( pWithSameChildren );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testCopyInitializedCollectionReferenceToNewEntityCollectionRoleAfterGet() {
|
||||||
|
Parent p = new Parent();
|
||||||
|
Child c = new Child();
|
||||||
|
p.children.add( c );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
p = s1.get( Parent.class, p.id );
|
||||||
|
Hibernate.initialize( p.children );
|
||||||
|
|
||||||
|
// Copy p.children (initialized) into a different Parent.oldChildren (note different collection role)
|
||||||
|
// and try to save in new session.
|
||||||
|
|
||||||
|
Parent pWithSameChildren = new Parent();
|
||||||
|
pWithSameChildren.oldChildren = p.children;
|
||||||
|
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( pWithSameChildren );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit in first session
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
Parent pGet = s1.get( Parent.class, p.id );
|
||||||
|
assertEquals( c.id, pGet.children.iterator().next().id );
|
||||||
|
session.delete( pGet );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testDeleteCommitCopyToNewOwnerInNewSession() {
|
||||||
|
Parent p1 = new Parent();
|
||||||
|
p1.nickNames.add( "nick" );
|
||||||
|
Parent p2 = new Parent();
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.save( p1 );
|
||||||
|
s1.save( p2 );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.delete( p1 );
|
||||||
|
s1.flush();
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
|
||||||
|
// need to commit after flushing; otherwise, will get lock failure when try to move the collection below
|
||||||
|
|
||||||
|
assertNull( ( (SessionImplementor) s1 ).getPersistenceContext().getEntry( p1 ) );
|
||||||
|
CollectionEntry ceChildren = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.children );
|
||||||
|
CollectionEntry ceNickNames = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.nickNames );
|
||||||
|
assertNull( ceChildren );
|
||||||
|
assertNull( ceNickNames );
|
||||||
|
assertNull( ( ( AbstractPersistentCollection) p1.children ).getSession() );
|
||||||
|
assertNull( ( ( AbstractPersistentCollection) p1.oldChildren ).getSession() );
|
||||||
|
assertNull( ( ( AbstractPersistentCollection) p1.nickNames ).getSession() );
|
||||||
|
assertNull( ( (AbstractPersistentCollection) p1.oldNickNames ).getSession() );
|
||||||
|
|
||||||
|
// Assign the deleted collection to a different entity with same collection role (p2.nickNames)
|
||||||
|
|
||||||
|
p2.nickNames = p1.nickNames;
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
s2.saveOrUpdate( p2 );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
s2.close();
|
||||||
|
|
||||||
|
s1.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testDeleteCommitCopyToNewOwnerNewCollectionRoleInNewSession() {
|
||||||
|
Parent p1 = new Parent();
|
||||||
|
p1.nickNames.add( "nick" );
|
||||||
|
Parent p2 = new Parent();
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.save( p1 );
|
||||||
|
s1.save( p2 );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.delete( p1 );
|
||||||
|
s1.flush();
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
|
||||||
|
// need to commit after flushing; otherwise, will get lock failure when try to move the collection below
|
||||||
|
|
||||||
|
assertNull( ( (SessionImplementor) s1 ).getPersistenceContext().getEntry( p1 ) );
|
||||||
|
CollectionEntry ceChildren = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.children );
|
||||||
|
CollectionEntry ceNickNames = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.nickNames );
|
||||||
|
assertNull( ceChildren );
|
||||||
|
assertNull( ceNickNames );
|
||||||
|
assertNull( ( ( AbstractPersistentCollection) p1.children ).getSession() );
|
||||||
|
assertNull( ( ( AbstractPersistentCollection) p1.oldChildren ).getSession() );
|
||||||
|
assertNull( ( ( AbstractPersistentCollection) p1.nickNames ).getSession() );
|
||||||
|
assertNull( ( (AbstractPersistentCollection) p1.oldNickNames ).getSession() );
|
||||||
|
|
||||||
|
// Assign the deleted collection to a different entity with different collection role (p2.oldNickNames)
|
||||||
|
|
||||||
|
p2.oldNickNames = p1.nickNames;
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
s2.saveOrUpdate( p2 );
|
||||||
|
s2.getTransaction().commit();
|
||||||
|
s2.close();
|
||||||
|
|
||||||
|
s1.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testDeleteCopyToNewOwnerInNewSessionBeforeFlush() {
|
||||||
|
Parent p1 = new Parent();
|
||||||
|
p1.nickNames.add( "nick" );
|
||||||
|
Parent p2 = new Parent();
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.save( p1 );
|
||||||
|
s1.save( p2 );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.delete( p1 );
|
||||||
|
|
||||||
|
// Assign the deleted collection to a different entity with same collection role (p2.nickNames)
|
||||||
|
// before committing delete.
|
||||||
|
|
||||||
|
p2.nickNames = p1.nickNames;
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( p2 );
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit the original delete
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-9518" )
|
||||||
|
public void testDeleteCopyToNewOwnerNewCollectionRoleInNewSessionBeforeFlush() {
|
||||||
|
Parent p1 = new Parent();
|
||||||
|
p1.nickNames.add( "nick" );
|
||||||
|
Parent p2 = new Parent();
|
||||||
|
Session s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.save( p1 );
|
||||||
|
s1.save( p2 );
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
|
||||||
|
s1 = openSession();
|
||||||
|
s1.getTransaction().begin();
|
||||||
|
s1.delete( p1 );
|
||||||
|
|
||||||
|
// Assign the deleted collection to a different entity with different collection role (p2.oldNickNames)
|
||||||
|
// before committing delete.
|
||||||
|
|
||||||
|
p2.oldNickNames = p1.nickNames;
|
||||||
|
Session s2 = openSession();
|
||||||
|
s2.getTransaction().begin();
|
||||||
|
try {
|
||||||
|
s2.saveOrUpdate( p2 );
|
||||||
|
fail( "should have thrown HibernateException" );
|
||||||
|
}
|
||||||
|
catch (HibernateException ex) {
|
||||||
|
log.error( ex );
|
||||||
|
s2.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
s2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still be able to commit the original delete
|
||||||
|
s1.getTransaction().commit();
|
||||||
|
s1.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Parent.class,
|
||||||
|
Child.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name="Parent")
|
||||||
|
public static class Parent {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private Set<String> nickNames = new HashSet<String>();
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
private Set<String> oldNickNames = new HashSet<String>();
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL)
|
||||||
|
@JoinColumn
|
||||||
|
private Set<Child> children = new HashSet<Child>();
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL)
|
||||||
|
@JoinColumn
|
||||||
|
private Set<Child> oldChildren = new HashSet<Child>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name="Child")
|
||||||
|
public static class Child {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user