HHH-6736 Added support for SELECT ... FOR UPDATE SKIP LOCKED
This commit is contained in:
parent
59bb86978e
commit
e0cfc6bf2e
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -254,10 +254,11 @@ 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 );
|
||||||
|
if ( lockOptions.getLockMode() != LockMode.UPGRADE_SKIPLOCKED ) {
|
||||||
|
LOG.usingFollowOnLocking();
|
||||||
lockOptions.setTimeOut( parameters.getLockOptions().getTimeOut() );
|
lockOptions.setTimeOut( parameters.getLockOptions().getTimeOut() );
|
||||||
lockOptions.setScope( parameters.getLockOptions().getScope() );
|
lockOptions.setScope( parameters.getLockOptions().getScope() );
|
||||||
afterLoadActions.add(
|
afterLoadActions.add(
|
||||||
|
@ -271,6 +272,7 @@ public abstract class Loader {
|
||||||
parameters.setLockOptions( new LockOptions() );
|
parameters.setLockOptions( new LockOptions() );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,10 +205,11 @@ public class CriteriaLoader extends OuterJoinLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( dialect.useFollowOnLocking() ) {
|
if ( dialect.useFollowOnLocking() ) {
|
||||||
|
final LockMode lockMode = determineFollowOnLockMode( lockOptions );
|
||||||
|
if( lockMode != LockMode.UPGRADE_SKIPLOCKED ) {
|
||||||
// Dialect prefers to perform locking in a separate step
|
// Dialect prefers to perform locking in a separate step
|
||||||
LOG.usingFollowOnLocking();
|
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() );
|
||||||
|
@ -225,7 +226,7 @@ public class CriteriaLoader extends OuterJoinLoader {
|
||||||
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());
|
||||||
|
|
|
@ -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 ?
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
|
Loading…
Reference in New Issue