|
From: <hib...@li...> - 2006-07-21 16:54:29
|
Author: ste...@jb...
Date: 2006-07-21 12:53:54 -0400 (Fri, 21 Jul 2006)
New Revision: 10132
Added:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/TimestampedEntity.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
Log:
port fix for HHH-1927 to 3.2 branch
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java 2006-07-21 16:52:30 UTC (rev 10131)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/event/def/DefaultMergeEventListener.java 2006-07-21 16:53:54 UTC (rev 10132)
@@ -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,14 +87,33 @@
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:
@@ -263,13 +284,50 @@
}
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,
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java 2006-07-21 16:52:30 UTC (rev 10131)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java 2006-07-21 16:53:54 UTC (rev 10132)
@@ -9,6 +9,7 @@
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
+import org.hibernate.NonUniqueObjectException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.criterion.Projections;
@@ -22,6 +23,58 @@
public MergeTest(String str) {
super(str);
}
+
+ public void testPersistThenMergeInSameTxnWithVersion() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ VersionedEntity entity = new VersionedEntity( "test", "test" );
+ s.persist( entity );
+ s.merge( new VersionedEntity( "test", "test-2" ) );
+
+ try {
+ // control operation...
+ s.saveOrUpdate( new VersionedEntity( "test", "test-3" ) );
+ fail( "saveOrUpdate() should fail here" );
+ }
+ catch( NonUniqueObjectException expected ) {
+ // expected behavior
+ }
+
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete( entity );
+ tx.commit();
+ s.close();
+ }
+
+ public void testPersistThenMergeInSameTxnWithTimestamp() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ TimestampedEntity entity = new TimestampedEntity( "test", "test" );
+ s.persist( entity );
+ s.merge( new TimestampedEntity( "test", "test-2" ) );
+
+ try {
+ // control operation...
+ s.saveOrUpdate( new TimestampedEntity( "test", "test-3" ) );
+ fail( "saveOrUpdate() should fail here" );
+ }
+ catch( NonUniqueObjectException expected ) {
+ // expected behavior
+ }
+
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete( entity );
+ tx.commit();
+ s.close();
+ }
public void testMergeDeepTree() {
@@ -330,7 +383,7 @@
}
protected String[] getMappings() {
- return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml" };
+ return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml" };
}
public static Test suite() {
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml 2006-07-21 16:52:30 UTC (rev 10131)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml 2006-07-21 16:53:54 UTC (rev 10132)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="VersionedEntity" table="V_ENTITY">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <version name="version" column="VERS" type="long" />
+ <property name="name" column="NAME" type="string" />
+ </class>
+
+ <class name="TimestampedEntity" table="T_ENTITY">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <timestamp name="timestamp" column="TS" />
+ <property name="name" column="NAME" type="string" />
+ </class>
+
+</hibernate-mapping>
+
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/TimestampedEntity.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/TimestampedEntity.java 2006-07-21 16:52:30 UTC (rev 10131)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/TimestampedEntity.java 2006-07-21 16:53:54 UTC (rev 10132)
@@ -0,0 +1,47 @@
+package org.hibernate.test.ops;
+
+import java.util.Date;
+
+/**
+ * todo: describe TimestampedEntity
+ *
+ * @author Steve Ebersole
+ */
+public class TimestampedEntity {
+ private String id;
+ private String name;
+ private Date timestamp;
+
+ public TimestampedEntity() {
+ }
+
+ public TimestampedEntity(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+}
+
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java 2006-07-21 16:52:30 UTC (rev 10131)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java 2006-07-21 16:53:54 UTC (rev 10132)
@@ -0,0 +1,44 @@
+package org.hibernate.test.ops;
+
+/**
+ * todo: describe VersionedEntity
+ *
+ * @author Steve Ebersole
+ */
+public class VersionedEntity {
+ private String id;
+ private String name;
+ private long version;
+
+ public VersionedEntity() {
+ }
+
+ public VersionedEntity(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+}
|