|
From: <hib...@li...> - 2006-08-05 04:28:26
|
Author: ste...@jb...
Date: 2006-08-05 00:28:22 -0400 (Sat, 05 Aug 2006)
New Revision: 10227
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/Delete.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/Document.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/OptimisticLockTest.java
Log:
HHH-1677 : optimistic-lock and delete
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java 2006-08-05 04:27:55 UTC (rev 10226)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java 2006-08-05 04:28:22 UTC (rev 10227)
@@ -43,6 +43,7 @@
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.Versioning;
import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.engine.EntityKey;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
@@ -1269,7 +1270,7 @@
lockers.put( LockMode.READ, generateLocker( LockMode.READ ) );
lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) );
lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) );
- lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
+ //lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
}
protected LockingStrategy generateLocker(LockMode lockMode) {
@@ -1757,9 +1758,9 @@
getPropertyUpdateability() : //optimistic-lock="all", include all updatable properties
includeProperty; //optimistic-lock="dirty", include all properties we are updating this time
+ boolean[] versionability = getPropertyVersionability();
+ Type[] types = getPropertyTypes();
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
- boolean[] versionability = getPropertyVersionability();
- Type[] types = getPropertyTypes();
boolean include = includeInWhere[i] &&
isPropertyOfTable( i, j ) &&
versionability[i];
@@ -2233,7 +2234,7 @@
else if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
//if all fields are null, we might need to delete existing row
isRowToUpdate = true;
- delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session );
+ delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session, null );
}
else {
//there is probably a row there, so try to update
@@ -2370,11 +2371,12 @@
*/
protected void delete(
final Serializable id,
- final Object version,
- final int j,
- final Object object,
- final String sql,
- final SessionImplementor session) throws HibernateException {
+ final Object version,
+ final int j,
+ final Object object,
+ final String sql,
+ final SessionImplementor session,
+ final Object[] loadedState) throws HibernateException {
if ( isInverseTable( j ) ) {
return;
@@ -2428,12 +2430,26 @@
// Do the key. The key is immutable so we can use the _current_ object state - not necessarily
// the state at the time the delete was issued
getIdentifierType().nullSafeSet( delete, id, index, session );
+ index += getIdentifierColumnSpan();
// We should use the _current_ object state (ie. after any updates that occurred during flush)
if ( useVersion ) {
- getVersionType().nullSafeSet( delete, version, getIdentifierColumnSpan() + index, session );
+ getVersionType().nullSafeSet( delete, version, index, session );
}
+ else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && loadedState != null ) {
+ boolean[] versionability = getPropertyVersionability();
+ Type[] types = getPropertyTypes();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( isPropertyOfTable( i, j ) && versionability[i] ) {
+ // this property belongs to the table and it is not specifically
+ // excluded from optimistic locking by optimistic-lock="false"
+ boolean[] settable = types[i].toColumnNullness( loadedState[i], getFactory() );
+ types[i].nullSafeSet( delete, loadedState[i], index, settable, session );
+ index += ArrayHelper.countTrue( settable );
+ }
+ }
+ }
if ( useBatch ) {
session.getBatcher().addToBatch( expectation );
@@ -2586,14 +2602,72 @@
*/
public void delete(Serializable id, Object version, Object object, SessionImplementor session)
throws HibernateException {
+ final int span = getTableSpan();
+ boolean isImpliedOptimisticLocking = entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION;
+ Object[] loadedState = null;
+ if ( isImpliedOptimisticLocking ) {
+ // need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense);
+ // first we need to locate the "loaded" state
+ //
+ // Note, it potentially could be a proxy, so perform the location the safe way...
+ EntityKey key = new EntityKey( id, this, session.getEntityMode() );
+ Object entity = session.getPersistenceContext().getEntity( key );
+ if ( entity != null ) {
+ EntityEntry entry = session.getPersistenceContext().getEntry( entity );
+ loadedState = entry.getLoadedState();
+ }
+ }
- final int span = getTableSpan();
+ final String[] deleteStrings;
+ if ( isImpliedOptimisticLocking && loadedState != null ) {
+ // we need to utilize dynamic delete statements
+ deleteStrings = generateSQLDeletStrings( loadedState );
+ }
+ else {
+ // otherwise, utilize the static delete statements
+ deleteStrings = getSQLDeleteStrings();
+ }
+
for ( int j = span - 1; j >= 0; j-- ) {
- delete( id, version, j, object, getSQLDeleteStrings()[j], session );
+ delete( id, version, j, object, deleteStrings[j], session, loadedState );
}
}
+ private String[] generateSQLDeletStrings(Object[] loadedState) {
+ int span = getTableSpan();
+ String[] deleteStrings = new String[span];
+ for ( int j = span - 1; j >= 0; j-- ) {
+ Delete delete = new Delete()
+ .setTableName( getTableName( j ) )
+ .setPrimaryKeyColumnNames( getKeyColumns( j ) );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ delete.setComment( "delete " + getEntityName() + " [" + j + "]" );
+ }
+
+ boolean[] versionability = getPropertyVersionability();
+ Type[] types = getPropertyTypes();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( isPropertyOfTable( i, j ) && versionability[i] ) {
+ // this property belongs to the table and it is not specifically
+ // excluded from optimistic locking by optimistic-lock="false"
+ String[] propertyColumnNames = getPropertyColumnNames( i );
+ boolean[] propertyNullness = types[i].toColumnNullness( loadedState[i], getFactory() );
+ for ( int k = 0; k < propertyNullness.length; k++ ) {
+ if ( propertyNullness[k] ) {
+ delete.addWhereFragment( propertyColumnNames[k] + " = ?" );
+ }
+ else {
+ delete.addWhereFragment( propertyColumnNames[k] + " is null" );
+ }
+ }
+ }
+ }
+ deleteStrings[j] = delete.toStatementString();
+ }
+ return deleteStrings;
+ }
+
protected void logStaticSQL() {
if ( log.isDebugEnabled() ) {
log.debug( "Static SQL for entity: " + getEntityName() );
@@ -3596,11 +3670,11 @@
public Type getIdentifierType() {
return entityMetamodel.getIdentifierProperty().getType();
}
-
+
public boolean hasSubselectLoadableCollections() {
return hasSubselectLoadableCollections;
}
-
+
public int[] getNaturalIdentifierProperties() {
return entityMetamodel.getNaturalIdentifierProperties();
}
@@ -3691,7 +3765,7 @@
public boolean hasNaturalIdentifier() {
return entityMetamodel.hasNaturalIdentifier();
}
-
+
public void setPropertyValue(Object object, String propertyName, Object value, EntityMode entityMode)
throws HibernateException {
getTuplizer( entityMode ).setPropertyValue( object, propertyName, value );
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/Delete.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/Delete.java 2006-08-05 04:27:55 UTC (rev 10226)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/Delete.java 2006-08-05 04:28:22 UTC (rev 10227)
@@ -61,6 +61,16 @@
return this;
}
+ public Delete addWhereFragment(String fragment) {
+ if ( where == null ) {
+ where = fragment;
+ }
+ else {
+ where += ( " and " + fragment );
+ }
+ return this;
+ }
+
public Delete setPrimaryKeyColumnNames(String[] primaryKeyColumnNames) {
this.primaryKeyColumnNames = primaryKeyColumnNames;
return this;
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/Document.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/Document.hbm.xml 2006-08-05 04:27:55 UTC (rev 10226)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/Document.hbm.xml 2006-08-05 04:28:22 UTC (rev 10227)
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
-<!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
@@ -12,44 +12,44 @@
-->
<hibernate-mapping package="org.hibernate.test.optlock">
-
- <class name="Document"
- entity-name="Dirty"
- table="Document"
- optimistic-lock="dirty"
- dynamic-update="true">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="title"/>
- <property name="author"/>
- <component name="pubDate">
- <property name="year" not-null="true"/>
- <property name="month"/>
- </component>
- <property name="summary"/>
- <property name="totalSales" optimistic-lock="false"/>
- <property name="text" column="`text`"/>
- </class>
-
- <class name="Document"
- entity-name="All"
- table="Document"
- optimistic-lock="all"
- dynamic-update="true">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="title"/>
- <property name="author"/>
- <component name="pubDate">
- <property name="year" not-null="true"/>
- <property name="month"/>
- </component>
- <property name="summary"/>
- <property name="totalSales" optimistic-lock="false"/>
- <property name="text" column="`text`"/>
- </class>
-
+
+ <class name="Document"
+ entity-name="Dirty"
+ table="Document"
+ optimistic-lock="dirty"
+ dynamic-update="true">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="title"/>
+ <property name="author"/>
+ <component name="pubDate">
+ <property name="year" not-null="true"/>
+ <property name="month"/>
+ </component>
+ <property name="summary"/>
+ <property name="totalSales" optimistic-lock="false"/>
+ <property name="text" column="`text`"/>
+ </class>
+
+ <class name="Document"
+ entity-name="All"
+ table="Document"
+ optimistic-lock="all"
+ dynamic-update="true">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="title"/>
+ <property name="author"/>
+ <component name="pubDate">
+ <property name="year" not-null="true"/>
+ <property name="month"/>
+ </component>
+ <property name="summary"/>
+ <property name="totalSales" optimistic-lock="false"/>
+ <property name="text" column="`text`"/>
+ </class>
+
</hibernate-mapping>
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/OptimisticLockTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/OptimisticLockTest.java 2006-08-05 04:27:55 UTC (rev 10226)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/optlock/OptimisticLockTest.java 2006-08-05 04:28:22 UTC (rev 10227)
@@ -10,146 +10,134 @@
import org.hibernate.test.TestCase;
/**
+ * Tests relating to the optimisitc-lock mapping option.
+ *
* @author Gavin King
+ * @author Steve Ebersole
*/
public class OptimisticLockTest extends TestCase {
-
+
public OptimisticLockTest(String str) {
super(str);
}
-
+
+ protected String[] getMappings() {
+ return new String[] { "optlock/Document.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new TestSuite(OptimisticLockTest.class);
+ }
+
public void testOptimisticLockDirty() {
- Session s = openSession();
- Transaction t = s.beginTransaction();
- Document doc = new Document();
- doc.setTitle("Hibernate in Action");
- doc.setAuthor("Bauer et al");
- doc.setSummary("Very boring book about persistence");
- doc.setText("blah blah yada yada yada");
- doc.setPubDate( new PublicationDate(2004) );
- s.save("Dirty", doc);
- s.flush();
- doc.setSummary("A modern classic");
- s.flush();
- doc.getPubDate().setMonth( new Integer(3) );
- s.flush();
- s.delete(doc);
- t.commit();
- s.close();
+ testUpdateOptimisticLockFailure( "Dirty" );
}
public void testOptimisticLockAll() {
- Session s = openSession();
- Transaction t = s.beginTransaction();
- Document doc = new Document();
- doc.setTitle("Hibernate in Action");
- doc.setAuthor("Bauer et al");
- doc.setSummary("Very boring book about persistence");
- doc.setText("blah blah yada yada yada");
- doc.setPubDate( new PublicationDate(2004) );
- s.save("All", doc);
- s.flush();
- doc.setSummary("A modern classic");
- s.flush();
- doc.getPubDate().setMonth( new Integer(3) );
- s.flush();
- s.delete(doc);
- t.commit();
- s.close();
+ testUpdateOptimisticLockFailure( "All" );
}
- //TODO: also test that non-overlapping changes behavior.
- public void testOptimisticLockDirtyDeleteFailureExpected() {
- //HHH-1677
+ public void testOptimisticLockDirtyDelete() {
+ testDeleteOptimisticLockFailure( "Dirty" );
+ }
+ public void testOptimisticLockAllDelete() {
+ testDeleteOptimisticLockFailure( "All" );
+ }
+
+ private void testUpdateOptimisticLockFailure(String entityName) {
Session s = openSession();
Transaction t = s.beginTransaction();
Document doc = new Document();
- doc.setTitle("Hibernate in Action");
- doc.setAuthor("Bauer et al");
- doc.setSummary("Very boring book about persistence");
- doc.setText("blah blah yada yada yada");
- doc.setPubDate( new PublicationDate(2004) );
- s.save("Dirty", doc);
- s.flush();
- doc.setSummary("A modern classic");
- s.flush();
- doc.getPubDate().setMonth( new Integer(3) );
- s.flush();
+ doc.setTitle( "Hibernate in Action" );
+ doc.setAuthor( "Bauer et al" );
+ doc.setSummary( "Very boring book about persistence" );
+ doc.setText( "blah blah yada yada yada" );
+ doc.setPubDate( new PublicationDate( 2004 ) );
+ s.save( entityName, doc );
t.commit();
s.close();
-
+
s = openSession();
t = s.beginTransaction();
- doc = (Document) s.get("Dirty", doc.getId());
-
- Session other = openSession();
- Transaction othert = other.beginTransaction();
- Document otherDoc = (Document) other.get("Dirty", doc.getId());
- otherDoc.setSummary( "my other summary" );
- other.flush();
- othert.commit();
- other.close();
-
+ doc = ( Document ) s.get( entityName, doc.getId() );
+
+ Session otherSession = openSession();
+ otherSession.beginTransaction();
+ Document otherDoc = ( Document ) otherSession.get( entityName, doc.getId() );
+ otherDoc.setSummary( "A modern classic" );
+ otherSession.getTransaction().commit();
+ otherSession.close();
+
try {
- s.delete(doc);
- t.commit();
- fail("Should fail since other session have update the summary");
- } catch(StaleObjectStateException soe) {
- // expected
+ doc.setSummary( "A machiavelian achievement of epic proportions" );
+ s.flush();
+ fail( "expecting opt lock failure" );
}
+ catch ( StaleObjectStateException expected ) {
+ // expected result...
+ }
+ s.clear();
+ t.commit();
s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ doc = ( Document ) s.load( entityName, doc.getId() );
+ s.delete( entityName, doc );
+ t.commit();
+ s.close();
}
- public void testOptimisticLockAllDeleteFailureExpected() {
- //HHH-1677
+ private void testDeleteOptimisticLockFailure(String entityName) {
Session s = openSession();
Transaction t = s.beginTransaction();
Document doc = new Document();
- doc.setTitle("Hibernate in Action");
- doc.setAuthor("Bauer et al");
- doc.setSummary("Very boring book about persistence");
- doc.setText("blah blah yada yada yada");
- doc.setPubDate( new PublicationDate(2004) );
- s.save("Dirty", doc);
+ doc.setTitle( "Hibernate in Action" );
+ doc.setAuthor( "Bauer et al" );
+ doc.setSummary( "Very boring book about persistence" );
+ doc.setText( "blah blah yada yada yada" );
+ doc.setPubDate( new PublicationDate( 2004 ) );
+ s.save( entityName, doc );
s.flush();
- doc.setSummary("A modern classic");
+ doc.setSummary( "A modern classic" );
s.flush();
- doc.getPubDate().setMonth( new Integer(3) );
+ doc.getPubDate().setMonth( new Integer( 3 ) );
s.flush();
t.commit();
s.close();
s = openSession();
t = s.beginTransaction();
- doc = (Document) s.get("All", doc.getId());
+ doc = ( Document ) s.get( entityName, doc.getId() );
Session other = openSession();
Transaction othert = other.beginTransaction();
- Document otherDoc = (Document) other.get("All", doc.getId());
+ Document otherDoc = ( Document ) other.get( entityName, doc.getId() );
otherDoc.setSummary( "my other summary" );
other.flush();
othert.commit();
other.close();
try {
- s.delete(doc);
- t.commit();
- fail("Should fail since other session have update the summary");
- } catch(StaleObjectStateException soe) {
+ s.delete( doc );
+ s.flush();
+ fail( "expecting opt lock failure" );
+ }
+ catch ( StaleObjectStateException e ) {
// expected
}
- s.close();
- }
+ s.clear();
+ t.commit();
+ s.close();
-
- protected String[] getMappings() {
- return new String[] { "optlock/Document.hbm.xml" };
+ s = openSession();
+ t = s.beginTransaction();
+ doc = ( Document ) s.load( entityName, doc.getId() );
+ s.delete( entityName, doc );
+ t.commit();
+ s.close();
}
- public static Test suite() {
- return new TestSuite(OptimisticLockTest.class);
- }
-
}
|