HHH-5983 : Actions have non-transient references that cause inconsistencies after deserialization
This commit is contained in:
parent
e1c03f28fd
commit
b1036c09a7
|
@ -44,11 +44,12 @@ import java.io.Serializable;
|
|||
*/
|
||||
public abstract class CollectionAction implements Executable, Serializable, Comparable {
|
||||
private transient CollectionPersister persister;
|
||||
private final Serializable key;
|
||||
private final SessionImplementor session;
|
||||
private final String collectionRole;
|
||||
private transient SessionImplementor session;
|
||||
private final PersistentCollection collection;
|
||||
|
||||
private final Serializable key;
|
||||
private final String collectionRole;
|
||||
|
||||
public CollectionAction(
|
||||
final CollectionPersister persister,
|
||||
final PersistentCollection collection,
|
||||
|
@ -65,9 +66,19 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
|
|||
return collection;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
persister = session.getFactory().getCollectionPersister( collectionRole );
|
||||
/**
|
||||
* Reconnect to session after deserialization...
|
||||
*/
|
||||
public void afterDeserialize(SessionImplementor session) {
|
||||
if ( this.session != null || this.persister != null ) {
|
||||
throw new IllegalStateException( "already attached to a session." );
|
||||
}
|
||||
// IMPL NOTE: non-flushed changes code calls this method with session == null...
|
||||
// guard against NullPointerException
|
||||
if ( session != null ) {
|
||||
this.session = session;
|
||||
this.persister = session.getFactory().getCollectionPersister( collectionRole );
|
||||
}
|
||||
}
|
||||
|
||||
public final void beforeExecutions() throws CacheException {
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.engine.EntityEntry;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -45,9 +47,9 @@ public abstract class EntityAction
|
|||
|
||||
private final String entityName;
|
||||
private final Serializable id;
|
||||
private final Object instance;
|
||||
private final SessionImplementor session;
|
||||
|
||||
private transient Object instance;
|
||||
private transient SessionImplementor session;
|
||||
private transient EntityPersister persister;
|
||||
|
||||
/**
|
||||
|
@ -98,11 +100,18 @@ public abstract class EntityAction
|
|||
*/
|
||||
public final Serializable getId() {
|
||||
if ( id instanceof DelayedPostInsertIdentifier ) {
|
||||
return session.getPersistenceContext().getEntry( instance ).getId();
|
||||
Serializable eeId = session.getPersistenceContext().getEntry( instance ).getId();
|
||||
return eeId instanceof DelayedPostInsertIdentifier ? null : eeId;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public final DelayedPostInsertIdentifier getDelayedId() {
|
||||
return DelayedPostInsertIdentifier.class.isInstance( id ) ?
|
||||
DelayedPostInsertIdentifier.class.cast( id ) :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* entity instance accessor
|
||||
*
|
||||
|
@ -156,15 +165,19 @@ public abstract class EntityAction
|
|||
}
|
||||
|
||||
/**
|
||||
* Serialization...
|
||||
*
|
||||
* @param ois Thed object stream
|
||||
* @throws IOException Problem performing the default stream reading
|
||||
* @throws ClassNotFoundException Problem performing the default stream reading
|
||||
* Reconnect to session after deserialization...
|
||||
*/
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
persister = session.getFactory().getEntityPersister( entityName );
|
||||
public void afterDeserialize(SessionImplementor session) {
|
||||
if ( this.session != null || this.persister != null ) {
|
||||
throw new IllegalStateException( "already attached to a session." );
|
||||
}
|
||||
// IMPL NOTE: non-flushed changes code calls this method with session == null...
|
||||
// guard against NullPointerException
|
||||
if ( session != null ) {
|
||||
this.session = session;
|
||||
this.persister = session.getFactory().getEntityPersister( entityName );
|
||||
this.instance = session.getPersistenceContext().getEntity( new EntityKey( id, persister, session.getEntityMode() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.io.Serializable;
|
|||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.engine.EntityEntry;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.event.PostInsertEvent;
|
||||
|
@ -39,7 +40,7 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
|
||||
public final class EntityIdentityInsertAction extends EntityAction {
|
||||
|
||||
private final Object[] state;
|
||||
private transient Object[] state;
|
||||
private final boolean isDelayed;
|
||||
private final EntityKey delayedEntityKey;
|
||||
//private CacheEntry cacheEntry;
|
||||
|
@ -51,7 +52,12 @@ public final class EntityIdentityInsertAction extends EntityAction {
|
|||
EntityPersister persister,
|
||||
SessionImplementor session,
|
||||
boolean isDelayed) throws HibernateException {
|
||||
super( session, null, instance, persister );
|
||||
super(
|
||||
session,
|
||||
( isDelayed ? generateDelayedPostInsertIdentifier() : null ),
|
||||
instance,
|
||||
persister
|
||||
);
|
||||
this.state = state;
|
||||
this.isDelayed = isDelayed;
|
||||
this.delayedEntityKey = isDelayed ? generateDelayedEntityKey() : null;
|
||||
|
@ -171,10 +177,24 @@ public final class EntityIdentityInsertAction extends EntityAction {
|
|||
return delayedEntityKey;
|
||||
}
|
||||
|
||||
private synchronized EntityKey generateDelayedEntityKey() {
|
||||
private synchronized static DelayedPostInsertIdentifier generateDelayedPostInsertIdentifier() {
|
||||
return new DelayedPostInsertIdentifier();
|
||||
}
|
||||
|
||||
private EntityKey generateDelayedEntityKey() {
|
||||
if ( !isDelayed ) {
|
||||
throw new AssertionFailure( "cannot request delayed entity-key for non-delayed post-insert-id generation" );
|
||||
}
|
||||
return new EntityKey( new DelayedPostInsertIdentifier(), getPersister(), getSession().getEntityMode() );
|
||||
return new EntityKey( getDelayedId(), getPersister(), getSession().getEntityMode() );
|
||||
}
|
||||
|
||||
public void afterDeserialize(SessionImplementor session) {
|
||||
super.afterDeserialize( session );
|
||||
// IMPL NOTE: non-flushed changes code calls this method with session == null...
|
||||
// guard against NullPointerException
|
||||
if ( session != null ) {
|
||||
EntityEntry entityEntry = session.getPersistenceContext().getEntry( getInstance() );
|
||||
this.state = entityEntry.getLoadedState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,9 +42,11 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.action.AfterTransactionCompletionProcess;
|
||||
import org.hibernate.action.BeforeTransactionCompletionProcess;
|
||||
import org.hibernate.action.BulkOperationCleanupAction;
|
||||
import org.hibernate.action.CollectionAction;
|
||||
import org.hibernate.action.CollectionRecreateAction;
|
||||
import org.hibernate.action.CollectionRemoveAction;
|
||||
import org.hibernate.action.CollectionUpdateAction;
|
||||
import org.hibernate.action.EntityAction;
|
||||
import org.hibernate.action.EntityDeleteAction;
|
||||
import org.hibernate.action.EntityIdentityInsertAction;
|
||||
import org.hibernate.action.EntityInsertAction;
|
||||
|
@ -480,42 +482,54 @@ public class ActionQueue {
|
|||
log.trace( "starting deserialization of [" + queueSize + "] insertions entries" );
|
||||
rtn.insertions = new ArrayList<Executable>( queueSize );
|
||||
for ( int i = 0; i < queueSize; i++ ) {
|
||||
rtn.insertions.add( ois.readObject() );
|
||||
EntityAction action = ( EntityAction ) ois.readObject();
|
||||
action.afterDeserialize( session );
|
||||
rtn.insertions.add( action );
|
||||
}
|
||||
|
||||
queueSize = ois.readInt();
|
||||
log.trace( "starting deserialization of [" + queueSize + "] deletions entries" );
|
||||
rtn.deletions = new ArrayList<Executable>( queueSize );
|
||||
for ( int i = 0; i < queueSize; i++ ) {
|
||||
rtn.deletions.add( ois.readObject() );
|
||||
EntityAction action = ( EntityAction ) ois.readObject();
|
||||
action.afterDeserialize( session );
|
||||
rtn.deletions.add( action );
|
||||
}
|
||||
|
||||
queueSize = ois.readInt();
|
||||
log.trace( "starting deserialization of [" + queueSize + "] updates entries" );
|
||||
rtn.updates = new ArrayList<Executable>( queueSize );
|
||||
for ( int i = 0; i < queueSize; i++ ) {
|
||||
rtn.updates.add( ois.readObject() );
|
||||
EntityAction action = ( EntityAction ) ois.readObject();
|
||||
action.afterDeserialize( session );
|
||||
rtn.updates.add( action );
|
||||
}
|
||||
|
||||
queueSize = ois.readInt();
|
||||
log.trace( "starting deserialization of [" + queueSize + "] collectionUpdates entries" );
|
||||
rtn.collectionUpdates = new ArrayList<Executable>( queueSize );
|
||||
for ( int i = 0; i < queueSize; i++ ) {
|
||||
rtn.collectionUpdates.add( ois.readObject() );
|
||||
CollectionAction action = ( CollectionAction ) ois.readObject();
|
||||
action.afterDeserialize( session );
|
||||
rtn.collectionUpdates.add( action );
|
||||
}
|
||||
|
||||
queueSize = ois.readInt();
|
||||
log.trace( "starting deserialization of [" + queueSize + "] collectionRemovals entries" );
|
||||
rtn.collectionRemovals = new ArrayList<Executable>( queueSize );
|
||||
for ( int i = 0; i < queueSize; i++ ) {
|
||||
rtn.collectionRemovals.add( ois.readObject() );
|
||||
CollectionAction action = ( CollectionAction ) ois.readObject();
|
||||
action.afterDeserialize( session );
|
||||
rtn.collectionRemovals.add( action );
|
||||
}
|
||||
|
||||
queueSize = ois.readInt();
|
||||
log.trace( "starting deserialization of [" + queueSize + "] collectionCreations entries" );
|
||||
rtn.collectionCreations = new ArrayList<Executable>( queueSize );
|
||||
for ( int i = 0; i < queueSize; i++ ) {
|
||||
rtn.collectionCreations.add( ois.readObject() );
|
||||
CollectionAction action = ( CollectionAction ) ois.readObject();
|
||||
action.afterDeserialize( session );
|
||||
rtn.collectionCreations.add( action );
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.Collection;
|
|||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.PersistentObjectException;
|
||||
import org.hibernate.Session;
|
||||
|
@ -104,6 +105,7 @@ public class CreateTest extends AbstractOperationTestCase {
|
|||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
|
@ -178,6 +180,7 @@ public class CreateTest extends AbstractOperationTestCase {
|
|||
dupe = ( NumberedNode ) getOldToNewEntityRefMap().get( dupe );
|
||||
s.persist( dupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
dupe = ( NumberedNode ) getOldToNewEntityRefMap().get( dupe );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
|
|
Loading…
Reference in New Issue