JPA requires that IllegalStateException be thrown instead of UOE
leave code comments making this very clear, because it wasn't clear at all (and is sort-of wrong) on the other hand, add getHibernateLockMode(), and let the client bypass the stupid JPA restriction
This commit is contained in:
parent
2a92267cd8
commit
c966acf178
|
@ -11,6 +11,7 @@ import java.util.Calendar;
|
|||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.FlushModeType;
|
||||
import jakarta.persistence.LockModeType;
|
||||
|
@ -537,9 +538,33 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
|
|||
@Override
|
||||
NativeQuery<T> setReadOnly(boolean readOnly);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* This operation is supported even for native queries.
|
||||
* Note that specifying an explicit lock mode might
|
||||
* result in changes to the native SQL query that is
|
||||
* actually executed.
|
||||
*/
|
||||
@Override
|
||||
LockOptions getLockOptions();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* This operation is supported even for native queries.
|
||||
* Note that specifying an explicit lock mode might
|
||||
* result in changes to the native SQL query that is
|
||||
* actually executed.
|
||||
*/
|
||||
@Override
|
||||
NativeQuery<T> setLockOptions(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Not applicable to native SQL queries.
|
||||
*
|
||||
* @throws IllegalStateException for consistency with JPA
|
||||
*/
|
||||
@Override
|
||||
NativeQuery<T> setLockMode(String alias, LockMode lockMode);
|
||||
|
||||
|
@ -558,9 +583,52 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
|
|||
@Override
|
||||
NativeQuery<T> setHint(String hintName, Object value);
|
||||
|
||||
/**
|
||||
* Not applicable to native SQL queries, due to an unfortunate
|
||||
* requirement of the JPA specification.
|
||||
* <p>
|
||||
* Use {@link #getHibernateLockMode()} to obtain the lock mode.
|
||||
*
|
||||
* @throws IllegalStateException as required by JPA
|
||||
*/
|
||||
@Override
|
||||
LockModeType getLockMode();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* This operation is supported even for native queries.
|
||||
* Note that specifying an explicit lock mode might
|
||||
* result in changes to the native SQL query that is
|
||||
* actually executed.
|
||||
*/
|
||||
@Override
|
||||
LockMode getHibernateLockMode();
|
||||
|
||||
/**
|
||||
* Not applicable to native SQL queries, due to an unfortunate
|
||||
* requirement of the JPA specification.
|
||||
* <p>
|
||||
* Use {@link #setHibernateLockMode(LockMode)} or the hint named
|
||||
* {@value org.hibernate.jpa.HibernateHints#HINT_NATIVE_LOCK_MODE}
|
||||
* to set the lock mode.
|
||||
*
|
||||
* @throws IllegalStateException as required by JPA
|
||||
*/
|
||||
@Override
|
||||
NativeQuery<T> setLockMode(LockModeType lockMode);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* This operation is supported even for native queries.
|
||||
* Note that specifying an explicit lock mode might
|
||||
* result in changes to the native SQL query that is
|
||||
* actually executed.
|
||||
*/
|
||||
@Override
|
||||
NativeQuery<T> setHibernateLockMode(LockMode lockMode);
|
||||
|
||||
@Override
|
||||
<R> NativeQuery<R> setTupleTransformer(TupleTransformer<R> transformer);
|
||||
|
||||
|
|
|
@ -353,8 +353,8 @@ 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 lockMode The lock mode to apply.
|
||||
* @param alias A query alias
|
||||
* @param lockMode The lock mode to apply
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*
|
||||
|
|
|
@ -336,6 +336,11 @@ public interface SelectionQuery<R> extends CommonQueryContract {
|
|||
*/
|
||||
SelectionQuery<R> setLockMode(LockModeType lockMode);
|
||||
|
||||
/**
|
||||
* Get the root {@link LockMode} for the query
|
||||
*/
|
||||
LockMode getHibernateLockMode();
|
||||
|
||||
/**
|
||||
* Specify the root {@link LockMode} for the query
|
||||
*/
|
||||
|
|
|
@ -570,14 +570,24 @@ public abstract class AbstractSelectionQuery<R>
|
|||
*
|
||||
* @see #setHibernateLockMode
|
||||
*/
|
||||
@Override
|
||||
public SelectionQuery<R> setLockMode(LockModeType lockMode) {
|
||||
setHibernateLockMode( LockModeTypeHelper.getLockMode( lockMode ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root LockMode for the query
|
||||
*/
|
||||
@Override
|
||||
public LockMode getHibernateLockMode() {
|
||||
return getLockOptions().getLockMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the root LockMode for the query
|
||||
*/
|
||||
@Override
|
||||
public SelectionQuery<R> setHibernateLockMode(LockMode lockMode) {
|
||||
getLockOptions().setLockMode( lockMode );
|
||||
return this;
|
||||
|
|
|
@ -55,7 +55,7 @@ public interface QueryImplementor<R> extends Query<R> {
|
|||
@Override
|
||||
QueryImplementor<R> setResultListTransformer(ResultListTransformer<R> transformer);
|
||||
|
||||
@Override
|
||||
@Override @Deprecated @SuppressWarnings("deprecation")
|
||||
default <T> QueryImplementor<T> setResultTransformer(ResultTransformer<T> transformer) {
|
||||
Query.super.setResultTransformer( transformer );
|
||||
//noinspection unchecked
|
||||
|
|
|
@ -472,22 +472,34 @@ public class NativeQueryImpl<R>
|
|||
|
||||
@Override
|
||||
public LockModeType getLockMode() {
|
||||
throw new UnsupportedOperationException( "Illegal attempt to get lock mode on a native-query" );
|
||||
// the JPA spec requires IllegalStateException here, even
|
||||
// though it's logically an UnsupportedOperationException
|
||||
throw new IllegalStateException( "Illegal attempt to get lock mode on a native-query" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> setLockOptions(LockOptions lockOptions) {
|
||||
throw new UnsupportedOperationException( "Illegal attempt to set lock options for a native query" );
|
||||
super.setLockOptions( lockOptions );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> setHibernateLockMode(LockMode lockMode) {
|
||||
super.setHibernateLockMode( lockMode );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> setLockMode(String alias, LockMode lockMode) {
|
||||
throw new UnsupportedOperationException( "Illegal attempt to set lock mode for a native query" );
|
||||
// throw IllegalStateException here for consistency with JPA
|
||||
throw new IllegalStateException( "Illegal attempt to set lock mode for a native query" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> setLockMode(LockModeType lockModeType) {
|
||||
throw new UnsupportedOperationException( "Illegal attempt to set lock mode for a native query" );
|
||||
// the JPA spec requires IllegalStateException here, even
|
||||
// though it's logically an UnsupportedOperationException
|
||||
throw new IllegalStateException( "Illegal attempt to set lock mode for a native query" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -42,12 +42,6 @@ import jakarta.persistence.metamodel.SingularAttribute;
|
|||
@Incubating
|
||||
public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQuery<R>, NameableQuery {
|
||||
|
||||
@Override
|
||||
LockOptions getLockOptions();
|
||||
|
||||
@Override
|
||||
LockModeType getLockMode();
|
||||
|
||||
/**
|
||||
* Best guess whether this is a select query. {@code null}
|
||||
* indicates unknown
|
||||
|
@ -59,7 +53,7 @@ public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQu
|
|||
// covariant overrides - NativeQuery
|
||||
|
||||
|
||||
@Override
|
||||
@Override @Deprecated @SuppressWarnings("deprecation")
|
||||
default <T> NativeQueryImplementor<T> setResultTransformer(ResultTransformer<T> transformer) {
|
||||
QueryImplementor.super.setResultTransformer( transformer );
|
||||
//noinspection unchecked
|
||||
|
@ -179,6 +173,9 @@ public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQu
|
|||
@Override
|
||||
NativeQueryImplementor<R> setLockOptions(LockOptions lockOptions);
|
||||
|
||||
@Override
|
||||
NativeQueryImplementor<R> setHibernateLockMode(LockMode lockMode);
|
||||
|
||||
@Override
|
||||
NativeQueryImplementor<R> setLockMode(LockModeType lockMode);
|
||||
|
||||
|
|
|
@ -115,12 +115,26 @@ public class QueryLockingTest extends BaseEntityManagerFunctionalTestCase {
|
|||
em.getTransaction().begin();
|
||||
NativeQuery query = em.createNativeQuery( "select * from lockable l" ).unwrap( NativeQuery.class );
|
||||
|
||||
// the spec disallows calling setLockMode in a native SQL query
|
||||
// the spec disallows calling setLockMode() and getLockMode()
|
||||
// on a native SQL query and requires that an IllegalStateException
|
||||
// be thrown
|
||||
try {
|
||||
query.setLockMode( LockModeType.READ );
|
||||
fail( "Should have failed" );
|
||||
}
|
||||
catch (UnsupportedOperationException expected) {
|
||||
catch (IllegalStateException expected) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail( "Should have thrown IllegalStateException but threw " + e.getClass().getName() );
|
||||
}
|
||||
try {
|
||||
query.getLockMode();
|
||||
fail( "Should have failed" );
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail( "Should have thrown IllegalStateException but threw " + e.getClass().getName() );
|
||||
}
|
||||
|
||||
// however, we should be able to set it using hints
|
||||
|
|
|
@ -106,7 +106,21 @@ public class PagingAndLockingTest extends BaseCoreFunctionalTestCase {
|
|||
qry.getLockOptions().setLockMode( LockMode.PESSIMISTIC_WRITE );
|
||||
qry.setFirstResult( 2 );
|
||||
qry.setMaxResults( 2 );
|
||||
@SuppressWarnings("unchecked") List results = qry.list();
|
||||
List results = qry.list();
|
||||
assertEquals( 2, results.size() );
|
||||
for ( Object door : results ) {
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
inTransaction(
|
||||
session -> {
|
||||
NativeQuery qry = session.createNativeQuery( "select * from door" );
|
||||
qry.addRoot( "door", Door.class );
|
||||
qry.setHibernateLockMode( LockMode.PESSIMISTIC_WRITE );
|
||||
qry.setFirstResult( 2 );
|
||||
qry.setMaxResults( 2 );
|
||||
List results = qry.list();
|
||||
assertEquals( 2, results.size() );
|
||||
for ( Object door : results ) {
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) );
|
||||
|
|
|
@ -171,6 +171,7 @@ public class AddNamedQueryTest {
|
|||
assertEquals( FlushModeType.COMMIT, hibernateQuery.getFlushMode() );
|
||||
assertEquals( CacheMode.IGNORE, hibernateQuery.getCacheMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getHibernateLockMode() );
|
||||
// jpa timeout is in milliseconds, whereas Hibernate's is in seconds
|
||||
assertEquals( (Integer) 3, hibernateQuery.getTimeout() );
|
||||
|
||||
|
@ -188,6 +189,7 @@ public class AddNamedQueryTest {
|
|||
assertEquals( FlushModeType.COMMIT, hibernateQuery.getFlushMode() );
|
||||
assertEquals( CacheMode.IGNORE, hibernateQuery.getCacheMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getHibernateLockMode() );
|
||||
assertEquals( (Integer) 10, hibernateQuery.getTimeout() );
|
||||
|
||||
query.setHint( HINT_SPEC_QUERY_TIMEOUT, 10000 );
|
||||
|
@ -202,6 +204,7 @@ public class AddNamedQueryTest {
|
|||
assertEquals( FlushModeType.COMMIT, hibernateQuery.getFlushMode() );
|
||||
assertEquals( CacheMode.IGNORE, hibernateQuery.getCacheMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getHibernateLockMode() );
|
||||
assertEquals( (Integer) 10, hibernateQuery.getTimeout() );
|
||||
|
||||
query.setFirstResult( 51 );
|
||||
|
@ -216,6 +219,7 @@ public class AddNamedQueryTest {
|
|||
assertEquals( FlushModeType.COMMIT, hibernateQuery.getFlushMode() );
|
||||
assertEquals( CacheMode.IGNORE, hibernateQuery.getCacheMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getHibernateLockMode() );
|
||||
assertEquals( (Integer) 10, hibernateQuery.getTimeout() );
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue