From: <hib...@li...> - 2006-07-21 16:53:05
|
Author: ste...@jb... Date: 2006-07-21 12:52:30 -0400 (Fri, 21 Jul 2006) New Revision: 10131 Modified: trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java trunk/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java Log: different approach to HHH-1927 Modified: trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java 2006-07-21 07:10:41 UTC (rev 10130) +++ trunk/Hibernate3/src/org/hibernate/event/def/AbstractSaveEventListener.java 2006-07-21 16:52:30 UTC (rev 10131) @@ -453,24 +453,6 @@ EntityEntry entry, //pass this as an argument only to avoid double looking SessionImplementor source) { - if ( entry == null && performDeepStateChecking() ) { - // check to handle fringe case of merging the same "entity data" via - // a transient instance within the same transaction it was saved; i.e.: - // Session s = ...; - // s.beginTransaction(); - // s.persist( new Entity( "someid" ) ); - // s.merge( new Entity( "someid" ) ); - // s.getTransaction().commit(); - EntityPersister persister = source.getEntityPersister( entityName, entity ); - Serializable id = persister.getIdentifier( entity, source.getEntityMode() ); - if ( id != null ) { - EntityKey key = new EntityKey( id, persister, source.getEntityMode() ); - entry = source.getPersistenceContext().getEntry( - source.getPersistenceContext().getEntity( key ) - ); - } - } - if ( entry != null ) { // the object is persistent as it has an entry associated with the // session; so check its status @@ -529,8 +511,4 @@ protected Boolean getAssumedUnsaved() { return null; } - - protected boolean performDeepStateChecking() { - return false; - } } Modified: trunk/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java 2006-07-21 07:10:41 UTC (rev 10130) +++ trunk/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java 2006-07-21 16:52:30 UTC (rev 10131) @@ -17,6 +17,8 @@ import org.hibernate.event.MergeEvent; import org.hibernate.event.MergeEventListener; import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.EntityEntry; +import org.hibernate.engine.EntityKey; import org.hibernate.intercept.FieldInterceptionHelper; import org.hibernate.intercept.FieldInterceptor; import org.hibernate.persister.entity.EntityPersister; @@ -85,15 +87,36 @@ event.setResult(entity); } else { - event.setEntity(entity); - - int entityState = getEntityState( - entity, - event.getEntityName(), - source.getPersistenceContext().getEntry(entity), - source - ); - + + event.setEntity( entity ); + + int entityState = -1; + + // Check the persistence context for an entry relating to this + // entity to be merged... + EntityEntry entry = source.getPersistenceContext().getEntry( entity ); + if ( entry == null ) { + EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity ); + Serializable id = persister.getIdentifier( entity, source.getEntityMode() ); + if ( id != null ) { + EntityKey key = new EntityKey( id, persister, source.getEntityMode() ); + Object managedEntity = source.getPersistenceContext().getEntity( key ); + entry = source.getPersistenceContext().getEntry( managedEntity ); + if ( entry != null ) { + // we have specialized case of a detached entity from the + // perspective of the merge operation. Specifically, we + // have an incoming entity instance which has a corresponding + // entry in the current persistence context, but registered + // under a different entity instance + entityState = DETACHED; + } + } + } + + if ( entityState == -1 ) { + entityState = getEntityState( entity, event.getEntityName(), entry, source ); + } + switch (entityState) { case DETACHED: entityIsDetached(event, copyCache); @@ -117,10 +140,6 @@ } - protected boolean performDeepStateChecking() { - return true; - } - protected void entityIsPersistent(MergeEvent event, Map copyCache) { log.trace("ignoring persistent instance"); @@ -151,7 +170,7 @@ final Serializable id = persister.hasIdentifierProperty() ? persister.getIdentifier( entity, source.getEntityMode() ) : null; - + final Object copy = persister.instantiate( id, source.getEntityMode() ); //TODO: should this be Session.instantiate(Persister, ...)? copyCache.put(entity, copy); //before cascade! @@ -267,14 +286,51 @@ } private boolean isVersionChanged(Object entity, EventSource source, EntityPersister persister, Object target) { - return persister.isVersioned() && - !persister.getVersionType().isSame( - persister.getVersion( target, source.getEntityMode() ), - persister.getVersion( entity, source.getEntityMode() ), - source.getEntityMode() - ); + if ( ! persister.isVersioned() ) { + return false; + } + // for merging of versioned entities, we consider the version having + // been changed only when: + // 1) the two version values are different; + // *AND* + // 2) The target actually represents database state! + // + // This second condition is a special case which allows + // an entity to be merged during the same transaction + // (though during a seperate operation) in which it was + // originally persisted/saved + boolean changed = persister.getVersionType().isSame( + persister.getVersion( target, source.getEntityMode() ), + persister.getVersion( entity, source.getEntityMode() ), + source.getEntityMode() + ); + + // TODO : perhaps we should additionally require that the incoming entity + // version be equivalent to the defined unsaved-value? + return changed && existsInDatabase( target, source, persister ); } - + + private boolean existsInDatabase(Object entity, EventSource source, EntityPersister persister) { + EntityEntry entry = source.getPersistenceContext().getEntry( entity ); + if ( entry == null ) { + Serializable id = persister.getIdentifier( entity, source.getEntityMode() ); + if ( id != null ) { + EntityKey key = new EntityKey( id, persister, source.getEntityMode() ); + Object managedEntity = source.getPersistenceContext().getEntity( key ); + entry = source.getPersistenceContext().getEntry( managedEntity ); + } + } + + if ( entry == null ) { + // perhaps this should be an exception since it is only ever used + // in the above method? + return false; + } + else { + return entry.isExistsInDatabase(); + } + } + protected void copyValues( final EntityPersister persister, final Object entity, |