improve javadoc for LockModes, and deprecate LockOptions.SKIP_LOCKED

Use LockMode.UPGRADE_SKIPLOCKED instead of setting the timeout to -2.
This commit is contained in:
Gavin King 2022-11-07 13:34:58 +01:00
parent 02ad34091c
commit 9d141a2793
7 changed files with 126 additions and 55 deletions

View File

@ -12,87 +12,136 @@ import jakarta.persistence.LockModeType;
/**
* Instances represent a lock mode for a row of a relational
* database table. It is not intended that users spend much
* time worrying about locking since Hibernate usually
* obtains exactly the right lock level automatically.
* Some "advanced" users may wish to explicitly specify lock
* levels.
* database table. It is not intended that users spend much time
* worrying about locking since Hibernate usually obtains exactly
* the right lock level automatically. Some "advanced" users may
* wish to explicitly specify lock levels.
* <p>
* This enumeration of lock modes competes with the JPA-defined
* {@link LockModeType}, but offers additional options, including
* {@link #UPGRADE_NOWAIT} and {@link #UPGRADE_SKIPLOCKED}.
*
* @author Gavin King
*
* @see Session#lock(Object, LockMode)
* @see LockModeType
* @see LockOptions
*/
public enum LockMode {
/**
* No lock required. If an object is requested with this
* lock mode, a {@link #READ} lock will be obtained if it
* is necessary to actually read the state from the database,
* No lock required. If an object is requested with this lock
* mode, a {@link #READ} lock will be obtained if it turns out
* to be necessary to actually read the state from the database,
* rather than pull it from a cache.
* <p>
* This is the "default" lock mode.
* This is the "default" lock mode, the mode requested by calling
* {@link Session#get(Class, Object)} without passing an explicit
* mode. It permits the state of an object to be retrieved from
* the cache without the cost of database access.
*
* @see LockModeType#NONE
*/
NONE( 0, "none" ),
/**
* A shared lock. Objects in this lock mode were read from
* the database in the current transaction, rather than being
* pulled from a cache.
* A shared lock. Objects in this lock mode were read from the
* database in the current transaction, rather than being pulled
* from a cache.
* <p>
* Note that this lock mode is the same as the JPA-defined modes
* {@link LockModeType#READ} and {@link LockModeType#OPTIMISTIC}.
*
* @see LockModeType#OPTIMISTIC
*/
READ( 5, "read" ),
/**
* Optimistically assume that transaction will not experience
* contention for an entity. The version will be verified near
* the transaction end.
* A shared optimistic lock. Assumes that the current transaction
* will not experience contention for the state of an entity. The
* version will be checked near the end of the transaction, to
* verify that this was indeed the case.
* <p>
* Note that, despite the similar names this lock mode is not the
* same as the JPA-defined mode {@link LockModeType#OPTIMISTIC}.
*/
OPTIMISTIC( 6, "optimistic" ),
/**
* Optimistically assume that transaction will not experience
* contention for an entity. The version will be verified and
* incremented near the transaction end.
* A kind of exclusive optimistic lock. Assumes that the current
* transaction will not experience contention for the state of an
* entity. The version will be checked and incremented near the
* end of the transaction, to verify that this was indeed the
* case, and to signal to concurrent optimistic readers that their
* optimistic locks have failed.
*
* @see LockModeType#OPTIMISTIC_FORCE_INCREMENT
*/
OPTIMISTIC_FORCE_INCREMENT( 7, "optimistic_force_increment" ),
/**
* A {@code WRITE} lock is obtained when an object is updated
* or inserted.
* An exclusive write lock. Objects in this lock mode were updated
* or inserted in the database in the current transaction.
* <p>
* This lock mode is for internal use only and is not a valid
* argument to {@code load()} or {@code lock()}. These method
* throw an exception if {@code WRITE} given as an argument.
* This lock mode is for internal use only and is not a legal
* argument to {@link Session#get(Class, Object, LockMode)},
* {@link Session#refresh(Object, LockMode)}, or
* {@link Session#lock(Object, LockMode)}. These methods throw
* an exception if {@code WRITE} is given as an argument.
* <p>
* Note that, despite the similar names, this lock mode is not
* the same as the JPA-defined mode {@link LockModeType#WRITE}.
*/
@Internal
WRITE( 10, "write" ),
/**
* Attempt to obtain an upgrade lock, using an Oracle-style
* A pessimistic upgrade lock, obtained using an Oracle-style
* {@code select for update nowait}. The semantics of this
* lock mode, once obtained, are the same as
* {@link #PESSIMISTIC_WRITE}.
* lock mode, if the lock is successfully obtained, are the same
* as {@link #PESSIMISTIC_WRITE}. If the lock is not immediately
* available, an exception occurs.
*/
UPGRADE_NOWAIT( 10, "upgrade-nowait" ),
/**
* Attempt to obtain an upgrade lock, using an Oracle-style
* {@code select for update skip locked}. The semantics of
* this lock mode, once obtained, are the same as
* {@link #PESSIMISTIC_WRITE}.
* A pessimistic upgrade lock, obtained using an Oracle-style
* {@code select for update skip locked}. The semantics of this
* lock mode, if the lock is successfully obtained, are the same
* as {@link #PESSIMISTIC_WRITE}. But if the lock is not
* immediately available, no exception occurs, but the locked
* row is not returned from the database.
*/
UPGRADE_SKIPLOCKED( 10, "upgrade-skiplocked" ),
/**
* Implemented as {@link #PESSIMISTIC_WRITE}.
* A pessimistic shared lock, which prevents concurrent
* transactions from writing the locked object. Obtained via
* a {@code select for share} statement in dialects where this
* syntax is supported, and via {@code select for update} in
* other dialects.
* <p>
* On databases which do not support {@code for share}, this
* lock mode is equivalent to {@link #PESSIMISTIC_WRITE}.
*
* @see LockModeType#PESSIMISTIC_READ
*/
PESSIMISTIC_READ( 12, "pessimistic_read" ),
/**
* Transaction will obtain a database lock immediately.
* A pessimistic upgrade lock, which prevents concurrent
* transactions from reading or writing the locked object.
* Obtained via a {@code select for update} statement.
*
* @see LockModeType#PESSIMISTIC_WRITE
*/
PESSIMISTIC_WRITE( 13, "pessimistic_write" ),
/**
* Transaction will immediately increment the entity version.
* A pessimistic write lock which immediately increments
* the version of the locked object. Obtained by immediate
* execution of an {@code update} statement.
*
* @see LockModeType#PESSIMISTIC_FORCE_INCREMENT
*/
PESSIMISTIC_FORCE_INCREMENT( 17, "pessimistic_force_increment" );

View File

@ -66,7 +66,8 @@ public class LockOptions implements Serializable {
/**
* Indicates that the database should not wait at all to acquire
* a pessimistic lock which is not immediately available.
* a pessimistic lock which is not immediately available. This
* has the same effect as {@link LockMode#UPGRADE_NOWAIT}.
*
* @see #getTimeOut
*/
@ -85,7 +86,10 @@ public class LockOptions implements Serializable {
* Indicates that rows which are already locked should be skipped.
*
* @see #getTimeOut()
*
* @deprecated use {@link LockMode#UPGRADE_SKIPLOCKED}
*/
@Deprecated
public static final int SKIP_LOCKED = -2;
private LockMode lockMode = LockMode.NONE;

View File

@ -742,6 +742,9 @@ public interface Session extends SharedSessionContract, EntityManager {
* </ul>
* This operation cascades to associated instances if the association is mapped
* with {@link jakarta.persistence.CascadeType#REFRESH}.
* <p>
* This operation requests {@link LockMode#READ}. To obtain a stronger lock,
* call {@link #refresh(Object, LockMode)}.
*
* @param object a persistent or detached instance
*/
@ -844,9 +847,24 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized instance.
* with the session, return that instance. This method never returns an uninitialized
* instance.
* <p>
* This operation is very similar to {@link #find(Class, Object)}.
* <p>
* This operation requests {@link LockMode#NONE}, that is, no lock, allowing the object
* to be retrieved from the cache without the cost of database access. However, if it is
* necessary to read the state from the database, the object will be returned with the
* lock mode {@link LockMode#READ}.
* <p>
* To bypass the second-level cache, and ensure that the state is read from the database,
* either:
* <ul>
* <li>call {@link #get(Class, Object, LockMode)} with the explicit lock mode
* {@link LockMode#READ}, or
* <li>{@linkplain #setCacheMode(CacheMode) set the cache mode} to {@link CacheMode#IGNORE}
* before calling this method.
* </ul>
*
* @param entityType the entity type
* @param id an identifier
@ -858,8 +876,8 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized instance.
* Obtain the specified lock mode if the instance exists.
* with the session, return that instance. This method never returns an uninitialized
* instance. Obtain the specified lock mode if the instance exists.
* <p>
* Convenient form of {@link #get(Class, Object, LockOptions)}.
* <p>
@ -878,8 +896,8 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized instance.
* Obtain the specified lock mode if the instance exists.
* with the session, return that instance. This method never returns an uninitialized
* instance. Obtain the specified lock mode if the instance exists.
*
* @param entityType the entity type
* @param id an identifier
@ -892,7 +910,8 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* Return the persistent instance of the given named entity with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized instance.
* with the session, return that instance. This method never returns an uninitialized
* instance.
*
* @param entityName the entity name
* @param id an identifier
@ -903,9 +922,9 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. (If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized instance.)
* Obtain the specified lock mode if the instance exists.
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized
* instance. Obtain the specified lock mode if the instance exists.
* <p/>
* Convenient form of {@link #get(String, Object, LockOptions)}
*
@ -922,8 +941,8 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* Return the persistent instance of the given entity class with the given identifier,
* or null if there is no such persistent instance. If the instance is already associated
* with the session, return that instance. This method never returns an uninitialized instance.
* Obtain the specified lock mode if the instance exists.
* with the session, return that instance. This method never returns an uninitialized
* instance. Obtain the specified lock mode if the instance exists.
*
* @param entityName the entity name
* @param id an identifier
@ -1235,7 +1254,8 @@ public interface Session extends SharedSessionContract, EntityManager {
/**
* A timeout value indicating that the database should not
* wait at all to acquire a pessimistic lock which is not
* immediately available.
* immediately available. This has the same effect as
* {@link LockMode#UPGRADE_NOWAIT}.
*/
int PESSIMISTIC_NO_WAIT = LockOptions.NO_WAIT;

View File

@ -1874,10 +1874,10 @@ public abstract class Dialect implements ConversionContext {
}
/**
* Get the {@code FOR UPDATE OF column_list} fragment appropriate for this
* dialect given the aliases of the columns to be write locked.
* Get the {@code FOR UPDATE OF} or {@code FOR SHARE OF} fragment appropriate
* for this dialect given the aliases of the columns to be locked.
*
* @param aliases The columns to be write locked.
* @param aliases The columns to be locked.
* @param lockOptions the lock options to apply
* @return The appropriate {@code FOR UPDATE OF column_list} clause string.
*/

View File

@ -691,9 +691,7 @@ public class PostgreSQLDialect extends Dialect {
@Override
public String getForUpdateString(String aliases, LockOptions lockOptions) {
/*
* Parent's implementation for (aliases, lockOptions) ignores aliases.
*/
// parent's implementation for (aliases, lockOptions) ignores aliases
if ( aliases.isEmpty() ) {
LockMode lockMode = lockOptions.getLockMode();
for ( Map.Entry<String, LockMode> entry : lockOptions.getAliasSpecificLocks() ) {

View File

@ -19,7 +19,7 @@ public class RefreshEvent extends AbstractEvent {
private final Object object;
private String entityName;
private LockOptions lockOptions = new LockOptions().setLockMode(LockMode.READ);
private LockOptions lockOptions = new LockOptions(LockMode.READ);
public RefreshEvent(Object object, EventSource source) {
super(source);

View File

@ -43,7 +43,7 @@ public class SimpleSelect {
protected LockOptions lockOptions = new LockOptions( LockMode.READ );
private Dialect dialect;
private final Dialect dialect;
public SimpleSelect addColumns(String[] columnNames, String[] columnAliases) {
@ -189,7 +189,7 @@ public class SimpleSelect {
}
if ( lockOptions != null ) {
buf = new StringBuilder(dialect.applyLocksToSql( buf.toString(), lockOptions, null ) );
buf = new StringBuilder( dialect.applyLocksToSql( buf.toString(), lockOptions, null ) );
}
return dialect.transformSelectString( buf.toString() );