From 18bb17061f629819414362b30134c365928d77bc Mon Sep 17 00:00:00 2001 From: Scott Marlow Date: Fri, 11 Dec 2009 13:23:17 +0000 Subject: [PATCH] HHH-4546 JPA-2.0 locking. More pessimistic lock exception support and timeout exception support git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18209 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- .../hibernate/PessimisticLockException.java | 26 +++++++------ .../exception/SQLStateConverter.java | 6 +++ .../java/org/hibernate/loader/Loader.java | 29 +++++++++++++-- .../org/hibernate/loader/OuterJoinLoader.java | 2 - .../ejb/AbstractEntityManagerImpl.java | 37 ++++++++++++++----- .../HibernateEntityManagerImplementor.java | 13 +++++++ 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/hibernate/PessimisticLockException.java b/core/src/main/java/org/hibernate/PessimisticLockException.java index 81849eb7ba..8c3742ef35 100644 --- a/core/src/main/java/org/hibernate/PessimisticLockException.java +++ b/core/src/main/java/org/hibernate/PessimisticLockException.java @@ -24,29 +24,33 @@ */ package org.hibernate; +import java.sql.SQLException; + /** * - * Throw when an pessimistic locking conflict occurs. + * Thrown when a pessimistic locking conflict occurs. * * @author Scott Marlow */ -public class PessimisticLockException extends HibernateException { +public class PessimisticLockException extends JDBCException { Object entity; - public PessimisticLockException(String s) { - super(s); - } - public PessimisticLockException(String s, Throwable throwable, Object entity) { - super(s, throwable); + public PessimisticLockException(String s, JDBCException je, Object entity) { + super(s, je.getSQLException()); + this.entity = entity; + } + + public PessimisticLockException(String s, SQLException se, Object entity) { + super(s, se); this.entity = entity; } - public PessimisticLockException(String s, Object entity) { - super(s); - this.entity = entity; - } + public PessimisticLockException(String s, SQLException se, String sql) { + super(s, se, sql); + this.entity = null; + } public Object getEntity() { return entity; diff --git a/core/src/main/java/org/hibernate/exception/SQLStateConverter.java b/core/src/main/java/org/hibernate/exception/SQLStateConverter.java index 548ef639b7..a0201c21d5 100644 --- a/core/src/main/java/org/hibernate/exception/SQLStateConverter.java +++ b/core/src/main/java/org/hibernate/exception/SQLStateConverter.java @@ -25,6 +25,7 @@ package org.hibernate.exception; import org.hibernate.JDBCException; +import org.hibernate.PessimisticLockException; import java.sql.SQLException; import java.util.HashSet; @@ -109,6 +110,11 @@ public class SQLStateConverter implements SQLExceptionConverter { // oracle sql-state code for deadlock return new LockAcquisitionException( message, sqlException, sql ); } + + if ( "40XL1".equals( sqlState ) || "40XL2".equals( sqlState )) { + // Derby "A lock could not be obtained within the time requested." + return new PessimisticLockException( message, sqlException, sql ); + } } return handledNonSpecificException( sqlException, message, sql ); diff --git a/core/src/main/java/org/hibernate/loader/Loader.java b/core/src/main/java/org/hibernate/loader/Loader.java index 4162f9284b..f8f5e1d45e 100644 --- a/core/src/main/java/org/hibernate/loader/Loader.java +++ b/core/src/main/java/org/hibernate/loader/Loader.java @@ -214,13 +214,34 @@ public abstract class Loader { return null; } + private Map buildLockMap(Map locks) { + Map result = locks; + if ( result == null ) { + LockOptions[] lockArray = getLockOptions(result); + String[] aliases = getAliases(); + if (aliases != null && + lockArray != null && + lockArray.length > 0 && + lockArray.length == aliases.length && + lockArray[0].getLockMode() != LockMode.NONE ) { + result = new HashMap(); + for ( int looper = 0; looper < lockArray.length; looper++ ) { + result.put(aliases[looper], lockArray[looper]); + } + } + } + + return result; + } /** * Modify the SQL, adding lock hints and comments, if necessary */ protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect) throws HibernateException { - - sql = applyLocks( sql, parameters.getLockOptions(), dialect ); + + Map locks = buildLockMap(parameters.getLockOptions()); + + sql = applyLocks( sql, locks, dialect ); return getFactory().getSettings().isCommentsEnabled() ? prependComment( sql, parameters ) : sql; @@ -700,9 +721,9 @@ public abstract class Loader { // // Would need to change the way the max-row stuff is handled (i.e. behind an interface) so // that I could do the control breaking at the means to know when to stop - final LockOptions[] lockOptionsArray = getLockOptions( queryParameters.getLockOptions() ); - final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session ); + final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session ); + final LockOptions[] lockOptionsArray = getLockOptions( queryParameters.getLockOptions() ); final boolean createSubselects = isSubselectLoadingEnabled(); final List subselectResultKeys = createSubselects ? new ArrayList() : null; final List results = new ArrayList(); diff --git a/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java b/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java index a7a79ea82e..9c710b9a80 100644 --- a/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java +++ b/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java @@ -25,9 +25,7 @@ package org.hibernate.loader; import java.util.Map; -import java.util.Set; -import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.dialect.Dialect; import org.hibernate.engine.SessionFactoryImplementor; diff --git a/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java b/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java index 9492589efd..c29cf7b76e 100755 --- a/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java @@ -257,9 +257,11 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } public A find(Class entityClass, Object primaryKey, LockModeType lockModeType, Map properties) { + LockOptions lockOptions = null; try { if ( lockModeType != null ) - return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey, getLockRequest(lockModeType, properties) ); + return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey, + ( lockOptions = getLockRequest(lockModeType, properties) ) ); else return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey ); } @@ -281,7 +283,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throw convert( he ); + throw convert( he , lockOptions ); } } @@ -351,12 +353,13 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage public void refresh(Object entity, LockModeType lockModeType, Map properties) { checkTransactionNeeded(); + LockOptions lockOptions = null; try { if ( !getSession().contains( entity ) ) { throw new IllegalArgumentException( "Entity not managed" ); } if(lockModeType != null) - getSession().refresh( entity, getLockRequest(lockModeType, properties) ); + getSession().refresh( entity, (lockOptions = getLockRequest(lockModeType, properties) ) ); else getSession().refresh( entity ); } @@ -364,7 +367,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage throw new IllegalArgumentException( e.getMessage(), e ); } catch ( HibernateException he ) { - throw convert( he ); + throw convert( he, lockOptions); } } @@ -515,6 +518,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } public void lock(Object entity, LockModeType lockModeType, Map properties) { + LockOptions lockOptions = null; try { if ( !isTransactionInProgress() ) { throw new TransactionRequiredException( "no transaction is in progress" ); @@ -523,10 +527,10 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage if ( !contains( entity ) ) { throw new IllegalArgumentException( "entity not in the persistence context" ); } - getSession().buildLockRequest(getLockRequest(lockModeType, properties)).lock( entity ); + getSession().buildLockRequest( (lockOptions = getLockRequest(lockModeType, properties))).lock( entity ); } catch ( HibernateException he ) { - throw convert( he ); + throw convert( he , lockOptions); } } @@ -835,18 +839,25 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage * {@inheritDoc} */ public RuntimeException convert(HibernateException e) { + return convert(e, null); + } + + /** + * {@inheritDoc} + */ + public RuntimeException convert(HibernateException e, LockOptions lockOptions) { if ( e instanceof StaleStateException ) { PersistenceException converted = wrapStaleStateException( ( StaleStateException ) e ); handlePersistenceException( converted ); return converted; } else if ( e instanceof org.hibernate.OptimisticLockException ) { - PersistenceException converted = wrapLockException(e); + PersistenceException converted = wrapLockException(e, lockOptions); handlePersistenceException( converted ); return converted; } else if ( e instanceof org.hibernate.PessimisticLockException ) { - PersistenceException converted = wrapLockException(e); + PersistenceException converted = wrapLockException(e, lockOptions); handlePersistenceException( converted ); return converted; } @@ -925,7 +936,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage return pe; } - public PersistenceException wrapLockException(HibernateException e) { + public PersistenceException wrapLockException(HibernateException e, LockOptions lockOptions) { PersistenceException pe; if ( e instanceof org.hibernate.OptimisticLockException ) { org.hibernate.OptimisticLockException ole = (org.hibernate.OptimisticLockException)e; @@ -933,7 +944,13 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage } else if ( e instanceof org.hibernate.PessimisticLockException ) { org.hibernate.PessimisticLockException ple = (org.hibernate.PessimisticLockException)e; - pe = new PessimisticLockException(ple.getMessage(), ple, ple.getEntity()); + if (lockOptions !=null && lockOptions.getTimeOut() > -1) { + // assume lock timeout occurred if a timeout or NO WAIT was specified + pe = new LockTimeoutException(ple.getMessage(), ple, ple.getEntity()); + } + else { + pe = new PessimisticLockException(ple.getMessage(), ple, ple.getEntity()); + } } else { pe = new OptimisticLockException( e ); diff --git a/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java b/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java index 850e7d48e7..4ccdf66ea5 100644 --- a/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java +++ b/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java @@ -27,6 +27,7 @@ import javax.persistence.PersistenceException; import org.hibernate.HibernateException; import org.hibernate.StaleStateException; +import org.hibernate.LockOptions; /** * Additional internal contracts for the Hibernate {@link javax.persistence.EntityManager} implementation. @@ -57,6 +58,18 @@ public interface HibernateEntityManagerImplementor extends HibernateEntityManage */ public void throwPersistenceException(PersistenceException e); + /** + * Converts a Hibernate-specific exception into a JPA-specified exception; note that the JPA sepcification makes use + * of exceptions outside its exception hierarchy, though they are all runtime exceptions. + *

+ * Any appropriate/needed calls to {@link #handlePersistenceException} are also made. + * + * @param e The Hibernate excepton. + * @param lockOptions The lock options in effect at the time of exception (can be null) + * @return The JPA-specified exception + */ + public RuntimeException convert(HibernateException e, LockOptions lockOptions); + /** * Converts a Hibernate-specific exception into a JPA-specified exception; note that the JPA sepcification makes use * of exceptions outside its exception hierarchy, though they are all runtime exceptions.