clean up locking strategies
- remove duplicated code - handle UPGRADE_NOWAIT and UPGRADE_SKIPLOCKED as flavors of PessimisticWriteSelectLockingStrategy - improve Javadoc Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
1b67ebee60
commit
54d34a445c
|
@ -808,14 +808,28 @@ public interface Session extends SharedSessionContract, EntityManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the specified lock level on the given managed instance associated
|
* Obtain the specified lock level on the given managed instance associated
|
||||||
* with this session. This may be used to:
|
* with this session. This operation may be used to:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>perform a version check with {@link LockMode#READ}, or
|
* <li>perform a version check on an entity read from the second-level cache
|
||||||
* <li>upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}).
|
* by requesting {@link LockMode#READ},
|
||||||
|
* <li>schedule a version check at transaction commit by requesting
|
||||||
|
* {@link LockMode#OPTIMISTIC},
|
||||||
|
* <li>schedule a version increment at transaction commit by requesting
|
||||||
|
* {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}
|
||||||
|
* <li>upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_READ}
|
||||||
|
* or {@link LockMode#PESSIMISTIC_WRITE}, or
|
||||||
|
* <li>immediately increment the version of the given instance by requesting
|
||||||
|
* {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}.
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
|
* If the requested lock mode is already held on the given entity, this
|
||||||
|
* operation has no effect.
|
||||||
|
* <p>
|
||||||
* This operation cascades to associated instances if the association is
|
* This operation cascades to associated instances if the association is
|
||||||
* mapped with {@link org.hibernate.annotations.CascadeType#LOCK}.
|
* mapped with {@link org.hibernate.annotations.CascadeType#LOCK}.
|
||||||
|
* <p>
|
||||||
|
* The modes {@link LockMode#WRITE} and {@link LockMode#UPGRADE_SKIPLOCKED}
|
||||||
|
* are not legal arguments to {@code lock()}.
|
||||||
*
|
*
|
||||||
* @param object a persistent instance
|
* @param object a persistent instance
|
||||||
* @param lockMode the lock level
|
* @param lockMode the lock level
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class EntityVerifyVersionProcess implements BeforeTransactionCompletionPr
|
||||||
@Override
|
@Override
|
||||||
public void doBeforeTransactionCompletion(SessionImplementor session) {
|
public void doBeforeTransactionCompletion(SessionImplementor session) {
|
||||||
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
|
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
|
||||||
// Don't check version for an entity that is not in the PersistenceContext;
|
// Don't check version for an entity that is not in the PersistenceContext
|
||||||
if ( entry != null ) {
|
if ( entry != null ) {
|
||||||
final Object latestVersion = entry.getPersister().getCurrentVersion( entry.getId(), session );
|
final Object latestVersion = entry.getPersister().getCurrentVersion( entry.getId(), session );
|
||||||
if ( !entry.getVersion().equals( latestVersion ) ) {
|
if ( !entry.getVersion().equals( latestVersion ) ) {
|
||||||
|
|
|
@ -2018,16 +2018,21 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
switch ( lockMode ) {
|
switch ( lockMode ) {
|
||||||
case PESSIMISTIC_FORCE_INCREMENT:
|
case PESSIMISTIC_FORCE_INCREMENT:
|
||||||
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
|
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
|
||||||
|
case UPGRADE_NOWAIT:
|
||||||
|
case UPGRADE_SKIPLOCKED:
|
||||||
case PESSIMISTIC_WRITE:
|
case PESSIMISTIC_WRITE:
|
||||||
return new PessimisticWriteSelectLockingStrategy( lockable, lockMode );
|
return new PessimisticWriteSelectLockingStrategy( lockable, lockMode );
|
||||||
case PESSIMISTIC_READ:
|
case PESSIMISTIC_READ:
|
||||||
return new PessimisticReadSelectLockingStrategy( lockable, lockMode );
|
return new PessimisticReadSelectLockingStrategy( lockable, lockMode );
|
||||||
case OPTIMISTIC:
|
|
||||||
return new OptimisticLockingStrategy( lockable, lockMode );
|
|
||||||
case OPTIMISTIC_FORCE_INCREMENT:
|
case OPTIMISTIC_FORCE_INCREMENT:
|
||||||
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
|
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
|
||||||
default:
|
case OPTIMISTIC:
|
||||||
|
return new OptimisticLockingStrategy( lockable, lockMode );
|
||||||
|
case READ:
|
||||||
return new SelectLockingStrategy( lockable, lockMode );
|
return new SelectLockingStrategy( lockable, lockMode );
|
||||||
|
default:
|
||||||
|
// WRITE, NONE are not allowed here
|
||||||
|
throw new IllegalArgumentException( "Unsupported lock mode" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,29 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect.lock;
|
package org.hibernate.dialect.lock;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.StaleObjectStateException;
|
||||||
|
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.event.spi.EventSource;
|
||||||
import org.hibernate.persister.entity.Lockable;
|
import org.hibernate.persister.entity.Lockable;
|
||||||
|
import org.hibernate.sql.SimpleSelect;
|
||||||
|
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import static org.hibernate.pretty.MessageHelper.infoString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base {@link LockingStrategy} implementation to support implementations
|
* Base {@link LockingStrategy} implementation to support implementations
|
||||||
* based on issuing {@code SQL} {@code SELECT} statements
|
* based on issuing SQL {@code SELECT} statements. For non-read locks,
|
||||||
|
* this is achieved via the dialect's native {@code SELECT ... FOR UPDATE}
|
||||||
|
* syntax.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -35,19 +51,87 @@ public abstract class AbstractSelectLockingStrategy implements LockingStrategy {
|
||||||
return lockMode;
|
return lockMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String generateLockString(int lockTimeout);
|
protected String generateLockString(int lockTimeout) {
|
||||||
|
final SessionFactoryImplementor factory = lockable.getFactory();
|
||||||
|
final LockOptions lockOptions = new LockOptions( lockMode );
|
||||||
|
lockOptions.setTimeOut( lockTimeout );
|
||||||
|
final SimpleSelect select =
|
||||||
|
new SimpleSelect( factory )
|
||||||
|
.setLockOptions( lockOptions )
|
||||||
|
.setTableName( lockable.getRootTableName() )
|
||||||
|
.addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
|
||||||
|
.addRestriction( lockable.getRootTableIdentifierColumnNames() );
|
||||||
|
if ( lockable.isVersioned() ) {
|
||||||
|
select.addRestriction( lockable.getVersionColumnName() );
|
||||||
|
}
|
||||||
|
if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||||
|
select.setComment( lockMode + " lock " + lockable.getEntityName() );
|
||||||
|
}
|
||||||
|
return select.toStatementString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lock(Object id, Object version, Object object, int timeout, EventSource session)
|
||||||
|
throws StaleObjectStateException, JDBCException {
|
||||||
|
final String sql = determineSql( timeout );
|
||||||
|
final SessionFactoryImplementor factory = session.getFactory();
|
||||||
|
final Lockable lockable = getLockable();
|
||||||
|
try {
|
||||||
|
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||||
|
final PreparedStatement st = jdbcCoordinator.getStatementPreparer().prepareStatement( sql );
|
||||||
|
try {
|
||||||
|
lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
|
||||||
|
if ( lockable.isVersioned() ) {
|
||||||
|
lockable.getVersionType().nullSafeSet(
|
||||||
|
st,
|
||||||
|
version,
|
||||||
|
lockable.getIdentifierType().getColumnSpan( factory ) + 1,
|
||||||
|
session
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResultSet rs = jdbcCoordinator.getResultSetReturn().extract( st, sql );
|
||||||
|
try {
|
||||||
|
if ( !rs.next() ) {
|
||||||
|
final StatisticsImplementor statistics = factory.getStatistics();
|
||||||
|
if ( statistics.isStatisticsEnabled() ) {
|
||||||
|
statistics.optimisticFailure( lockable.getEntityName() );
|
||||||
|
}
|
||||||
|
throw new StaleObjectStateException( lockable.getEntityName(), id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( rs, st );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( st );
|
||||||
|
jdbcCoordinator.afterStatementExecution();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( SQLException sqle ) {
|
||||||
|
throw convertException( object, jdbcException( id, session, sqle, sql ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JDBCException jdbcException(Object id, EventSource session, SQLException sqle, String sql) {
|
||||||
|
return session.getJdbcServices().getSqlExceptionHelper()
|
||||||
|
.convert( sqle, "could not lock: " + infoString( lockable, id, session.getFactory() ), sql );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HibernateException convertException(Object entity, JDBCException ex) {
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
protected String determineSql(int timeout) {
|
protected String determineSql(int timeout) {
|
||||||
if ( timeout == LockOptions.WAIT_FOREVER) {
|
switch (timeout) {
|
||||||
|
case LockOptions.WAIT_FOREVER:
|
||||||
return waitForeverSql;
|
return waitForeverSql;
|
||||||
}
|
case LockOptions.NO_WAIT:
|
||||||
else if ( timeout == LockOptions.NO_WAIT) {
|
|
||||||
return getNoWaitSql();
|
return getNoWaitSql();
|
||||||
}
|
case LockOptions.SKIP_LOCKED:
|
||||||
else if ( timeout == LockOptions.SKIP_LOCKED) {
|
|
||||||
return getSkipLockedSql();
|
return getSkipLockedSql();
|
||||||
}
|
default:
|
||||||
else {
|
|
||||||
return generateLockString( timeout );
|
return generateLockString( timeout );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class OptimisticForceIncrementLockingStrategy implements LockingStrategy
|
||||||
if ( !lockable.isVersioned() ) {
|
if ( !lockable.isVersioned() ) {
|
||||||
throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
|
throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
|
||||||
}
|
}
|
||||||
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( object );
|
// final EntityEntry entry = session.getPersistenceContextInternal().getEntry( object );
|
||||||
// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.
|
// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.
|
||||||
session.getActionQueue().registerProcess( new EntityIncrementVersionProcess( object ) );
|
session.getActionQueue().registerProcess( new EntityIncrementVersionProcess( object ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,32 +6,18 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect.lock;
|
package org.hibernate.dialect.lock;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import org.hibernate.HibernateException;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.StaleObjectStateException;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.event.spi.EventSource;
|
|
||||||
import org.hibernate.persister.entity.Lockable;
|
import org.hibernate.persister.entity.Lockable;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.sql.SimpleSelect;
|
|
||||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pessimistic locking strategy where a lock is obtained via a
|
* A pessimistic locking strategy where {@link LockMode#PESSIMISTIC_READ}
|
||||||
* select statements.
|
* is obtained via a select statement.
|
||||||
* <p>
|
* <p>
|
||||||
* For non-read locks, this is achieved through the dialect's native
|
* Differs from {@link SelectLockingStrategy} in throwing
|
||||||
* {@code SELECT ... FOR UPDATE} syntax.
|
* {@link PessimisticEntityLockException}.
|
||||||
* <p>
|
|
||||||
* This strategy is valid for {@link LockMode#PESSIMISTIC_READ}.
|
|
||||||
* <p>
|
|
||||||
* This class is a clone of {@link SelectLockingStrategy}.
|
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
* @author Scott Marlow
|
* @author Scott Marlow
|
||||||
|
@ -53,68 +39,7 @@ public class PessimisticReadSelectLockingStrategy extends AbstractSelectLockingS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void lock(Object id, Object version, Object object, int timeout, EventSource session) {
|
protected HibernateException convertException(Object entity, JDBCException ex) {
|
||||||
final String sql = determineSql( timeout );
|
return new PessimisticEntityLockException( entity, "could not obtain pessimistic lock", ex );
|
||||||
final SessionFactoryImplementor factory = session.getFactory();
|
|
||||||
try {
|
|
||||||
final Lockable lockable = getLockable();
|
|
||||||
try {
|
|
||||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
|
||||||
final PreparedStatement st = jdbcCoordinator.getStatementPreparer().prepareStatement( sql );
|
|
||||||
try {
|
|
||||||
lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
|
|
||||||
if ( lockable.isVersioned() ) {
|
|
||||||
lockable.getVersionType().nullSafeSet(
|
|
||||||
st,
|
|
||||||
version,
|
|
||||||
lockable.getIdentifierType().getColumnSpan( factory ) + 1,
|
|
||||||
session
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ResultSet rs = jdbcCoordinator.getResultSetReturn().extract( st, sql );
|
|
||||||
if ( !rs.next() ) {
|
|
||||||
final StatisticsImplementor statistics = factory.getStatistics();
|
|
||||||
if ( statistics.isStatisticsEnabled() ) {
|
|
||||||
statistics.optimisticFailure( lockable.getEntityName() );
|
|
||||||
}
|
|
||||||
throw new StaleObjectStateException( lockable.getEntityName(), id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( st );
|
|
||||||
jdbcCoordinator.afterStatementExecution();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ( SQLException e ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
e,
|
|
||||||
"could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
|
|
||||||
sql
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JDBCException e) {
|
|
||||||
throw new PessimisticEntityLockException( object, "could not obtain pessimistic lock", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String generateLockString(int lockTimeout) {
|
|
||||||
final SessionFactoryImplementor factory = getLockable().getFactory();
|
|
||||||
final LockOptions lockOptions = new LockOptions( getLockMode() );
|
|
||||||
lockOptions.setTimeOut( lockTimeout );
|
|
||||||
final SimpleSelect select = new SimpleSelect( factory )
|
|
||||||
.setLockOptions( lockOptions )
|
|
||||||
.setTableName( getLockable().getRootTableName() )
|
|
||||||
.addColumn( getLockable().getRootTableIdentifierColumnNames()[0] )
|
|
||||||
.addRestriction( getLockable().getRootTableIdentifierColumnNames() );
|
|
||||||
if ( getLockable().isVersioned() ) {
|
|
||||||
select.addRestriction( getLockable().getVersionColumnName() );
|
|
||||||
}
|
|
||||||
if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) {
|
|
||||||
select.setComment( getLockMode() + " lock " + getLockable().getEntityName() );
|
|
||||||
}
|
|
||||||
return select.toStatementString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,32 +6,18 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect.lock;
|
package org.hibernate.dialect.lock;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import org.hibernate.HibernateException;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.StaleObjectStateException;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.event.spi.EventSource;
|
|
||||||
import org.hibernate.persister.entity.Lockable;
|
import org.hibernate.persister.entity.Lockable;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.sql.SimpleSelect;
|
|
||||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pessimistic locking strategy where a lock is obtained via a
|
* A pessimistic locking strategy where {@link LockMode#PESSIMISTIC_WRITE}
|
||||||
* select statement.
|
* lock is obtained via a select statement.
|
||||||
* <p>
|
* <p>
|
||||||
* For non-read locks, this is achieved through the dialect's native
|
* Differs from {@link SelectLockingStrategy} in throwing
|
||||||
* {@code SELECT ... FOR UPDATE} syntax.
|
* {@link PessimisticEntityLockException}.
|
||||||
* <p>
|
|
||||||
* This strategy is valid for {@link LockMode#PESSIMISTIC_WRITE}.
|
|
||||||
* <p>
|
|
||||||
* This class is a clone of {@link SelectLockingStrategy}.
|
|
||||||
*
|
*
|
||||||
* @see org.hibernate.dialect.Dialect#getForUpdateString(LockMode)
|
* @see org.hibernate.dialect.Dialect#getForUpdateString(LockMode)
|
||||||
* @see org.hibernate.dialect.Dialect#appendLockHint(LockOptions, String)
|
* @see org.hibernate.dialect.Dialect#appendLockHint(LockOptions, String)
|
||||||
|
@ -52,72 +38,7 @@ public class PessimisticWriteSelectLockingStrategy extends AbstractSelectLocking
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void lock(Object id, Object version, Object object, int timeout, EventSource session) {
|
protected HibernateException convertException(Object entity, JDBCException ex) {
|
||||||
final String sql = determineSql( timeout );
|
return new PessimisticEntityLockException( entity, "could not obtain pessimistic lock", ex );
|
||||||
final SessionFactoryImplementor factory = session.getFactory();
|
|
||||||
try {
|
|
||||||
final Lockable lockable = getLockable();
|
|
||||||
try {
|
|
||||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
|
||||||
final PreparedStatement st = jdbcCoordinator.getStatementPreparer().prepareStatement( sql );
|
|
||||||
try {
|
|
||||||
lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
|
|
||||||
if ( lockable.isVersioned() ) {
|
|
||||||
lockable.getVersionType().nullSafeSet(
|
|
||||||
st,
|
|
||||||
version,
|
|
||||||
lockable.getIdentifierType().getColumnSpan( factory ) + 1,
|
|
||||||
session
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ResultSet rs = jdbcCoordinator.getResultSetReturn().extract( st, sql );
|
|
||||||
try {
|
|
||||||
if ( !rs.next() ) {
|
|
||||||
final StatisticsImplementor statistics = factory.getStatistics();
|
|
||||||
if ( statistics.isStatisticsEnabled() ) {
|
|
||||||
statistics.optimisticFailure( lockable.getEntityName() );
|
|
||||||
}
|
|
||||||
throw new StaleObjectStateException( lockable.getEntityName(), id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( rs, st );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( st );
|
|
||||||
jdbcCoordinator.afterStatementExecution();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( SQLException e ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
e,
|
|
||||||
"could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
|
|
||||||
sql
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JDBCException e) {
|
|
||||||
throw new PessimisticEntityLockException( object, "could not obtain pessimistic lock", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String generateLockString(int lockTimeout) {
|
|
||||||
final SessionFactoryImplementor factory = getLockable().getFactory();
|
|
||||||
final LockOptions lockOptions = new LockOptions( getLockMode() );
|
|
||||||
lockOptions.setTimeOut( lockTimeout );
|
|
||||||
final SimpleSelect select = new SimpleSelect( factory )
|
|
||||||
.setLockOptions( lockOptions )
|
|
||||||
.setTableName( getLockable().getRootTableName() )
|
|
||||||
.addColumn( getLockable().getRootTableIdentifierColumnNames()[0] )
|
|
||||||
.addRestriction( getLockable().getRootTableIdentifierColumnNames() );
|
|
||||||
if ( getLockable().isVersioned() ) {
|
|
||||||
select.addRestriction( getLockable().getVersionColumnName() );
|
|
||||||
}
|
|
||||||
if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) {
|
|
||||||
select.setComment( getLockMode() + " lock " + getLockable().getEntityName() );
|
|
||||||
}
|
|
||||||
return select.toStatementString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,27 +6,19 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect.lock;
|
package org.hibernate.dialect.lock;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import org.hibernate.HibernateException;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.StaleObjectStateException;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.event.spi.EventSource;
|
|
||||||
import org.hibernate.persister.entity.Lockable;
|
import org.hibernate.persister.entity.Lockable;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
|
||||||
import org.hibernate.sql.SimpleSelect;
|
|
||||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A locking strategy where a lock is obtained via a select statement.
|
* A locking strategy where an optimistic lock is obtained via a select
|
||||||
|
* statement.
|
||||||
* <p>
|
* <p>
|
||||||
* For non-read locks, this is achieved through the dialect's native
|
* Differs from {@link PessimisticWriteSelectLockingStrategy} and
|
||||||
* {@code SELECT ... FOR UPDATE} syntax.
|
* {@link PessimisticReadSelectLockingStrategy} in throwing
|
||||||
|
* {@link OptimisticEntityLockException}.
|
||||||
*
|
*
|
||||||
* @see org.hibernate.dialect.Dialect#getForUpdateString(LockMode)
|
* @see org.hibernate.dialect.Dialect#getForUpdateString(LockMode)
|
||||||
* @see org.hibernate.dialect.Dialect#appendLockHint(LockOptions, String)
|
* @see org.hibernate.dialect.Dialect#appendLockHint(LockOptions, String)
|
||||||
|
@ -46,73 +38,7 @@ public class SelectLockingStrategy extends AbstractSelectLockingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void lock(
|
protected HibernateException convertException(Object entity, JDBCException ex) {
|
||||||
Object id,
|
return new OptimisticEntityLockException( entity, "could not obtain optimistic lock", ex );
|
||||||
Object version,
|
|
||||||
Object object,
|
|
||||||
int timeout,
|
|
||||||
EventSource session) throws StaleObjectStateException, JDBCException {
|
|
||||||
final String sql = determineSql( timeout );
|
|
||||||
final SessionFactoryImplementor factory = session.getFactory();
|
|
||||||
final Lockable lockable = getLockable();
|
|
||||||
try {
|
|
||||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
|
||||||
final PreparedStatement st = jdbcCoordinator.getStatementPreparer().prepareStatement( sql );
|
|
||||||
try {
|
|
||||||
lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
|
|
||||||
if ( lockable.isVersioned() ) {
|
|
||||||
lockable.getVersionType().nullSafeSet(
|
|
||||||
st,
|
|
||||||
version,
|
|
||||||
lockable.getIdentifierType().getColumnSpan( factory ) + 1,
|
|
||||||
session
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ResultSet rs = jdbcCoordinator.getResultSetReturn().extract( st, sql );
|
|
||||||
try {
|
|
||||||
if ( !rs.next() ) {
|
|
||||||
final StatisticsImplementor statistics = factory.getStatistics();
|
|
||||||
if ( statistics.isStatisticsEnabled() ) {
|
|
||||||
statistics.optimisticFailure( lockable.getEntityName() );
|
|
||||||
}
|
|
||||||
throw new StaleObjectStateException( lockable.getEntityName(), id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( rs, st );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( st );
|
|
||||||
jdbcCoordinator.afterStatementExecution();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch ( SQLException sqle ) {
|
|
||||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
|
||||||
sqle,
|
|
||||||
"could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
|
|
||||||
sql
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String generateLockString(int timeout) {
|
|
||||||
final SessionFactoryImplementor factory = getLockable().getFactory();
|
|
||||||
final LockOptions lockOptions = new LockOptions( getLockMode() );
|
|
||||||
lockOptions.setTimeOut( timeout );
|
|
||||||
final SimpleSelect select = new SimpleSelect( factory )
|
|
||||||
.setLockOptions( lockOptions )
|
|
||||||
.setTableName( getLockable().getRootTableName() )
|
|
||||||
.addColumn( getLockable().getRootTableIdentifierColumnNames()[0] )
|
|
||||||
.addRestriction( getLockable().getRootTableIdentifierColumnNames() );
|
|
||||||
if ( getLockable().isVersioned() ) {
|
|
||||||
select.addRestriction( getLockable().getVersionColumnName() );
|
|
||||||
}
|
|
||||||
if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) {
|
|
||||||
select.setComment( getLockMode() + " lock " + getLockable().getEntityName() );
|
|
||||||
}
|
|
||||||
return select.toStatementString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,8 +51,7 @@ public class LoaderHelper {
|
||||||
public static void upgradeLock(Object object, EntityEntry entry, LockOptions lockOptions, EventSource session) {
|
public static void upgradeLock(Object object, EntityEntry entry, LockOptions lockOptions, EventSource session) {
|
||||||
final LockMode requestedLockMode = lockOptions.getLockMode();
|
final LockMode requestedLockMode = lockOptions.getLockMode();
|
||||||
if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) {
|
if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) {
|
||||||
// The user requested a "greater" (i.e. more restrictive) form of
|
// Request is for a more restrictive lock than the lock already held
|
||||||
// pessimistic lock
|
|
||||||
|
|
||||||
if ( entry.getStatus().isDeletedOrGone()) {
|
if ( entry.getStatus().isDeletedOrGone()) {
|
||||||
throw new ObjectDeletedException(
|
throw new ObjectDeletedException(
|
||||||
|
|
Loading…
Reference in New Issue