HHH-8542 - javax.persistence.Query#setLockMode should throw ISE if not a SELECT JPQL or Criteria query
This commit is contained in:
parent
ccd1fb07c8
commit
903321fe92
|
@ -110,4 +110,12 @@ public class QueryHints {
|
|||
*/
|
||||
public static final String TIMEOUT_JPA = "javax.persistence.query.timeout";
|
||||
|
||||
/**
|
||||
* Available to apply lock mode to a native SQL query since JPA requires that
|
||||
* {@link javax.persistence.Query#setLockMode} throw an IllegalStateException if called for a native query.
|
||||
* <p/>
|
||||
* Accepts a {@link javax.persistence.LockModeType} or a {@link org.hibernate.LockMode}
|
||||
*/
|
||||
public static final String NATIVE_LOCKMODE = "org.hibernate.lockMode";
|
||||
|
||||
}
|
||||
|
|
|
@ -400,4 +400,8 @@ public class HQLQueryPlan implements Serializable {
|
|||
public Class getDynamicInstantiationResultType() {
|
||||
return translators[0].getDynamicInstantiationResultType();
|
||||
}
|
||||
|
||||
public boolean isSelect() {
|
||||
return !translators[0].isManipulationStatement();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,12 @@
|
|||
*
|
||||
*/
|
||||
package org.hibernate.internal;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.Filter;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -139,10 +141,9 @@ public class QueryImpl extends AbstractQueryImpl {
|
|||
return lockOptions;
|
||||
}
|
||||
|
||||
public boolean isSelect() {
|
||||
return getSession().getFactory().getQueryPlanCache()
|
||||
.getHQLQueryPlan( getQueryString(), false, Collections.<String, Filter>emptyMap() )
|
||||
.isSelect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -22,9 +22,20 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.jpa;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import static org.hibernate.annotations.QueryHints.*;
|
||||
|
||||
import static org.hibernate.annotations.QueryHints.CACHEABLE;
|
||||
import static org.hibernate.annotations.QueryHints.CACHE_MODE;
|
||||
import static org.hibernate.annotations.QueryHints.CACHE_REGION;
|
||||
import static org.hibernate.annotations.QueryHints.COMMENT;
|
||||
import static org.hibernate.annotations.QueryHints.FETCH_SIZE;
|
||||
import static org.hibernate.annotations.QueryHints.FLUSH_MODE;
|
||||
import static org.hibernate.annotations.QueryHints.NATIVE_LOCKMODE;
|
||||
import static org.hibernate.annotations.QueryHints.READ_ONLY;
|
||||
import static org.hibernate.annotations.QueryHints.TIMEOUT_HIBERNATE;
|
||||
import static org.hibernate.annotations.QueryHints.TIMEOUT_JPA;
|
||||
|
||||
/**
|
||||
* Defines the supported JPA query hints
|
||||
|
@ -85,6 +96,8 @@ public class QueryHints {
|
|||
*/
|
||||
public static final String HINT_FLUSH_MODE = FLUSH_MODE;
|
||||
|
||||
public static final String HINT_NATIVE_LOCKMODE = NATIVE_LOCKMODE;
|
||||
|
||||
private static final Set<String> HINTS = buildHintsSet();
|
||||
|
||||
private static Set<String> buildHintsSet() {
|
||||
|
@ -98,6 +111,7 @@ public class QueryHints {
|
|||
hints.add( HINT_READONLY );
|
||||
hints.add( HINT_CACHE_MODE );
|
||||
hints.add( HINT_FLUSH_MODE );
|
||||
hints.add( HINT_NATIVE_LOCKMODE );
|
||||
return java.util.Collections.unmodifiableSet( hints );
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,20 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
extractParameterInfo( namedParameterTypeRedefinitions );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNativeSqlQuery() {
|
||||
return SQLQuery.class.isInstance( query );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSelectQuery() {
|
||||
if ( isNativeSqlQuery() ) {
|
||||
throw new IllegalStateException( "Cannot tell if native SQL query is SELECT query" );
|
||||
}
|
||||
|
||||
return org.hibernate.internal.QueryImpl.class.cast( query ).isSelect();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "RedundantCast" })
|
||||
private void extractParameterInfo(Map<String,Class> namedParameterTypeRedefinition) {
|
||||
if ( ! org.hibernate.internal.AbstractQueryImpl.class.isInstance( query ) ) {
|
||||
|
|
|
@ -402,6 +402,16 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNativeSqlQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSelectQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query setLockMode(LockModeType lockMode) {
|
||||
throw new IllegalStateException( "javax.persistence.Query.setLockMode not valid on javax.persistence.StoredProcedureQuery" );
|
||||
|
@ -415,6 +425,12 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
|
|||
|
||||
// unsupported hints/calls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@Override
|
||||
protected void internalApplyLockMode(LockModeType lockModeType) {
|
||||
throw new IllegalStateException( "Specifying LockMode not valid on javax.persistence.StoredProcedureQuery" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFirstResult(int firstResult) {
|
||||
}
|
||||
|
|
|
@ -120,6 +120,14 @@ public abstract class AbstractQueryImpl<X> extends BaseQueryImpl implements Type
|
|||
public TypedQuery<X> setLockMode(javax.persistence.LockModeType lockModeType) {
|
||||
checkOpen( true );
|
||||
|
||||
if ( isNativeSqlQuery() ) {
|
||||
throw new IllegalStateException( "Illegal attempt to set lock mode on a native SQL query" );
|
||||
}
|
||||
|
||||
if ( ! isSelectQuery() ) {
|
||||
throw new IllegalStateException( "Illegal attempt to set lock mode on a non-SELECT query" );
|
||||
}
|
||||
|
||||
// todo : technically this check should be on execution of the query, not here : HHH-8521
|
||||
if ( lockModeType != LockModeType.NONE ) {
|
||||
if (! getEntityManager().isTransactionInProgress()) {
|
||||
|
@ -136,8 +144,6 @@ public abstract class AbstractQueryImpl<X> extends BaseQueryImpl implements Type
|
|||
return this;
|
||||
}
|
||||
|
||||
protected abstract void internalApplyLockMode(javax.persistence.LockModeType lockModeType);
|
||||
|
||||
@Override
|
||||
public javax.persistence.LockModeType getLockMode() {
|
||||
getEntityManager().checkOpen( false );
|
||||
|
|
|
@ -26,6 +26,7 @@ package org.hibernate.jpa.spi;
|
|||
import javax.persistence.CacheRetrieveMode;
|
||||
import javax.persistence.CacheStoreMode;
|
||||
import javax.persistence.FlushModeType;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.Parameter;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TemporalType;
|
||||
|
@ -59,6 +60,7 @@ import static org.hibernate.jpa.QueryHints.HINT_CACHE_REGION;
|
|||
import static org.hibernate.jpa.QueryHints.HINT_COMMENT;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_FLUSH_MODE;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_NATIVE_LOCKMODE;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_READONLY;
|
||||
import static org.hibernate.jpa.QueryHints.HINT_TIMEOUT;
|
||||
import static org.hibernate.jpa.QueryHints.SPEC_HINT_TIMEOUT;
|
||||
|
@ -330,6 +332,31 @@ public abstract class BaseQueryImpl implements Query {
|
|||
CacheModeHelper.interpretCacheMode( storeMode, retrieveMode )
|
||||
);
|
||||
}
|
||||
else if ( QueryHints.HINT_NATIVE_LOCKMODE.equals( hintName ) ) {
|
||||
if ( !isNativeSqlQuery() ) {
|
||||
throw new IllegalStateException(
|
||||
"Illegal attempt to set lock mode on non-native query via hint; use Query#setLockMode instead"
|
||||
);
|
||||
}
|
||||
if ( LockMode.class.isInstance( value ) ) {
|
||||
internalApplyLockMode( LockModeTypeHelper.getLockModeType( (LockMode) value ) );
|
||||
}
|
||||
else if ( LockModeType.class.isInstance( value ) ) {
|
||||
internalApplyLockMode( (LockModeType) value );
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Native lock-mode hint [%s] must specify %s or %s. Encountered type : %s",
|
||||
HINT_NATIVE_LOCKMODE,
|
||||
LockMode.class.getName(),
|
||||
LockModeType.class.getName(),
|
||||
value.getClass().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
applied = true;
|
||||
}
|
||||
else if ( hintName.startsWith( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE ) ) {
|
||||
if ( canApplyAliasSpecificLockModeHints() ) {
|
||||
// extract the alias
|
||||
|
@ -369,6 +396,21 @@ public abstract class BaseQueryImpl implements Query {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the query represented here a native SQL query?
|
||||
*
|
||||
* @return {@code true} if it is a native SQL query; {@code false} otherwise
|
||||
*/
|
||||
protected abstract boolean isNativeSqlQuery();
|
||||
|
||||
/**
|
||||
* Is the query represented here a SELECT query?
|
||||
*
|
||||
* @return {@code true} if the query is a SELECT; {@code false} otherwise.
|
||||
*/
|
||||
protected abstract boolean isSelectQuery();
|
||||
|
||||
protected abstract void internalApplyLockMode(javax.persistence.LockModeType lockModeType);
|
||||
|
||||
// FlushMode ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ import javax.persistence.LockModeType;
|
|||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.ejb.AvailableSettings;
|
||||
import org.hibernate.jpa.AvailableSettings;
|
||||
import org.hibernate.jpa.QueryHints;
|
||||
import org.hibernate.jpa.internal.QueryImpl;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.internal.SessionImpl;
|
||||
|
@ -40,6 +41,7 @@ import static org.junit.Assert.assertFalse;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -89,12 +91,18 @@ public class QueryLockingTest extends BaseEntityManagerFunctionalTestCase {
|
|||
QueryImpl query = em.createNativeQuery( "select * from lockable l" ).unwrap( QueryImpl.class );
|
||||
|
||||
org.hibernate.internal.SQLQueryImpl hibernateQuery = (org.hibernate.internal.SQLQueryImpl) query.getHibernateQuery();
|
||||
// assertEquals( LockMode.NONE, hibernateQuery.getLockOptions().getLockMode() );
|
||||
// assertNull( hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
|
||||
// assertEquals( LockMode.NONE, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) );
|
||||
|
||||
// the spec disallows calling setLockMode in a native SQL query
|
||||
try {
|
||||
query.setLockMode( LockModeType.READ );
|
||||
fail( "Should have failed" );
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
}
|
||||
|
||||
// however, we should be able to set it using hints
|
||||
query.setHint( QueryHints.HINT_NATIVE_LOCKMODE, LockModeType.READ );
|
||||
// NOTE : LockModeType.READ should map to LockMode.OPTIMISTIC
|
||||
query.setLockMode( LockModeType.READ );
|
||||
assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertNull( hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
|
||||
assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) );
|
||||
|
|
Loading…
Reference in New Issue