HHH-6736 Added support for SELECT ... FOR UPDATE SKIP LOCKED

This commit is contained in:
Aleksander Blomskøld 2013-01-27 18:58:35 +01:00 committed by Steve Ebersole
parent 59bb86978e
commit e0cfc6bf2e
17 changed files with 189 additions and 88 deletions

View File

@ -263,6 +263,11 @@
<entry><para>acquired upon explicit user request using a <code>SELECT ... FOR UPDATE NOWAIT</code> in <entry><para>acquired upon explicit user request using a <code>SELECT ... FOR UPDATE NOWAIT</code> in
Oracle.</para></entry> Oracle.</para></entry>
</row> </row>
<row>
<entry>LockMode.UPGRADE_SKIPLOCKED</entry>
<entry><para>acquired upon explicit user request using a <code>SELECT ... FOR UPDATE SKIP LOCKED</code> in
Oracle, or <code>SELECT ... with (rowlock,updlock,readpast) in SQL Server</code>.</para></entry>
</row>
<row> <row>
<entry>LockMode.READ</entry> <entry>LockMode.READ</entry>
<entry><para>acquired automatically when Hibernate reads data under <phrase>Repeatable Read</phrase> or <entry><para>acquired automatically when Hibernate reads data under <phrase>Repeatable Read</phrase> or
@ -299,17 +304,18 @@
</listitem> </listitem>
</itemizedlist> </itemizedlist>
<para> <para>
If you call <methodname>Session.load()</methodname> with option <option>UPGRADE</option> or If you call <methodname>Session.load()</methodname> with option <option>UPGRADE</option>,
<option>UPGRADE_NOWAIT</option>, and the requested object is not already loaded by the session, the object is <option>UPGRADE_NOWAIT</option> or <option>UPGRADE_SKIPLOCKED</option>, and the requested object is not already
loaded using <code>SELECT ... FOR UPDATE</code>. If you call <methodname>load()</methodname> for an object that loaded by the session, the object is loaded using <code>SELECT ... FOR UPDATE</code>. If you call
is already loaded with a less restrictive lock than the one you request, Hibernate calls <methodname>load()</methodname> for an object that is already loaded with a less restrictive lock than the one
<methodname>lock()</methodname> for that object. you request, Hibernate calls <methodname>lock()</methodname> for that object.
</para> </para>
<para> <para>
<methodname>Session.lock()</methodname> performs a version number check if the specified lock mode is <methodname>Session.lock()</methodname> performs a version number check if the specified lock mode is
<literal>READ</literal>, <literal>UPGRADE</literal>, or <literal>UPGRADE_NOWAIT</literal>. In the case of <literal>READ</literal>, <literal>UPGRADE</literal>, <literal>UPGRADE_NOWAIT</literal> or
<literal>UPGRADE</literal> or <literal>UPGRADE_NOWAIT</literal>, <code>SELECT ... FOR UPDATE</code> syntax is <literal>UPGRADE_SKIPLOCKED</literal>. In the case of <literal>UPGRADE</literal>,
used. <literal>UPGRADE_NOWAIT</literal> or <literal>UPGRADE_SKIPLOCKED</literal>, <code>SELECT ... FOR UPDATE</code>
syntax is used.
</para> </para>
<para> <para>
If the requested lock mode is not supported by the database, Hibernate uses an appropriate alternate mode If the requested lock mode is not supported by the database, Hibernate uses an appropriate alternate mode

View File

@ -66,6 +66,15 @@ public enum LockMode {
* <tt>UPGRADE</tt>. * <tt>UPGRADE</tt>.
*/ */
UPGRADE_NOWAIT( 10 ), UPGRADE_NOWAIT( 10 ),
/**
* Attempt to obtain an upgrade lock, using an Oracle-style
* <tt>select for update skip locked</tt>. The semantics of
* this lock mode, once obtained, are the same as
* <tt>UPGRADE</tt>.
*/
UPGRADE_SKIPLOCKED( 10 ),
/** /**
* A <tt>WRITE</tt> lock is obtained when an object is updated * A <tt>WRITE</tt> lock is obtained when an object is updated
* or inserted. This lock mode is for internal use only and is * or inserted. This lock mode is for internal use only and is

View File

@ -64,6 +64,12 @@ public class LockOptions implements Serializable {
*/ */
public static final int WAIT_FOREVER = -1; public static final int WAIT_FOREVER = -1;
/**
* Indicates that rows that are already locked should be skipped.
* @see #getTimeOut()
*/
public static final int SKIP_LOCKED = -2;
private LockMode lockMode = LockMode.NONE; private LockMode lockMode = LockMode.NONE;
private int timeout = WAIT_FOREVER; private int timeout = WAIT_FOREVER;
@ -221,9 +227,9 @@ public class LockOptions implements Serializable {
* The timeout is the amount of time, in milliseconds, we should instruct the database * The timeout is the amount of time, in milliseconds, we should instruct the database
* to wait for any requested pessimistic lock acquisition. * to wait for any requested pessimistic lock acquisition.
* <p/> * <p/>
* {@link #NO_WAIT} and {@link #WAIT_FOREVER} represent 2 "magic" values. * {@link #NO_WAIT}, {@link #WAIT_FOREVER} or {@link #SKIP_LOCKED} represent 3 "magic" values.
* *
* @return timeout in milliseconds, or {@link #NO_WAIT} or {@link #WAIT_FOREVER} * @return timeout in milliseconds, {@link #NO_WAIT}, {@link #WAIT_FOREVER} or {@link #SKIP_LOCKED}
*/ */
public int getTimeOut() { public int getTimeOut() {
return timeout; return timeout;

View File

@ -375,6 +375,9 @@ public abstract class ResultSetMappingBinder {
else if ( "upgrade-nowait".equals( lockMode ) ) { else if ( "upgrade-nowait".equals( lockMode ) ) {
return LockMode.UPGRADE_NOWAIT; return LockMode.UPGRADE_NOWAIT;
} }
else if ( "upgrade-skiplocked".equals( lockMode )) {
return LockMode.UPGRADE_SKIPLOCKED;
}
else if ( "write".equals( lockMode ) ) { else if ( "write".equals( lockMode ) ) {
return LockMode.WRITE; return LockMode.WRITE;
} }

View File

@ -23,40 +23,14 @@
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.NClob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.NullPrecedence; import org.hibernate.NullPrecedence;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CastFunction; import org.hibernate.dialect.function.*;
import org.hibernate.dialect.function.SQLFunction; import org.hibernate.dialect.lock.*;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardAnsiSqlAggregationFunctions;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.PessimisticReadSelectLockingStrategy;
import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy;
import org.hibernate.dialect.lock.SelectLockingStrategy;
import org.hibernate.dialect.pagination.LegacyLimitHandler; import org.hibernate.dialect.pagination.LegacyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.unique.DefaultUniqueDelegate; import org.hibernate.dialect.unique.DefaultUniqueDelegate;
@ -78,16 +52,17 @@ import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.io.StreamCopier; import org.hibernate.internal.util.io.StreamCopier;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.persister.entity.Lockable; import org.hibernate.persister.entity.Lockable;
import org.hibernate.sql.ANSICaseFragment; import org.hibernate.sql.*;
import org.hibernate.sql.ANSIJoinFragment;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.*;
import java.util.*;
/** /**
* Represents a dialect of SQL implemented by a particular RDBMS. * Represents a dialect of SQL implemented by a particular RDBMS.
* Subclasses implement Hibernate compatibility with different systems.<br> * Subclasses implement Hibernate compatibility with different systems.<br>
@ -1195,6 +1170,8 @@ public abstract class Dialect implements ConversionContext {
case FORCE: case FORCE:
case PESSIMISTIC_FORCE_INCREMENT: case PESSIMISTIC_FORCE_INCREMENT:
return getForUpdateNowaitString(); return getForUpdateNowaitString();
case UPGRADE_SKIPLOCKED:
return getForUpdateSkipLockedString();
default: default:
return ""; return "";
} }
@ -1313,6 +1290,16 @@ public abstract class Dialect implements ConversionContext {
return getForUpdateString(); return getForUpdateString();
} }
/**
* Retrieves the <tt>FOR UPDATE SKIP LOCKED</tt> syntax specific to this dialect.
*
* @return The appropriate <tt>FOR UPDATE SKIP LOCKED</tt> clause string.
*/
public String getForUpdateSkipLockedString() {
// by default we report no support for SKIP_LOCKED lock semantics
return getForUpdateString();
}
/** /**
* Get the <tt>FOR UPDATE OF column_list NOWAIT</tt> fragment appropriate * Get the <tt>FOR UPDATE OF column_list NOWAIT</tt> fragment appropriate
* for this dialect given the aliases of the columns to be write locked. * for this dialect given the aliases of the columns to be write locked.
@ -1324,6 +1311,17 @@ public abstract class Dialect implements ConversionContext {
return getForUpdateString( aliases ); return getForUpdateString( aliases );
} }
/**
* Get the <tt>FOR UPDATE OF column_list SKIP LOCKED</tt> fragment appropriate
* for this dialect given the aliases of the columns to be write locked.
*
* @param aliases The columns to be write locked.
* @return The appropriate <tt>FOR UPDATE colunm_list SKIP LOCKED</tt> clause string.
*/
public String getForUpdateSkipLockedString(String aliases) {
return getForUpdateString( aliases );
}
/** /**
* Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>, * Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>,
* whereby a "lock hint" is appends to the table name in the from clause. * whereby a "lock hint" is appends to the table name in the from clause.

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import org.hibernate.LockOptions;
import org.hibernate.sql.ANSIJoinFragment; import org.hibernate.sql.ANSIJoinFragment;
import org.hibernate.sql.JoinFragment; import org.hibernate.sql.JoinFragment;
@ -45,4 +46,21 @@ public class Oracle10gDialect extends Oracle9iDialect {
public JoinFragment createOuterJoinFragment() { public JoinFragment createOuterJoinFragment() {
return new ANSIJoinFragment(); return new ANSIJoinFragment();
} }
public String getWriteLockString(int timeout) {
if ( timeout == LockOptions.SKIP_LOCKED ) {
return getForUpdateSkipLockedString();
}
else {
return super.getWriteLockString(timeout);
}
}
public String getForUpdateSkipLockedString() {
return " for update skip locked";
}
public String getForUpdateSkipLockedString(String aliases) {
return getForUpdateString() + " of " + aliases + " skip locked";
}
} }

View File

@ -134,6 +134,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
return tableName + " with (updlock, rowlock)"; return tableName + " with (updlock, rowlock)";
case PESSIMISTIC_READ: case PESSIMISTIC_READ:
return tableName + " with (holdlock, rowlock)"; return tableName + " with (holdlock, rowlock)";
case UPGRADE_SKIPLOCKED:
return tableName + " with (updlock, rowlock, readpast)";
default: default:
return tableName; return tableName;
} }

View File

@ -54,11 +54,18 @@ public abstract class AbstractSelectLockingStrategy implements LockingStrategy {
protected abstract String generateLockString(int lockTimeout); protected abstract String generateLockString(int lockTimeout);
protected String determineSql(int timeout) { protected String determineSql(int timeout) {
return timeout == LockOptions.WAIT_FOREVER if ( timeout == LockOptions.WAIT_FOREVER) {
? waitForeverSql return waitForeverSql;
: timeout == LockOptions.NO_WAIT }
? getNoWaitSql() else if ( timeout == LockOptions.NO_WAIT) {
: generateLockString( timeout ); return getNoWaitSql();
}
else if ( timeout == LockOptions.SKIP_LOCKED) {
return getSkipLockedSql();
}
else {
return generateLockString( timeout );
}
} }
private String noWaitSql; private String noWaitSql;
@ -69,4 +76,13 @@ public abstract class AbstractSelectLockingStrategy implements LockingStrategy {
} }
return noWaitSql; return noWaitSql;
} }
private String skipLockedSql;
public String getSkipLockedSql() {
if ( skipLockedSql == null ) {
skipLockedSql = generateLockString( LockOptions.SKIP_LOCKED );
}
return skipLockedSql;
}
} }

View File

@ -57,7 +57,8 @@ public class LockModeConverter {
} }
else if ( lockMode == LockMode.PESSIMISTIC_WRITE else if ( lockMode == LockMode.PESSIMISTIC_WRITE
|| lockMode == LockMode.UPGRADE || lockMode == LockMode.UPGRADE
|| lockMode == LockMode.UPGRADE_NOWAIT ) { || lockMode == LockMode.UPGRADE_NOWAIT
|| lockMode == LockMode.UPGRADE_SKIPLOCKED) {
return LockModeType.PESSIMISTIC_WRITE; return LockModeType.PESSIMISTIC_WRITE;
} }
else if ( lockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT else if ( lockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT

View File

@ -254,22 +254,24 @@ public abstract class Loader {
Dialect dialect, Dialect dialect,
List<AfterLoadAction> afterLoadActions) { List<AfterLoadAction> afterLoadActions) {
if ( dialect.useFollowOnLocking() ) { if ( dialect.useFollowOnLocking() ) {
LOG.usingFollowOnLocking();
// currently only one lock mode is allowed in follow-on locking // currently only one lock mode is allowed in follow-on locking
final LockMode lockMode = determineFollowOnLockMode( parameters.getLockOptions() ); final LockMode lockMode = determineFollowOnLockMode( parameters.getLockOptions() );
final LockOptions lockOptions = new LockOptions( lockMode ); final LockOptions lockOptions = new LockOptions( lockMode );
lockOptions.setTimeOut( parameters.getLockOptions().getTimeOut() ); if ( lockOptions.getLockMode() != LockMode.UPGRADE_SKIPLOCKED ) {
lockOptions.setScope( parameters.getLockOptions().getScope() ); LOG.usingFollowOnLocking();
afterLoadActions.add( lockOptions.setTimeOut( parameters.getLockOptions().getTimeOut() );
new AfterLoadAction() { lockOptions.setScope( parameters.getLockOptions().getScope() );
@Override afterLoadActions.add(
public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { new AfterLoadAction() {
( (Session) session ).buildLockRequest( lockOptions ).lock( persister.getEntityName(), entity ); @Override
public void afterLoad(SessionImplementor session, Object entity, Loadable persister) {
( (Session) session ).buildLockRequest( lockOptions ).lock( persister.getEntityName(), entity );
}
} }
} );
); parameters.setLockOptions( new LockOptions() );
parameters.setLockOptions( new LockOptions() ); return true;
return true; }
} }
return false; return false;
} }

View File

@ -205,27 +205,28 @@ public class CriteriaLoader extends OuterJoinLoader {
} }
if ( dialect.useFollowOnLocking() ) { if ( dialect.useFollowOnLocking() ) {
// Dialect prefers to perform locking in a separate step final LockMode lockMode = determineFollowOnLockMode( lockOptions );
LOG.usingFollowOnLocking(); if( lockMode != LockMode.UPGRADE_SKIPLOCKED ) {
// Dialect prefers to perform locking in a separate step
LOG.usingFollowOnLocking();
final LockMode lockMode = determineFollowOnLockMode( lockOptions ); final LockOptions lockOptionsToUse = new LockOptions( lockMode );
final LockOptions lockOptionsToUse = new LockOptions( lockMode ); lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() );
lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); lockOptionsToUse.setScope( lockOptions.getScope() );
lockOptionsToUse.setScope( lockOptions.getScope() );
afterLoadActions.add( afterLoadActions.add(
new AfterLoadAction() { new AfterLoadAction() {
@Override @Override
public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { public void afterLoad(SessionImplementor session, Object entity, Loadable persister) {
( (Session) session ).buildLockRequest( lockOptionsToUse ) ( (Session) session ).buildLockRequest( lockOptionsToUse )
.lock( persister.getEntityName(), entity ); .lock( persister.getEntityName(), entity );
} }
} }
); );
parameters.setLockOptions( new LockOptions() ); parameters.setLockOptions( new LockOptions() );
return sql; return sql;
}
} }
final LockOptions locks = new LockOptions(lockOptions.getLockMode()); final LockOptions locks = new LockOptions(lockOptions.getLockMode());
locks.setScope( lockOptions.getScope()); locks.setScope( lockOptions.getScope());
locks.setTimeOut( lockOptions.getTimeOut()); locks.setTimeOut( lockOptions.getTimeOut());

View File

@ -1910,6 +1910,7 @@ public abstract class AbstractEntityPersister
lockers.put( LockMode.READ, generateLocker( LockMode.READ ) ); lockers.put( LockMode.READ, generateLocker( LockMode.READ ) );
lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) ); lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) );
lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) ); lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) );
lockers.put( LockMode.UPGRADE_SKIPLOCKED, generateLocker( LockMode.UPGRADE_SKIPLOCKED ) );
lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) ); lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
lockers.put( LockMode.PESSIMISTIC_READ, generateLocker( LockMode.PESSIMISTIC_READ ) ); lockers.put( LockMode.PESSIMISTIC_READ, generateLocker( LockMode.PESSIMISTIC_READ ) );
lockers.put( LockMode.PESSIMISTIC_WRITE, generateLocker( LockMode.PESSIMISTIC_WRITE ) ); lockers.put( LockMode.PESSIMISTIC_WRITE, generateLocker( LockMode.PESSIMISTIC_WRITE ) );
@ -3851,6 +3852,12 @@ public abstract class AbstractEntityPersister
readLoader : readLoader :
createEntityLoader( LockMode.UPGRADE_NOWAIT ) createEntityLoader( LockMode.UPGRADE_NOWAIT )
); );
loaders.put(
LockMode.UPGRADE_SKIPLOCKED,
disableForUpdate ?
readLoader :
createEntityLoader( LockMode.UPGRADE_SKIPLOCKED )
);
loaders.put( loaders.put(
LockMode.FORCE, LockMode.FORCE,
disableForUpdate ? disableForUpdate ?

View File

@ -39,6 +39,7 @@ import org.hibernate.internal.util.StringHelper;
public class ForUpdateFragment { public class ForUpdateFragment {
private final StringBuilder aliases = new StringBuilder(); private final StringBuilder aliases = new StringBuilder();
private boolean isNowaitEnabled; private boolean isNowaitEnabled;
private boolean isSkipLockedEnabled;
private final Dialect dialect; private final Dialect dialect;
private LockMode lockMode; private LockMode lockMode;
private LockOptions lockOptions; private LockOptions lockOptions;
@ -89,6 +90,10 @@ public class ForUpdateFragment {
if ( upgradeType == LockMode.UPGRADE_NOWAIT ) { if ( upgradeType == LockMode.UPGRADE_NOWAIT ) {
setNowaitEnabled( true ); setNowaitEnabled( true );
} }
if ( upgradeType == LockMode.UPGRADE_SKIPLOCKED ) {
setSkipLockedEnabled( true );
}
} }
public ForUpdateFragment addTableAlias(String alias) { public ForUpdateFragment addTableAlias(String alias) {
@ -110,13 +115,25 @@ public class ForUpdateFragment {
return ""; return "";
} }
// TODO: pass lockmode // TODO: pass lockmode
return isNowaitEnabled ? if(isNowaitEnabled) {
dialect.getForUpdateNowaitString( aliases.toString() ) : return dialect.getForUpdateNowaitString( aliases.toString() );
dialect.getForUpdateString( aliases.toString() ); }
else if (isSkipLockedEnabled) {
return dialect.getForUpdateSkipLockedString( aliases.toString() );
}
else {
return dialect.getForUpdateString( aliases.toString() );
}
} }
public ForUpdateFragment setNowaitEnabled(boolean nowait) { public ForUpdateFragment setNowaitEnabled(boolean nowait) {
isNowaitEnabled = nowait; isNowaitEnabled = nowait;
return this; return this;
} }
public ForUpdateFragment setSkipLockedEnabled(boolean skipLocked) {
isSkipLockedEnabled = skipLocked;
return this;
}
} }

View File

@ -993,7 +993,7 @@ finder methods for named queries -->
<!ATTLIST return alias CDATA #IMPLIED> <!ATTLIST return alias CDATA #IMPLIED>
<!ATTLIST return entity-name CDATA #IMPLIED> <!ATTLIST return entity-name CDATA #IMPLIED>
<!ATTLIST return class CDATA #IMPLIED> <!ATTLIST return class CDATA #IMPLIED>
<!ATTLIST return lock-mode (none|read|upgrade|upgrade-nowait|write) "read"> <!ATTLIST return lock-mode (none|read|upgrade|upgrade-nowait|upgrade-skiplocked|write) "read">
<!ELEMENT return-property (return-column*)> <!ELEMENT return-property (return-column*)>
<!ATTLIST return-property name CDATA #REQUIRED> <!ATTLIST return-property name CDATA #REQUIRED>
@ -1008,12 +1008,12 @@ finder methods for named queries -->
<!ELEMENT return-join (return-property)*> <!ELEMENT return-join (return-property)*>
<!ATTLIST return-join alias CDATA #REQUIRED> <!ATTLIST return-join alias CDATA #REQUIRED>
<!ATTLIST return-join property CDATA #REQUIRED> <!ATTLIST return-join property CDATA #REQUIRED>
<!ATTLIST return-join lock-mode (none|read|upgrade|upgrade-nowait|write) "read"> <!ATTLIST return-join lock-mode (none|read|upgrade|upgrade-nowait|upgrade-skiplocked|write) "read">
<!ELEMENT load-collection (return-property)*> <!ELEMENT load-collection (return-property)*>
<!ATTLIST load-collection alias CDATA #REQUIRED> <!ATTLIST load-collection alias CDATA #REQUIRED>
<!ATTLIST load-collection role CDATA #REQUIRED> <!ATTLIST load-collection role CDATA #REQUIRED>
<!ATTLIST load-collection lock-mode (none|read|upgrade|upgrade-nowait|write) "read"> <!ATTLIST load-collection lock-mode (none|read|upgrade|upgrade-nowait|upgrade-skiplocked|write) "read">
<!ELEMENT return-scalar EMPTY> <!ELEMENT return-scalar EMPTY>
<!ATTLIST return-scalar column CDATA #REQUIRED> <!ATTLIST return-scalar column CDATA #REQUIRED>

View File

@ -1785,6 +1785,7 @@ arbitrary number of queries, and import declarations of arbitrary classes.
<xs:enumeration value="read"/> <xs:enumeration value="read"/>
<xs:enumeration value="upgrade"/> <xs:enumeration value="upgrade"/>
<xs:enumeration value="upgrade-nowait"/> <xs:enumeration value="upgrade-nowait"/>
<xs:enumeration value="upgrade-skiplocked"/>
<xs:enumeration value="write"/> <xs:enumeration value="write"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

View File

@ -1098,10 +1098,13 @@ public class ParentChildTest extends LegacyTestCase {
s3.setCount(3); s3.setCount(3);
Simple s4 = new Simple( Long.valueOf(4) ); Simple s4 = new Simple( Long.valueOf(4) );
s4.setCount(4); s4.setCount(4);
Simple s5 = new Simple( Long.valueOf(5) );
s5.setCount(5);
s.save( s1 ); s.save( s1 );
s.save( s2 ); s.save( s2 );
s.save( s3 ); s.save( s3 );
s.save( s4 ); s.save( s4 );
s.save( s5 );
assertTrue( s.getCurrentLockMode(s1)==LockMode.WRITE ); assertTrue( s.getCurrentLockMode(s1)==LockMode.WRITE );
tx.commit(); tx.commit();
s.close(); s.close();
@ -1116,6 +1119,8 @@ public class ParentChildTest extends LegacyTestCase {
assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE ); assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE );
s4 = (Simple) s.get(Simple.class, new Long(4), LockMode.UPGRADE_NOWAIT); s4 = (Simple) s.get(Simple.class, new Long(4), LockMode.UPGRADE_NOWAIT);
assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT ); assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT );
s5 = (Simple) s.get(Simple.class, new Long(5), LockMode.UPGRADE_SKIPLOCKED);
assertTrue( s.getCurrentLockMode(s5)==LockMode.UPGRADE_SKIPLOCKED );
s1 = (Simple) s.load(Simple.class, new Long(1), LockMode.UPGRADE); //upgrade s1 = (Simple) s.load(Simple.class, new Long(1), LockMode.UPGRADE); //upgrade
assertTrue( s.getCurrentLockMode(s1)==LockMode.UPGRADE ); assertTrue( s.getCurrentLockMode(s1)==LockMode.UPGRADE );
@ -1125,6 +1130,8 @@ public class ParentChildTest extends LegacyTestCase {
assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE ); assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE );
s4 = (Simple) s.load(Simple.class, new Long(4), LockMode.UPGRADE); s4 = (Simple) s.load(Simple.class, new Long(4), LockMode.UPGRADE);
assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT ); assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT );
s5 = (Simple) s.load(Simple.class, new Long(5), LockMode.UPGRADE);
assertTrue( s.getCurrentLockMode(s5)==LockMode.UPGRADE_SKIPLOCKED );
s.lock(s2, LockMode.UPGRADE); //upgrade s.lock(s2, LockMode.UPGRADE); //upgrade
assertTrue( s.getCurrentLockMode(s2)==LockMode.UPGRADE ); assertTrue( s.getCurrentLockMode(s2)==LockMode.UPGRADE );
@ -1132,7 +1139,9 @@ public class ParentChildTest extends LegacyTestCase {
assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE ); assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE );
s.lock(s1, LockMode.UPGRADE_NOWAIT); s.lock(s1, LockMode.UPGRADE_NOWAIT);
s.lock(s4, LockMode.NONE); s.lock(s4, LockMode.NONE);
s.lock(s5, LockMode.UPGRADE_SKIPLOCKED);
assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT ); assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT );
assertTrue( s.getCurrentLockMode(s5)==LockMode.UPGRADE_SKIPLOCKED );
tx.commit(); tx.commit();
tx = s.beginTransaction(); tx = s.beginTransaction();
@ -1141,6 +1150,7 @@ public class ParentChildTest extends LegacyTestCase {
assertTrue( s.getCurrentLockMode(s1)==LockMode.NONE ); assertTrue( s.getCurrentLockMode(s1)==LockMode.NONE );
assertTrue( s.getCurrentLockMode(s2)==LockMode.NONE ); assertTrue( s.getCurrentLockMode(s2)==LockMode.NONE );
assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE ); assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE );
assertTrue( s.getCurrentLockMode(s5)==LockMode.NONE );
s.lock(s1, LockMode.READ); //upgrade s.lock(s1, LockMode.READ); //upgrade
assertTrue( s.getCurrentLockMode(s1)==LockMode.READ ); assertTrue( s.getCurrentLockMode(s1)==LockMode.READ );
@ -1162,8 +1172,9 @@ public class ParentChildTest extends LegacyTestCase {
assertTrue( s.getCurrentLockMode(s1)==LockMode.NONE ); assertTrue( s.getCurrentLockMode(s1)==LockMode.NONE );
assertTrue( s.getCurrentLockMode(s2)==LockMode.NONE ); assertTrue( s.getCurrentLockMode(s2)==LockMode.NONE );
assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE ); assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE );
assertTrue( s.getCurrentLockMode(s5)==LockMode.NONE );
s.delete(s1); s.delete(s2); s.delete(s3); s.delete(s4); s.delete(s1); s.delete(s2); s.delete(s3); s.delete(s4); s.delete(s5);
tx.commit(); tx.commit();
s.close(); s.close();
} }

View File

@ -285,7 +285,10 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
throw new PersistenceException( "Unable to parse " + AvailableSettings.LOCK_TIMEOUT + ": " + lockTimeout ); throw new PersistenceException( "Unable to parse " + AvailableSettings.LOCK_TIMEOUT + ": " + lockTimeout );
} }
if ( timeoutSet ) { if ( timeoutSet ) {
if ( timeout < 0 ) { if ( timeout == LockOptions.SKIP_LOCKED ) {
options.setTimeOut( LockOptions.SKIP_LOCKED );
}
else if ( timeout < 0 ) {
options.setTimeOut( LockOptions.WAIT_FOREVER ); options.setTimeOut( LockOptions.WAIT_FOREVER );
} }
else if ( timeout == 0 ) { else if ( timeout == 0 ) {