[hibernate-commits] Hibernate SVN: r19095 - in core/trunk: core/src/main/java/org/hibernate/dialect/lock and 7 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Mar 24 10:37:59 EDT 2010


Author: steve.ebersole at jboss.com
Date: 2010-03-24 10:37:56 -0400 (Wed, 24 Mar 2010)
New Revision: 19095

Added:
   core/trunk/core/src/main/java/org/hibernate/dialect/lock/AbstractSelectLockingStrategy.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/dialect/Dialect.java
   core/trunk/core/src/main/java/org/hibernate/dialect/HSQLDialect.java
   core/trunk/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java
   core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java
   core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteSelectLockingStrategy.java
   core/trunk/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java
   core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java
   core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java
   core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
   core/trunk/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java
   core/trunk/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java
   core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java
   core/trunk/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
   core/trunk/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java
   core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java
Log:
HHH-5009 - Support NOWAIT for "SELECT ... FOR UPDATE" with JPA interfaces


Modified: core/trunk/core/src/main/java/org/hibernate/dialect/Dialect.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/Dialect.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/Dialect.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -1,10 +1,10 @@
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, 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 Middleware LLC.
+ * 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
@@ -20,7 +20,6 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
- *
  */
 package org.hibernate.dialect;
 
@@ -943,6 +942,34 @@
 	// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 	/**
+	 * Informational metadata about whether this dialect is known to support
+	 * specifying timeouts for requested lock acquisitions.
+	 *
+	 * @return True is this dialect supports specifying lock timeouts.
+	 */
+	public boolean supportsLockTimeouts() {
+		return true;
+
+	}
+
+	/**
+	 * If this dialect supports specifying lock timeouts, are those timeouts
+	 * rendered into the <tt>SQL</tt> string as parameters.  The implication
+	 * is that Hibernate will need to bind the timeout value as a parameter
+	 * in the {@link java.sql.PreparedStatement}.  If true, the param position
+	 * is always handled as the last parameter; if the dialect specifies the
+	 * lock timeout elsewhere in the <tt>SQL</tt> statement then the timeout
+	 * value should be directly rendered into the statement and this method
+	 * should return false.
+	 *
+	 * @return True if the lock timeout is rendered into the <tt>SQL</tt>
+	 * string as a parameter; false otherwise.
+	 */
+	public boolean isLockTimeoutParameterized() {
+		return false;
+	}
+
+	/**
 	 * Get a strategy instance which knows how to acquire a database-level lock
 	 * of the specified mode for this dialect.
 	 *

Modified: core/trunk/core/src/main/java/org/hibernate/dialect/HSQLDialect.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/HSQLDialect.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/HSQLDialect.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -173,6 +173,10 @@
 		return "null";
 	}
 
+	public boolean supportsLockTimeouts() {
+		return false;
+	}
+
 	public String getForUpdateString() {
 		return "";
 	}

Added: core/trunk/core/src/main/java/org/hibernate/dialect/lock/AbstractSelectLockingStrategy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/lock/AbstractSelectLockingStrategy.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/lock/AbstractSelectLockingStrategy.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -0,0 +1,73 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, 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.dialect.lock;
+
+import org.hibernate.LockMode;
+import org.hibernate.LockOptions;
+import org.hibernate.persister.entity.Lockable;
+
+/**
+ * Base {@link LockingStrategy} implementation to support implementations
+ * based on issuing <tt>SQL</tt> <tt>SELECT</tt> statements
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractSelectLockingStrategy implements LockingStrategy {
+	private final Lockable lockable;
+	private final LockMode lockMode;
+	private final String waitForeverSql;
+
+	protected AbstractSelectLockingStrategy(Lockable lockable, LockMode lockMode) {
+		this.lockable = lockable;
+		this.lockMode = lockMode;
+		this.waitForeverSql = generateLockString( LockOptions.WAIT_FOREVER );
+	}
+
+	protected Lockable getLockable() {
+		return lockable;
+	}
+
+	protected LockMode getLockMode() {
+		return lockMode;
+	}
+
+	protected abstract String generateLockString(int lockTimeout);
+
+	protected String determineSql(int timeout) {
+		return timeout == LockOptions.WAIT_FOREVER
+				? waitForeverSql
+				: timeout == LockOptions.NO_WAIT
+						? getNoWaitSql()
+						: generateLockString( timeout );
+	}
+
+	private String noWaitSql;
+
+	public String getNoWaitSql() {
+		if ( noWaitSql == null ) {
+			noWaitSql = generateLockString( LockOptions.NO_WAIT );
+		}
+		return noWaitSql;
+	}
+}

Modified: core/trunk/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -1,10 +1,10 @@
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, 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 Middleware LLC.
+ * 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
@@ -20,7 +20,6 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
- *
  */
 package org.hibernate.dialect.lock;
 
@@ -33,7 +32,7 @@
 /**
  * A strategy abstraction for how locks are obtained in the underlying database.
  * <p/>
- * All locking provided implemenations assume the underlying database supports
+ * All locking provided implementations assume the underlying database supports
  * (and that the connection is in) at least read-committed transaction isolation.
  * The most glaring exclusion to this is HSQLDB which only offers support for
  * READ_UNCOMMITTED isolation.
@@ -53,9 +52,9 @@
 	 * @param object The object logically being locked (currently not used)
 	 * @param timeout timeout in milliseconds, 0 = no wait, -1 = wait indefinitely
 	 * @param session The session from which the lock request originated
-	 * @throws StaleObjectStateException Indicates an optimisitic lock failure
+	 * @throws StaleObjectStateException Indicates an optimistic lock failure
 	 * as part of acquiring the requested database lock.
-	 * @throws JDBCException
+	 * @throws JDBCException Indicates errors from the <tt>JDBC</tt> driver.
 	 */
 	public void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session)
 	throws StaleObjectStateException, JDBCException;

Modified: core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticReadSelectLockingStrategy.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -1,10 +1,10 @@
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, 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 Middleware LLC.
+ * 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
@@ -20,7 +20,6 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
- *
  */
 package org.hibernate.dialect.lock;
 
@@ -51,57 +50,43 @@
  *
  * This class is a clone of SelectLockingStrategy.
  *
+ * @author Steve Ebersole
+ * @author Scott Marlow
  * @see org.hibernate.dialect.Dialect#getForUpdateString(org.hibernate.LockMode)
  * @see org.hibernate.dialect.Dialect#appendLockHint(org.hibernate.LockMode, String)
  * @since 3.5
- *
- * @author Steve Ebersole
- * @author Scott Marlow
  */
-public class PessimisticReadSelectLockingStrategy implements LockingStrategy {
-
-	private final Lockable lockable;
-	private final LockMode lockMode;
-	private final String sql;
-
+public class PessimisticReadSelectLockingStrategy extends AbstractSelectLockingStrategy {
 	/**
 	 * Construct a locking strategy based on SQL SELECT statements.
 	 *
 	 * @param lockable The metadata for the entity to be locked.
-	 * @param lockMode Indictates the type of lock to be acquired.
+	 * @param lockMode Indicates the type of lock to be acquired.
 	 */
 	public PessimisticReadSelectLockingStrategy(Lockable lockable, LockMode lockMode) {
-		this.lockable = lockable;
-		this.lockMode = lockMode;
-		this.sql = generateLockString(LockOptions.WAIT_FOREVER);
+		super( lockable, lockMode );
 	}
 
-   /**
+	/**
 	 * @see org.hibernate.dialect.lock.LockingStrategy#lock
 	 */
 	public void lock(
-      Serializable id,
-      Object version,
-      Object object,
-      int timeout, SessionImplementor session) throws StaleObjectStateException, JDBCException {
-		String sql = this.sql;
-		if ( timeout == LockOptions.NO_WAIT ) {
-			sql = generateLockString( LockOptions.NO_WAIT );
-		}
-		else if ( timeout > 0) {
-			sql = generateLockString( timeout );
-		}
-
+			Serializable id,
+			Object version,
+			Object object,
+			int timeout,
+			SessionImplementor session) throws StaleObjectStateException, JDBCException {
+		final String sql = determineSql( timeout );
 		SessionFactoryImplementor factory = session.getFactory();
 		try {
 			PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
 			try {
-				lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
-				if ( lockable.isVersioned() ) {
-					lockable.getVersionType().nullSafeSet(
+				getLockable().getIdentifierType().nullSafeSet( st, id, 1, session );
+				if ( getLockable().isVersioned() ) {
+					getLockable().getVersionType().nullSafeSet(
 							st,
 							version,
-							lockable.getIdentifierType().getColumnSpan( factory ) + 1,
+							getLockable().getIdentifierType().getColumnSpan( factory ) + 1,
 							session
 					);
 				}
@@ -111,9 +96,9 @@
 					if ( !rs.next() ) {
 						if ( factory.getStatistics().isStatisticsEnabled() ) {
 							factory.getStatisticsImplementor()
-									.optimisticFailure( lockable.getEntityName() );
+									.optimisticFailure( getLockable().getEntityName() );
 						}
-						throw new StaleObjectStateException( lockable.getEntityName(), id );
+						throw new StaleObjectStateException( getLockable().getEntityName(), id );
 					}
 				}
 				finally {
@@ -129,31 +114,27 @@
 			JDBCException e = JDBCExceptionHelper.convert(
 					session.getFactory().getSQLExceptionConverter(),
 					sqle,
-					"could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
+					"could not lock: " + MessageHelper.infoString( getLockable(), id, session.getFactory() ),
 					sql
-				);
-			throw new PessimisticLockException("could not obtain pessimistic lock", e, object);
+			);
+			throw new PessimisticLockException( "could not obtain pessimistic lock", e, object );
 		}
 	}
 
-	protected LockMode getLockMode() {
-		return lockMode;
-	}
-
 	protected String generateLockString(int lockTimeout) {
-		SessionFactoryImplementor factory = lockable.getFactory();
-		LockOptions lockOptions = new LockOptions(this.lockMode);
+		SessionFactoryImplementor factory = getLockable().getFactory();
+		LockOptions lockOptions = new LockOptions( getLockMode() );
 		lockOptions.setTimeOut( lockTimeout );
 		SimpleSelect select = new SimpleSelect( factory.getDialect() )
 				.setLockOptions( lockOptions )
-				.setTableName( lockable.getRootTableName() )
-				.addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
-				.addCondition( lockable.getRootTableIdentifierColumnNames(), "=?" );
-		if ( lockable.isVersioned() ) {
-			select.addCondition( lockable.getVersionColumnName(), "=?" );
+				.setTableName( getLockable().getRootTableName() )
+				.addColumn( getLockable().getRootTableIdentifierColumnNames()[0] )
+				.addCondition( getLockable().getRootTableIdentifierColumnNames(), "=?" );
+		if ( getLockable().isVersioned() ) {
+			select.addCondition( getLockable().getVersionColumnName(), "=?" );
 		}
 		if ( factory.getSettings().isCommentsEnabled() ) {
-			select.setComment( lockMode + " lock " + lockable.getEntityName() );
+			select.setComment( getLockMode() + " lock " + getLockable().getEntityName() );
 		}
 		return select.toStatementString();
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteSelectLockingStrategy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteSelectLockingStrategy.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteSelectLockingStrategy.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -1,10 +1,10 @@
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, 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 Middleware LLC.
+ * 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
@@ -20,7 +20,6 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
- *
  */
 package org.hibernate.dialect.lock;
 
@@ -46,62 +45,48 @@
  * <p/>
  * For non-read locks, this is achieved through the Dialect's specific
  * SELECT ... FOR UPDATE syntax.
- * 
+ *
  * This strategy is valid for LockMode.PESSIMISTIC_WRITE
  *
  * This class is a clone of SelectLockingStrategy.
  *
+ * @author Steve Ebersole
+ * @author Scott Marlow
  * @see org.hibernate.dialect.Dialect#getForUpdateString(org.hibernate.LockMode)
  * @see org.hibernate.dialect.Dialect#appendLockHint(org.hibernate.LockMode, String)
  * @since 3.5
- *
- * @author Steve Ebersole
- * @author Scott Marlow
  */
-public class PessimisticWriteSelectLockingStrategy implements LockingStrategy {
-
-	private final Lockable lockable;
-	private final LockMode lockMode;
-	private final String sql;
-
+public class PessimisticWriteSelectLockingStrategy extends AbstractSelectLockingStrategy {
 	/**
 	 * Construct a locking strategy based on SQL SELECT statements.
 	 *
 	 * @param lockable The metadata for the entity to be locked.
-	 * @param lockMode Indictates the type of lock to be acquired.
+	 * @param lockMode Indicates the type of lock to be acquired.
 	 */
 	public PessimisticWriteSelectLockingStrategy(Lockable lockable, LockMode lockMode) {
-		this.lockable = lockable;
-		this.lockMode = lockMode;
-		this.sql = generateLockString(LockOptions.WAIT_FOREVER);
+		super( lockable, lockMode );
 	}
 
-   /**
+	/**
 	 * @see LockingStrategy#lock
 	 */
 	public void lock(
-      Serializable id,
-      Object version,
-      Object object,
-      int timeout, SessionImplementor session) throws StaleObjectStateException, JDBCException {
-		String sql = this.sql;
-		if ( timeout == LockOptions.NO_WAIT ) {
-			sql = generateLockString( LockOptions.NO_WAIT );
-		}
-		else if ( timeout > 0) {
-			sql = generateLockString( timeout );
-		}
-
+			Serializable id,
+			Object version,
+			Object object,
+			int timeout,
+			SessionImplementor session) throws StaleObjectStateException, JDBCException {
+		final String sql = determineSql( timeout );
 		SessionFactoryImplementor factory = session.getFactory();
 		try {
 			PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
 			try {
-				lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
-				if ( lockable.isVersioned() ) {
-					lockable.getVersionType().nullSafeSet(
+				getLockable().getIdentifierType().nullSafeSet( st, id, 1, session );
+				if ( getLockable().isVersioned() ) {
+					getLockable().getVersionType().nullSafeSet(
 							st,
 							version,
-							lockable.getIdentifierType().getColumnSpan( factory ) + 1,
+							getLockable().getIdentifierType().getColumnSpan( factory ) + 1,
 							session
 					);
 				}
@@ -111,9 +96,9 @@
 					if ( !rs.next() ) {
 						if ( factory.getStatistics().isStatisticsEnabled() ) {
 							factory.getStatisticsImplementor()
-									.optimisticFailure( lockable.getEntityName() );
+									.optimisticFailure( getLockable().getEntityName() );
 						}
-						throw new StaleObjectStateException( lockable.getEntityName(), id );
+						throw new StaleObjectStateException( getLockable().getEntityName(), id );
 					}
 				}
 				finally {
@@ -129,31 +114,27 @@
 			JDBCException e = JDBCExceptionHelper.convert(
 					session.getFactory().getSQLExceptionConverter(),
 					sqle,
-					"could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
+					"could not lock: " + MessageHelper.infoString( getLockable(), id, session.getFactory() ),
 					sql
-				);
-			throw new PessimisticLockException("could not obtain pessimistic lock", e, object);
+			);
+			throw new PessimisticLockException( "could not obtain pessimistic lock", e, object );
 		}
 	}
 
-	protected LockMode getLockMode() {
-		return lockMode;
-	}
-
 	protected String generateLockString(int lockTimeout) {
-		SessionFactoryImplementor factory = lockable.getFactory();
-		LockOptions lockOptions = new LockOptions(this.lockMode);
+		SessionFactoryImplementor factory = getLockable().getFactory();
+		LockOptions lockOptions = new LockOptions( getLockMode() );
 		lockOptions.setTimeOut( lockTimeout );
 		SimpleSelect select = new SimpleSelect( factory.getDialect() )
 				.setLockOptions( lockOptions )
-				.setTableName( lockable.getRootTableName() )
-				.addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
-				.addCondition( lockable.getRootTableIdentifierColumnNames(), "=?" );
-		if ( lockable.isVersioned() ) {
-			select.addCondition( lockable.getVersionColumnName(), "=?" );
+				.setTableName( getLockable().getRootTableName() )
+				.addColumn( getLockable().getRootTableIdentifierColumnNames()[0] )
+				.addCondition( getLockable().getRootTableIdentifierColumnNames(), "=?" );
+		if ( getLockable().isVersioned() ) {
+			select.addCondition( getLockable().getVersionColumnName(), "=?" );
 		}
 		if ( factory.getSettings().isCommentsEnabled() ) {
-			select.setComment( lockMode + " lock " + lockable.getEntityName() );
+			select.setComment( getLockMode() + " lock " + getLockable().getEntityName() );
 		}
 		return select.toStatementString();
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -1,10 +1,10 @@
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, 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 Middleware LLC.
+ * 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
@@ -20,10 +20,10 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
- *
  */
 package org.hibernate.dialect.lock;
 
+import org.hibernate.LockOptions;
 import org.hibernate.persister.entity.Lockable;
 import org.hibernate.engine.SessionImplementor;
 import org.hibernate.engine.SessionFactoryImplementor;
@@ -51,12 +51,7 @@
  *
  * @author Steve Ebersole
  */
-public class SelectLockingStrategy implements LockingStrategy {
-
-	private final Lockable lockable;
-	private final LockMode lockMode;
-	private final String sql;
-
+public class SelectLockingStrategy extends AbstractSelectLockingStrategy {
 	/**
 	 * Construct a locking strategy based on SQL SELECT statements.
 	 *
@@ -64,9 +59,7 @@
 	 * @param lockMode Indictates the type of lock to be acquired.
 	 */
 	public SelectLockingStrategy(Lockable lockable, LockMode lockMode) {
-		this.lockable = lockable;
-		this.lockMode = lockMode;
-		this.sql = generateLockString();
+		super( lockable, lockMode );
 	}
 
 	/**
@@ -78,17 +71,17 @@
 	        Object object,
 	        int timeout, 
 	        SessionImplementor session) throws StaleObjectStateException, JDBCException {
-
+		final String sql = determineSql( timeout );
 		SessionFactoryImplementor factory = session.getFactory();
 		try {
 			PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
 			try {
-				lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
-				if ( lockable.isVersioned() ) {
-					lockable.getVersionType().nullSafeSet(
+				getLockable().getIdentifierType().nullSafeSet( st, id, 1, session );
+				if ( getLockable().isVersioned() ) {
+					getLockable().getVersionType().nullSafeSet(
 							st,
 							version,
-							lockable.getIdentifierType().getColumnSpan( factory ) + 1,
+							getLockable().getIdentifierType().getColumnSpan( factory ) + 1,
 							session
 					);
 				}
@@ -98,9 +91,9 @@
 					if ( !rs.next() ) {
 						if ( factory.getStatistics().isStatisticsEnabled() ) {
 							factory.getStatisticsImplementor()
-									.optimisticFailure( lockable.getEntityName() );
+									.optimisticFailure( getLockable().getEntityName() );
 						}
-						throw new StaleObjectStateException( lockable.getEntityName(), id );
+						throw new StaleObjectStateException( getLockable().getEntityName(), id );
 					}
 				}
 				finally {
@@ -116,28 +109,26 @@
 			throw JDBCExceptionHelper.convert(
 					session.getFactory().getSQLExceptionConverter(),
 					sqle,
-					"could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
+					"could not lock: " + MessageHelper.infoString( getLockable(), id, session.getFactory() ),
 					sql
 				);
 		}
 	}
 
-	protected LockMode getLockMode() {
-		return lockMode;
-	}
-
-	protected String generateLockString() {
-		SessionFactoryImplementor factory = lockable.getFactory();
+	protected String generateLockString(int timeout) {
+		SessionFactoryImplementor factory = getLockable().getFactory();
+		LockOptions lockOptions = new LockOptions( getLockMode() );
+		lockOptions.setTimeOut( timeout );
 		SimpleSelect select = new SimpleSelect( factory.getDialect() )
-				.setLockMode( lockMode )
-				.setTableName( lockable.getRootTableName() )
-				.addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
-				.addCondition( lockable.getRootTableIdentifierColumnNames(), "=?" );
-		if ( lockable.isVersioned() ) {
-			select.addCondition( lockable.getVersionColumnName(), "=?" );
+				.setLockOptions( lockOptions )
+				.setTableName( getLockable().getRootTableName() )
+				.addColumn( getLockable().getRootTableIdentifierColumnNames()[0] )
+				.addCondition( getLockable().getRootTableIdentifierColumnNames(), "=?" );
+		if ( getLockable().isVersioned() ) {
+			select.addCondition( getLockable().getVersionColumnName(), "=?" );
 		}
 		if ( factory.getSettings().isCommentsEnabled() ) {
-			select.setComment( lockMode + " lock " + lockable.getEntityName() );
+			select.setComment( getLockMode() + " lock " + getLockable().getEntityName() );
 		}
 		return select.toStatementString();
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -272,6 +272,10 @@
 		return lockOptions;
 	}
 
+	public void setLockOptions(LockOptions lockOptions) {
+		this.lockOptions = lockOptions;
+	}
+
 	public void traceParameters(SessionFactoryImplementor factory) throws HibernateException {
 		Printer print = new Printer( factory );
 		if ( positionalParameterValues.length != 0 ) {

Modified: core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -496,7 +496,7 @@
 		Object entity = persister.load(
 				event.getEntityId(),
 				event.getInstanceToLoad(),
-				event.getLockMode(),
+				event.getLockOptions(),
 				source
 		);
 

Modified: core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/Loader.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/loader/Loader.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -247,11 +247,10 @@
 	 * persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
 	 * initialize that object. If a collection is supplied, attempt to initialize that collection.
 	 */
-	private List doQueryAndInitializeNonLazyCollections(final SessionImplementor session,
-														final QueryParameters queryParameters,
-														final boolean returnProxies) 
-		throws HibernateException, SQLException {
-
+	private List doQueryAndInitializeNonLazyCollections(
+			final SessionImplementor session,
+			final QueryParameters queryParameters,
+			final boolean returnProxies) throws HibernateException, SQLException {
 		final PersistenceContext persistenceContext = session.getPersistenceContext();
 		boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
 		if ( queryParameters.isReadOnlyInitialized() ) {
@@ -1628,6 +1627,24 @@
 					st.setFetchSize( selection.getFetchSize().intValue() );
 				}
 			}
+
+			// handle lock timeout...
+			LockOptions lockOptions = queryParameters.getLockOptions();
+			if ( lockOptions != null ) {
+				if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
+					if ( !dialect.supportsLockTimeouts() ) {
+						log.debug(
+								"Lock timeout [" + lockOptions.getTimeOut() +
+										"] requested but dialect reported to not support lock timeouts"
+						);
+					}
+					else if ( dialect.isLockTimeoutParameterized() ) {
+						st.setInt( col++, lockOptions.getTimeOut() );
+					}
+				}
+			}
+
+			log.trace( "Bound [" + col + "] parameters total" );
 		}
 		catch ( SQLException sqle ) {
 			session.getBatcher().closeQueryStatement( st, null );
@@ -1885,15 +1902,17 @@
 	/**
 	 * Called by subclasses that load entities
 	 * @param persister only needed for logging
+	 * @param lockOptions
 	 */
 	protected final List loadEntity(
-	        final SessionImplementor session,
-	        final Object id,
-	        final Type identifierType,
-	        final Object optionalObject,
-	        final String optionalEntityName,
-	        final Serializable optionalIdentifier,
-	        final EntityPersister persister) throws HibernateException {
+			final SessionImplementor session,
+			final Object id,
+			final Type identifierType,
+			final Object optionalObject,
+			final String optionalEntityName,
+			final Serializable optionalIdentifier,
+			final EntityPersister persister,
+			LockOptions lockOptions) throws HibernateException {
 		
 		if ( log.isDebugEnabled() ) {
 			log.debug( 
@@ -1904,17 +1923,14 @@
 
 		List result;
 		try {
-			result = doQueryAndInitializeNonLazyCollections( 
-					session,
-					new QueryParameters(
-							new Type[] { identifierType },
-							new Object[] { id },
-							optionalObject,
-							optionalEntityName,
-							optionalIdentifier 
-						),
-					false 
-				);
+			QueryParameters qp = new QueryParameters();
+			qp.setPositionalParameterTypes( new Type[] { identifierType } );
+			qp.setPositionalParameterValues( new Object[] { id } );
+			qp.setOptionalObject( optionalObject );
+			qp.setOptionalEntityName( optionalEntityName );
+			qp.setOptionalId( optionalIdentifier );
+			qp.setLockOptions( lockOptions );
+			result = doQueryAndInitializeNonLazyCollections( session, qp, false );
 		}
 		catch ( SQLException sqle ) {
 			final Loadable[] persisters = getEntityPersisters();
@@ -1951,14 +1967,14 @@
 
 		List result;
 		try {
-			result = doQueryAndInitializeNonLazyCollections( 
+			result = doQueryAndInitializeNonLazyCollections(
 					session,
-					new QueryParameters( 
+					new QueryParameters(
 							new Type[] { keyType, indexType },
 							new Object[] { key, index }
-						),
-					false 
-				);
+					),
+					false
+			);
 		}
 		catch ( SQLException sqle ) {
 			throw JDBCExceptionHelper.convert(
@@ -1978,15 +1994,17 @@
 	/**
 	 * Called by wrappers that batch load entities
 	 * @param persister only needed for logging
+	 * @param lockOptions
 	 */
 	public final List loadEntityBatch(
-	        final SessionImplementor session,
-	        final Serializable[] ids,
-	        final Type idType,
-	        final Object optionalObject,
-	        final String optionalEntityName,
-	        final Serializable optionalId,
-	        final EntityPersister persister) throws HibernateException {
+			final SessionImplementor session,
+			final Serializable[] ids,
+			final Type idType,
+			final Object optionalObject,
+			final String optionalEntityName,
+			final Serializable optionalId,
+			final EntityPersister persister,
+			LockOptions lockOptions) throws HibernateException {
 
 		if ( log.isDebugEnabled() ) {
 			log.debug( 
@@ -1999,11 +2017,14 @@
 		Arrays.fill( types, idType );
 		List result;
 		try {
-			result = doQueryAndInitializeNonLazyCollections( 
-					session,
-					new QueryParameters( types, ids, optionalObject, optionalEntityName, optionalId ),
-					false 
-				);
+			QueryParameters qp = new QueryParameters();
+			qp.setPositionalParameterTypes( types );
+			qp.setPositionalParameterValues( ids );
+			qp.setOptionalObject( optionalObject );
+			qp.setOptionalEntityName( optionalEntityName );
+			qp.setOptionalId( optionalId );
+			qp.setLockOptions( lockOptions );
+			result = doQueryAndInitializeNonLazyCollections( session, qp, false );
 		}
 		catch ( SQLException sqle ) {
 			throw JDBCExceptionHelper.convert(

Modified: core/trunk/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -32,6 +32,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.hibernate.HibernateException;
+import org.hibernate.LockOptions;
 import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.engine.SessionImplementor;
 import org.hibernate.engine.LoadQueryInfluencers;
@@ -60,13 +61,27 @@
 		
 	}
 
-	public Object load(Serializable id, Object optionalObject, SessionImplementor session) 
-	throws HibernateException {
-		return load(session, id, optionalObject, id);
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
+		// this form is deprecated!
+		return load( id, optionalObject, session, LockOptions.NONE );
 	}
 
-	protected Object load(SessionImplementor session, Object id, Object optionalObject, Serializable optionalId) 
-	throws HibernateException {
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
+		return load( session, id, optionalObject, id, lockOptions );
+	}
+
+	protected Object load(
+			SessionImplementor session,
+			Object id,
+			Object optionalObject,
+			Serializable optionalId,
+			LockOptions lockOptions) {
 		
 		List list = loadEntity(
 				session, 
@@ -75,7 +90,8 @@
 				optionalObject, 
 				entityName, 
 				optionalId, 
-				persister
+				persister,
+				lockOptions
 			);
 		
 		if ( list.size()==1 ) {

Modified: core/trunk/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -78,9 +78,15 @@
 		return null;
 	}
 
-	public Object load(Serializable id, Object optionalObject, SessionImplementor session)
-	throws HibernateException {
-		
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
+		// this form is deprecated!
+		return load( id, optionalObject, session, LockOptions.NONE );
+	}
+
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
 		Serializable[] batch = session.getPersistenceContext()
 			.getBatchFetchQueue()
 			.getEntityBatch( persister, id, batchSizes[0], session.getEntityMode() );
@@ -97,7 +103,8 @@
 						optionalObject, 
 						persister.getEntityName(), 
 						id, 
-						persister
+						persister,
+						lockOptions
 				);
 				return getObjectFromList(results, id, session); //EARLY EXIT
 			}

Modified: core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -148,14 +148,16 @@
 
 		batchLoader = batchSize > 1;
 
-		log.debug( "Static select for entity " + entityName + ": " + getSQLString() );
+		log.debug(
+				"Static select for entity " + entityName +
+						" [" + lockOptions.getLockMode() + ":" + lockOptions.getTimeOut() + "]: "
+						+ getSQLString() 
+		);
 
 	}
 
-	public Object loadByUniqueKey(
-			SessionImplementor session,
-			Object key) throws HibernateException {
-		return load( session, key, null, null );
+	public Object loadByUniqueKey(SessionImplementor session,Object key) {
+		return load( session, key, null, null, LockOptions.NONE );
 	}
 
 	protected boolean isSingleRowLoader() {

Modified: core/trunk/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -27,16 +27,37 @@
 import java.io.Serializable;
 
 import org.hibernate.HibernateException;
+import org.hibernate.LockOptions;
 import org.hibernate.engine.SessionImplementor;
 
 /**
  * Loads entities for a <tt>EntityPersister</tt>
+ *
  * @author Gavin King
+ * @author Steve Ebersole
  */
 public interface UniqueEntityLoader {
 	/**
 	 * Load an entity instance. If <tt>optionalObject</tt> is supplied,
 	 * load the entity state into the given (uninitialized) object.
+	 *
+	 * @deprecated use {@link #load(java.io.Serializable, Object, SessionImplementor, LockOptions)} instead.
+	 * @noinspection JavaDoc
 	 */
 	public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException;
+
+	/**
+	 * Load an entity instance by id.  If <tt>optionalObject</tt> is supplied (non-<tt>null</tt>,
+	 * the entity state is loaded into that object instance instead of instantiating a new one.
+	 *
+	 * @param id The id to be loaded
+	 * @param optionalObject The (optional) entity instance in to which to load the state
+	 * @param session The session from which the request originated
+	 * @param lockOptions The lock options.
+	 *
+	 * @return The loaded entity
+	 *
+	 * @throws HibernateException indicates problem performing the load.
+	 */
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions);
 }

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -3248,18 +3248,8 @@
 	 * Load an instance using either the <tt>forUpdateLoader</tt> or the outer joining <tt>loader</tt>,
 	 * depending upon the value of the <tt>lock</tt> parameter
 	 */
-	public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session)
-			throws HibernateException {
-
-		if ( log.isTraceEnabled() ) {
-			log.trace(
-					"Fetching entity: " +
-					MessageHelper.infoString( this, id, getFactory() )
-				);
-		}
-
-		final UniqueEntityLoader loader = getAppropriateLoader( new LockOptions().setLockMode(lockMode), session );
-		return loader.load( id, optionalObject, session );
+	public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session) {
+		return load( id, optionalObject, new LockOptions().setLockMode(lockMode), session );
 	}
 
 	/**
@@ -3277,7 +3267,7 @@
 		}
 
 		final UniqueEntityLoader loader = getAppropriateLoader(lockOptions, session );
-		return loader.load( id, optionalObject, session );
+		return loader.load( id, optionalObject, session, lockOptions );
 	}
 
 	public void registerAffectingFetchProfile(String fetchProfileName) {
@@ -3321,6 +3311,9 @@
 			// SQL query used for loading based on those influencers
 			return createEntityLoader(lockOptions, session.getLoadQueryInfluencers() );
 		}
+		else if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
+			return createEntityLoader( lockOptions, session.getLoadQueryInfluencers() );
+		}
 		else {
 			return ( UniqueEntityLoader ) loaders.get( lockOptions.getLockMode() );
 		}

Modified: core/trunk/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -30,6 +30,7 @@
 import org.slf4j.LoggerFactory;
 import org.hibernate.FlushMode;
 import org.hibernate.HibernateException;
+import org.hibernate.LockOptions;
 import org.hibernate.engine.EntityKey;
 import org.hibernate.engine.SessionImplementor;
 import org.hibernate.impl.AbstractQueryImpl;
@@ -38,7 +39,9 @@
 /**
  * Not really a <tt>Loader</tt>, just a wrapper around a
  * named query.
+ *
  * @author Gavin King
+ * @author Steve Ebersole
  */
 public final class NamedQueryLoader implements UniqueEntityLoader {
 	private final String queryName;
@@ -52,9 +55,14 @@
 		this.persister = persister;
 	}
 
-	public Object load(Serializable id, Object optionalObject, SessionImplementor session) 
-	throws HibernateException {
-		
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
+		if ( lockOptions != null ) {
+			log.debug( "Ignoring lock-options passed to named query loader" );
+		}
+		return load( id, optionalObject, session );
+	}
+
+	public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
 		if ( log.isDebugEnabled() ) {
 			log.debug(
 					"loading entity: " + persister.getEntityName() + 

Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -216,14 +216,14 @@
 			timeout = Integer.parseInt( ( String ) lockTimeout );
 			timeoutSet = true;
 		}
-		else if ( lockTimeout instanceof Integer ) {
-			timeout = ( Integer ) lockTimeout;
+		else if ( lockTimeout instanceof Number ) {
+			timeout = ( (Number) lockTimeout ).intValue();
 			timeoutSet = true;
 		}
 		else if ( lockTimeout != null ) {
 			throw new PersistenceException( "Unable to parse " + AvailableSettings.LOCK_TIMEOUT + ": " + lockTimeout );
 		}
-		if ( timeoutSet != false ) {
+		if ( timeoutSet ) {
 			if ( timeout < 0 ) {
 				options.setTimeOut( LockOptions.WAIT_FOREVER );
 			}

Modified: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java	2010-03-24 12:18:25 UTC (rev 19094)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java	2010-03-24 14:37:56 UTC (rev 19095)
@@ -12,6 +12,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.hibernate.dialect.HSQLDialect;
 import org.hibernate.dialect.Oracle10gDialect;
+import org.hibernate.ejb.AvailableSettings;
 import org.hibernate.ejb.test.TestCase;
 
 import java.util.HashMap;
@@ -26,8 +27,33 @@
  * @author Emmanuel Bernard
  */
 public class LockTest extends TestCase {
+	private static final Log log = LogFactory.getLog( LockTest.class );
 
-	private static final Log log = LogFactory.getLog( LockTest.class );
+	public void testFindWithTimeoutHint() {
+		EntityManager em = getOrCreateEntityManager();
+		em.getTransaction().begin();
+		Lock lock = new Lock();
+		lock.setName( "name" );
+		em.persist( lock );
+		em.getTransaction().commit();
+		em.close();
+
+		em = getOrCreateEntityManager();
+		em.getTransaction().begin();
+		Map<String, Object> properties = new HashMap<String, Object>();
+		properties.put( AvailableSettings.LOCK_TIMEOUT, 0L );
+		em.find( Lock.class, 1, LockModeType.PESSIMISTIC_WRITE, properties );
+		em.getTransaction().commit();
+		em.close();
+
+		em = getOrCreateEntityManager();
+		em.getTransaction().begin();
+		lock = em.find( Lock.class, lock.getId() );
+		em.remove( lock );
+		em.getTransaction().commit();
+		em.close();
+	}
+
 	public void testLockRead() throws Exception {
 		Lock lock = new Lock();
 		lock.setName( "name" );



More information about the hibernate-commits mailing list