HHH-15682 fix potential classloading deadlock
+ add some documentation around follow-on locking
This commit is contained in:
parent
85836fbcf8
commit
39bef7bc70
|
@ -12,12 +12,14 @@ import java.util.Iterator;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.PessimisticLockScope;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.unmodifiableSet;
|
||||
|
||||
/**
|
||||
* Contains a set of options describing how a row of a database table
|
||||
|
@ -40,6 +42,11 @@ import static java.util.Collections.emptyList;
|
|||
* more granular fashion, with the option to specify a lock mode that
|
||||
* {@linkplain #setAliasSpecificLockMode(String, LockMode) applies
|
||||
* only to a certain query alias}.
|
||||
* <p>
|
||||
* Finally, the use of follow-on locking may be force enabled or disabled,
|
||||
* overriding the {@linkplain org.hibernate.dialect.Dialect#useFollowOnLocking
|
||||
* default behavior of the SQL dialect} by passing a non-null argument
|
||||
* to {@link #setFollowOnLocking(Boolean)}.
|
||||
*
|
||||
* @author Scott Marlow
|
||||
*/
|
||||
|
@ -48,13 +55,13 @@ public class LockOptions implements Serializable {
|
|||
* Represents {@link LockMode#NONE}, to which timeout and scope are
|
||||
* not applicable.
|
||||
*/
|
||||
public static final LockOptions NONE = new ImmutableLockOptions(LockMode.NONE);
|
||||
public static final LockOptions NONE = new LockOptions(true, LockMode.NONE);
|
||||
|
||||
/**
|
||||
* Represents {@link LockMode#READ}, to which timeout and scope are
|
||||
* not applicable.
|
||||
*/
|
||||
public static final LockOptions READ = new ImmutableLockOptions(LockMode.READ);
|
||||
public static final LockOptions READ = new LockOptions(true, LockMode.READ);
|
||||
|
||||
/**
|
||||
* Represents {@link LockMode#PESSIMISTIC_WRITE} with
|
||||
|
@ -62,7 +69,7 @@ public class LockOptions implements Serializable {
|
|||
* {@linkplain PessimisticLockScope#NORMAL no extension of the
|
||||
* lock to owned collections}.
|
||||
*/
|
||||
public static final LockOptions UPGRADE = new ImmutableLockOptions(LockMode.PESSIMISTIC_WRITE);
|
||||
public static final LockOptions UPGRADE = new LockOptions(true, LockMode.PESSIMISTIC_WRITE);
|
||||
|
||||
/**
|
||||
* Indicates that the database should not wait at all to acquire
|
||||
|
@ -86,44 +93,49 @@ 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;
|
||||
private int timeout = WAIT_FOREVER;
|
||||
private final boolean immutable;
|
||||
private LockMode lockMode;
|
||||
private int timeout;
|
||||
private boolean scope;
|
||||
|
||||
private Map<String,LockMode> aliasSpecificLockModes;
|
||||
|
||||
private Boolean followOnLocking;
|
||||
private Map<String, LockMode> aliasSpecificLockModes;
|
||||
|
||||
/**
|
||||
* Construct an instance with mode {@link LockMode#NONE} and
|
||||
* timeout {@link #WAIT_FOREVER}.
|
||||
*/
|
||||
public LockOptions() {
|
||||
immutable = false;
|
||||
lockMode = LockMode.NONE;
|
||||
timeout = WAIT_FOREVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance with the given {@linkplain LockMode mode}
|
||||
* and {@link #WAIT_FOREVER}.
|
||||
*
|
||||
* @param lockMode The lock mode to use
|
||||
* @param lockMode The initial lock mode
|
||||
*/
|
||||
public LockOptions(LockMode lockMode) {
|
||||
immutable = false;
|
||||
this.lockMode = lockMode;
|
||||
timeout = WAIT_FOREVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance with the given {@linkplain LockMode mode}
|
||||
* and timeout.
|
||||
*
|
||||
* @param lockMode The lock mode to use
|
||||
* @param lockMode The initial lock mode
|
||||
* @param timeout The initial timeout
|
||||
*/
|
||||
public LockOptions(LockMode lockMode, int timeout) {
|
||||
immutable = false;
|
||||
this.lockMode = lockMode;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
@ -132,14 +144,25 @@ public class LockOptions implements Serializable {
|
|||
* Construct an instance with the given {@linkplain LockMode mode},
|
||||
* timeout, and {@link PessimisticLockScope scope}.
|
||||
*
|
||||
* @param lockMode The lock mode to use
|
||||
* @param lockMode The initial lock mode
|
||||
* @param timeout The initial timeout
|
||||
* @param scope The initial lock scope
|
||||
*/
|
||||
public LockOptions(LockMode lockMode, int timeout, PessimisticLockScope scope) {
|
||||
immutable = false;
|
||||
this.lockMode = lockMode;
|
||||
this.timeout = timeout;
|
||||
this.scope = scope == PessimisticLockScope.EXTENDED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal operation used to create immutable global instances.
|
||||
*/
|
||||
private LockOptions(boolean immutable, LockMode lockMode) {
|
||||
this.immutable = immutable;
|
||||
this.lockMode = lockMode;
|
||||
timeout = WAIT_FOREVER;
|
||||
}
|
||||
/**
|
||||
* Determine of the lock options are empty.
|
||||
*
|
||||
|
@ -171,6 +194,9 @@ public class LockOptions implements Serializable {
|
|||
* @return {@code this} for method chaining
|
||||
*/
|
||||
public LockOptions setLockMode(LockMode lockMode) {
|
||||
if ( immutable ) {
|
||||
throw new UnsupportedOperationException("immutable global instance of LockMode");
|
||||
}
|
||||
this.lockMode = lockMode;
|
||||
return this;
|
||||
}
|
||||
|
@ -185,6 +211,9 @@ public class LockOptions implements Serializable {
|
|||
* @see Query#setLockMode(String, LockMode)
|
||||
*/
|
||||
public LockOptions setAliasSpecificLockMode(String alias, LockMode lockMode) {
|
||||
if ( immutable ) {
|
||||
throw new UnsupportedOperationException("immutable global instance of LockMode");
|
||||
}
|
||||
if ( aliasSpecificLockModes == null ) {
|
||||
aliasSpecificLockModes = new LinkedHashMap<>();
|
||||
}
|
||||
|
@ -240,8 +269,7 @@ public class LockOptions implements Serializable {
|
|||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean hasAliasSpecificLockModes() {
|
||||
return aliasSpecificLockModes != null
|
||||
&& ! aliasSpecificLockModes.isEmpty();
|
||||
return aliasSpecificLockModes != null && !aliasSpecificLockModes.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,13 +294,14 @@ public class LockOptions implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Iterable with {@link Map.Entry}s, each containing an alias and its
|
||||
* Set of {@link Map.Entry}s, each associating an alias with its
|
||||
* specified {@linkplain #setAliasSpecificLockMode alias-specific}
|
||||
* {@link LockMode}.
|
||||
*
|
||||
* @return an iterable with the {@link Map.Entry}s
|
||||
*/
|
||||
public Iterable<Map.Entry<String,LockMode>> getAliasSpecificLocks() {
|
||||
return aliasSpecificLockModes == null ? emptyList() : aliasSpecificLockModes.entrySet();
|
||||
public Set<Map.Entry<String,LockMode>> getAliasSpecificLocks() {
|
||||
return aliasSpecificLockModes == null ? emptySet() : unmodifiableSet( aliasSpecificLockModes.entrySet() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,6 +357,9 @@ public class LockOptions implements Serializable {
|
|||
* @see #getTimeOut
|
||||
*/
|
||||
public LockOptions setTimeOut(int timeout) {
|
||||
if ( immutable ) {
|
||||
throw new UnsupportedOperationException("immutable global instance of LockMode");
|
||||
}
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
@ -360,6 +392,9 @@ public class LockOptions implements Serializable {
|
|||
* @return {@code this} for method chaining
|
||||
*/
|
||||
public LockOptions setLockScope(PessimisticLockScope scope) {
|
||||
if ( immutable ) {
|
||||
throw new UnsupportedOperationException("immutable global instance of LockMode");
|
||||
}
|
||||
return setScope(scope==PessimisticLockScope.EXTENDED);
|
||||
}
|
||||
|
||||
|
@ -397,15 +432,24 @@ public class LockOptions implements Serializable {
|
|||
*/
|
||||
@Deprecated(since = "6.2")
|
||||
public LockOptions setScope(boolean scope) {
|
||||
if ( immutable ) {
|
||||
throw new UnsupportedOperationException("immutable global instance of LockMode");
|
||||
}
|
||||
this.scope = scope;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current follow-on locking setting.
|
||||
* Returns a value indicating if follow-on locking was force
|
||||
* enabled or disabled, overriding the default behavior of
|
||||
* the SQL dialect.
|
||||
*
|
||||
* @return {@code true} if follow-on locking is enabled
|
||||
* @return {@code true} if follow-on locking was force enabled,
|
||||
* {@code false} if follow-on locking was force disabled,
|
||||
* or {@code null} if the default behavior of the dialect
|
||||
* has not been overridden.
|
||||
*
|
||||
* @see org.hibernate.jpa.HibernateHints#HINT_FOLLOW_ON_LOCKING
|
||||
* @see org.hibernate.dialect.Dialect#useFollowOnLocking(String, QueryOptions)
|
||||
*/
|
||||
public Boolean getFollowOnLocking() {
|
||||
|
@ -413,14 +457,19 @@ public class LockOptions implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the follow-on locking setting.
|
||||
* Force enable or disable the use of follow-on locking,
|
||||
* overriding the default behavior of the SQL dialect.
|
||||
*
|
||||
* @param followOnLocking The new follow-on locking setting
|
||||
* @return {@code this} for method chaining
|
||||
*
|
||||
* @see org.hibernate.jpa.HibernateHints#HINT_FOLLOW_ON_LOCKING
|
||||
* @see org.hibernate.dialect.Dialect#useFollowOnLocking(String, QueryOptions)
|
||||
*/
|
||||
public LockOptions setFollowOnLocking(Boolean followOnLocking) {
|
||||
if ( immutable ) {
|
||||
throw new UnsupportedOperationException("immutable global instance of LockMode");
|
||||
}
|
||||
this.followOnLocking = followOnLocking;
|
||||
return this;
|
||||
}
|
||||
|
@ -492,40 +541,4 @@ public class LockOptions implements Serializable {
|
|||
public int hashCode() {
|
||||
return Objects.hash( lockMode, timeout, aliasSpecificLockModes, followOnLocking, scope );
|
||||
}
|
||||
|
||||
private static class ImmutableLockOptions extends LockOptions {
|
||||
private ImmutableLockOptions(LockMode lockMode) {
|
||||
super(lockMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions setLockMode(LockMode lockMode) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions setLockScope(PessimisticLockScope scope) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions setFollowOnLocking(Boolean followOnLocking) {
|
||||
return super.setFollowOnLocking(followOnLocking);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions setTimeOut(int timeout) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions setAliasSpecificLockMode(String alias, LockMode lockMode) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions setScope(boolean scope) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.hibernate.query.Query;
|
|||
/**
|
||||
* List of Hibernate-specific (extension) hints available to query,
|
||||
* load and lock scenarios.
|
||||
*
|
||||
* <p>
|
||||
* Some hints are only effective in certain scenarios, which is noted on
|
||||
* each constant's documentation
|
||||
*
|
||||
|
@ -96,36 +96,39 @@ public interface HibernateHints {
|
|||
|
||||
/**
|
||||
* Hint to enable/disable the follow-on-locking mechanism provided by
|
||||
* {@link org.hibernate.dialect.Dialect#useFollowOnLocking(String, org.hibernate.query.spi.QueryOptions)}.
|
||||
* {@link org.hibernate.dialect.Dialect#useFollowOnLocking}.
|
||||
* <p>
|
||||
* A value of {@code true} enables follow-on-locking, whereas a value of
|
||||
* {@code false} disables it. If the value is {@code null}, the
|
||||
* {@code Dialect}'s default strategy is used.
|
||||
*
|
||||
* @see org.hibernate.LockOptions#setFollowOnLocking(Boolean)
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
String HINT_FOLLOW_ON_LOCKING = "hibernate.query.followOnLocking";
|
||||
|
||||
/**
|
||||
* Hint for specifying the lock-mode to apply to the results from a
|
||||
* native-query.
|
||||
*
|
||||
* While Hibernate supports applying lock-mode to a native-query, the specification
|
||||
* requires that {@link jakarta.persistence.Query#setLockMode} throw an
|
||||
* {@link IllegalStateException} if called for a native query.
|
||||
*
|
||||
* Hint for specifying the lock mode to apply to the results of a
|
||||
* native query.
|
||||
* <p>
|
||||
* While Hibernate supports applying lock-mode to a native-query, the
|
||||
* JPA specification requires that {@link jakarta.persistence.Query#setLockMode}
|
||||
* throw an {@link IllegalStateException} if called for a native query.
|
||||
* <p>
|
||||
* Accepts a {@link jakarta.persistence.LockModeType} or a {@link org.hibernate.LockMode}
|
||||
*/
|
||||
String HINT_NATIVE_LOCK_MODE = "org.hibernate.lockMode";
|
||||
|
||||
/**
|
||||
* Hint for specifying query spaces to be applied to a NativeQuery.
|
||||
*
|
||||
* Hint for specifying query spaces to be applied to a native query.
|
||||
* <p>
|
||||
* Passed value can be any of:<ul>
|
||||
* <li>List of the spaces</li>
|
||||
* <li>array of the spaces</li>
|
||||
* <li>String as "whitespace"-separated list of the spaces</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Note that the passed space need not match any real spaces/tables in
|
||||
* the underlying query. This can be used to completely circumvent
|
||||
* the auto-flush checks as well as any cache invalidation that might
|
||||
|
|
Loading…
Reference in New Issue