HHH-15683+HHH-15684 clean up the handling of LockOptions for queries

This contains a change to LockOptions.overlay() which is breaking
in principle, but more natural and less fragile.

It also deprecates SelectionQuery.setAliasSpecificLockMode() which
I believe was added in 6.0 by mistake. The method is an overload of
setLockMode() in the rest of the hierarchy.
This commit is contained in:
Gavin King 2022-11-07 12:09:48 +01:00
parent 58ba65f529
commit 02ad34091c
11 changed files with 81 additions and 52 deletions

View File

@ -432,8 +432,18 @@ public class LockOptions implements Serializable {
return copy;
}
/**
* Copy the given lock options into this instance,
* merging the alias-specific lock modes.
*/
public void overlay(LockOptions lockOptions) {
copy( lockOptions, this );
setLockMode( lockOptions.getLockMode() );
setScope( lockOptions.getScope() );
setTimeOut( lockOptions.getTimeOut() );
if ( lockOptions.aliasSpecificLockModes != null ) {
lockOptions.aliasSpecificLockModes.forEach(this::setAliasSpecificLockMode);
}
setFollowOnLocking( lockOptions.getFollowOnLocking() );
}
/**

View File

@ -23,6 +23,7 @@ import java.util.stream.Stream;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -329,11 +330,6 @@ public class ProcedureCallImpl<R>
return queryOptions;
}
@Override
public QueryImplementor<R> setLockMode(String alias, LockMode lockMode) {
throw new IllegalStateException( "Cannot set LockMode on a procedure-call" );
}
@Override
public ProcedureParameterMetadataImpl getParameterMetadata() {
return parameterMetadata;
@ -1085,14 +1081,24 @@ public class ProcedureCallImpl<R>
throw new PersistenceException( "Unrecognized unwrap type : " + cls.getName() );
}
@Override
public QueryImplementor<R> setLockMode(String alias, LockMode lockMode) {
throw new UnsupportedOperationException( "setLockMode does not apply to procedure calls" );
}
@Override
public ProcedureCallImplementor<R> setLockMode(LockModeType lockMode) {
throw new IllegalStateException("jakarta.persistence.Query.setLockMode not valid on jakarta.persistence.StoredProcedureQuery" );
throw new UnsupportedOperationException( "setLockMode does not apply to procedure calls" );
}
@Override
public LockModeType getLockMode() {
throw new IllegalStateException( "jakarta.persistence.Query.getHibernateFlushMode not valid on jakarta.persistence.StoredProcedureQuery" );
throw new UnsupportedOperationException( "getLockMode does not apply to procedure calls" );
}
@Override
public QueryImplementor<R> setLockOptions(LockOptions lockOptions) {
throw new UnsupportedOperationException( "setLockOptions does not apply to procedure calls" );
}
@Override

View File

@ -39,9 +39,9 @@ import org.hibernate.type.BasicTypeReference;
* </li>
* <li>
* Tables used via {@link #addSynchronizedQuerySpace}, {@link #addSynchronizedEntityName} and
* {@link org.hibernate.query.SynchronizeableQuery#addSynchronizedEntityClass}. This allows Hibernate to know how to properly deal with
* auto-flush checking as well as cached query results if the results of the query are being
* cached.
* {@link org.hibernate.query.SynchronizeableQuery#addSynchronizedEntityClass}. This allows
* Hibernate to know how to properly deal with auto-flush checking as well as cached query
* results if the results of the query are being cached.
* </li>
* </ul>
*
@ -51,7 +51,8 @@ import org.hibernate.type.BasicTypeReference;
* of its metadata
* </li>
* <li>
* A pre-defined (defined in metadata and named) mapping can be associated via {@link org.hibernate.Session#createNativeQuery(String, String)}
* A pre-defined (defined in metadata and named) mapping can be associated via
* {@link org.hibernate.Session#createNativeQuery(String, String)}
* </li>
* <li>
* Defined locally per the various {@link #addEntity}, {@link #addRoot}, {@link #addJoin},

View File

@ -317,22 +317,22 @@ public interface Query<R> extends SelectionQuery<R>, MutationQuery, TypedQuery<R
/**
* Obtains the {@link LockOptions} in effect for this query.
*
* @return The LockOptions
* @return The {@link LockOptions} currently in effect
*
* @see LockOptions
*/
@Override
LockOptions getLockOptions();
/**
* Set the lock options for the query.
* <p>
* Only the following options are taken into consideration:
* <ol>
* <li>{@link LockOptions#getLockMode()}</li>
* <li>{@link LockOptions#getScope()}</li>
* <li>{@link LockOptions#getTimeOut()}</li>
* </ol>
* For alias-specific locking, use {@link #setLockMode(String, LockMode)}.
* Apply the given {@linkplain LockOptions lock options} to this
* query. Alias-specific lock modes in the given lock options are
* merged with any alias-specific lock mode which have already been
* {@linkplain #setAliasSpecificLockMode(String, LockMode) set}. If
* a lock mode has already been specified for an alias that is among
* the aliases in the given lock options, the lock mode specified in
* the given lock options overrides the lock mode that was already
* set.
*
* @param lockOptions The lock options to apply to the query.
*
@ -353,13 +353,14 @@ public interface Query<R> extends SelectionQuery<R>, MutationQuery, TypedQuery<R
* driver and database. For maximum portability, the given lock
* mode should be {@link LockMode#PESSIMISTIC_WRITE}.
*
* @param alias a query alias, or {@code "this"} for a collection filter
* @param alias a query alias, or {@code this} for a collection filter
* @param lockMode The lock mode to apply.
*
* @return {@code this}, for method chaining
*
* @see #getLockOptions()
*/
@Override
Query<R> setLockMode(String alias, LockMode lockMode);
/**

View File

@ -21,6 +21,7 @@ import org.hibernate.Incubating;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NonUniqueResultException;
import org.hibernate.Remove;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
@ -324,25 +325,33 @@ public interface SelectionQuery<R> extends CommonQueryContract {
SelectionQuery<R> setCacheRegion(String cacheRegion);
/**
* The LockOptions currently in effect for the query
* The {@link LockOptions} currently in effect for the query
*/
LockOptions getLockOptions();
/**
* Specify the root LockModeType for the query
* Specify the root {@link LockModeType} for the query
*
* @see #setHibernateLockMode
*/
SelectionQuery<R> setLockMode(LockModeType lockMode);
/**
* Specify the root LockMode for the query
* Specify the root {@link LockMode} for the query
*/
SelectionQuery<R> setHibernateLockMode(LockMode lockMode);
/**
* Specify a LockMode to apply to a specific alias defined in the query
* Specify a {@link LockMode} to apply to a specific alias defined in the query
*/
SelectionQuery<R> setLockMode(String alias, LockMode lockMode);
/**
* Specify a {@link LockMode} to apply to a specific alias defined in the query
*
* @deprecated use {@link #setLockMode(String, LockMode)}
*/
@Deprecated(since = "6.2") @Remove
SelectionQuery<R> setAliasSpecificLockMode(String alias, LockMode lockMode);
/**

View File

@ -247,16 +247,12 @@ public abstract class AbstractQuery<R>
@Override
public LockModeType getLockMode() {
getSession().checkOpen( false );
return LockModeTypeHelper.getLockModeType( getQueryOptions().getLockOptions().getLockMode() );
}
@Override
public QueryImplementor<R> setLockOptions(LockOptions lockOptions) {
getQueryOptions().getLockOptions().setLockMode( lockOptions.getLockMode() );
getQueryOptions().getLockOptions().setScope( lockOptions.getScope() );
getQueryOptions().getLockOptions().setTimeOut( lockOptions.getTimeOut() );
getQueryOptions().getLockOptions().setFollowOnLocking( lockOptions.getFollowOnLocking() );
getQueryOptions().getLockOptions().overlay( lockOptions );
return this;
}

View File

@ -585,7 +585,10 @@ public abstract class AbstractSelectionQuery<R>
/**
* Specify a LockMode to apply to a specific alias defined in the query
*
* @deprecated use {{@link #setLockMode(String, LockMode)}}
*/
@Override @Deprecated
public SelectionQuery<R> setAliasSpecificLockMode(String alias, LockMode lockMode) {
getLockOptions().setAliasSpecificLockMode( alias, lockMode );
return this;

View File

@ -472,24 +472,22 @@ public class NativeQueryImpl<R>
@Override
public LockModeType getLockMode() {
throw new IllegalStateException( "Illegal attempt to get lock mode on a native-query" );
throw new UnsupportedOperationException( "Illegal attempt to get lock mode on a native-query" );
}
@Override
public NativeQueryImplementor<R> setLockOptions(LockOptions lockOptions) {
super.setLockOptions( lockOptions );
return this;
throw new UnsupportedOperationException( "Illegal attempt to set lock options for a native query" );
}
@Override
public NativeQueryImplementor<R> setLockMode(String alias, LockMode lockMode) {
super.setLockMode( alias, lockMode );
return this;
throw new UnsupportedOperationException( "Illegal attempt to set lock mode for a native query" );
}
@Override
public NativeQueryImplementor<R> setLockMode(LockModeType lockModeType) {
throw new IllegalStateException( "Illegal attempt to set lock mode on a native-query" );
throw new UnsupportedOperationException( "Illegal attempt to set lock mode for a native query" );
}
@Override
@ -599,10 +597,8 @@ public class NativeQueryImpl<R>
private SelectQueryPlan<R> resolveSelectQueryPlan() {
final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping );
if ( cacheKey != null ) {
return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan(
cacheKey,
() -> createQueryPlan( resultSetMapping )
);
return getSession().getFactory().getQueryEngine().getInterpretationCache()
.resolveSelectQueryPlan( cacheKey, () -> createQueryPlan( resultSetMapping ) );
}
else {
return createQueryPlan( resultSetMapping );

View File

@ -43,14 +43,10 @@ import jakarta.persistence.metamodel.SingularAttribute;
public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQuery<R>, NameableQuery {
@Override
default LockOptions getLockOptions() {
return null;
}
LockOptions getLockOptions();
@Override
default LockModeType getLockMode() {
return null;
}
LockModeType getLockMode();
/**
* Best guess whether this is a select query. {@code null}

View File

@ -456,7 +456,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
}
/**
* Specify the root LockMode for the query
* Specify the root {@link LockMode} for the query
*/
@Override
public SqmSelectionQuery<R> setHibernateLockMode(LockMode lockMode) {
@ -465,10 +465,21 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
}
/**
* Specify a LockMode to apply to a specific alias defined in the query
* Specify a {@link LockMode} to apply to a specific alias defined in the query
*
* @deprecated use {{@link #setLockMode(String, LockMode)}}
*/
@Override @Deprecated
public SqmSelectionQuery<R> setAliasSpecificLockMode(String alias, LockMode lockMode) {
getLockOptions().setAliasSpecificLockMode( alias, lockMode );
return this;
}
/**
* Specify a {@link LockMode} to apply to a specific alias defined in the query
*/
@Override
public SqmSelectionQuery<R> setAliasSpecificLockMode(String alias, LockMode lockMode) {
public SqmSelectionQuery<R> setLockMode(String alias, LockMode lockMode) {
getLockOptions().setAliasSpecificLockMode( alias, lockMode );
return this;
}

View File

@ -120,7 +120,7 @@ public class QueryLockingTest extends BaseEntityManagerFunctionalTestCase {
query.setLockMode( LockModeType.READ );
fail( "Should have failed" );
}
catch (IllegalStateException expected) {
catch (UnsupportedOperationException expected) {
}
// however, we should be able to set it using hints