HHH-11579 - Disable Query parameter validation when a Session is unwrapped from an EntityManager

This commit is contained in:
Andrea Boriero 2017-03-20 19:46:53 +00:00 committed by Vlad Mihalcea
parent c15fa77f0f
commit be0a273c7c
20 changed files with 253 additions and 13 deletions

View File

@ -325,6 +325,8 @@ Can reference
* `StatementInspector` implementation {@link Class} reference
* `StatementInspector` implementation class name (fully-qualified class name)
| `hibernate.query.validate_parameters` | `true` (default value) or `false` | This configuration property can be used to disable parameters validation performed by `org.hibernate.query.Query#setParameter` when the the Session is bootstrapped via JPA `javax.persistence.EntityManagerFactory`
3+|Multi-table bulk HQL operations
|`hibernate.hql.bulk_id_strategy` | A fully-qualified class name, an instance, or a `Class` object reference |Provide a custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/hql/spi/id/MultiTableBulkIdStrategy.html[`org.hibernate.hql.spi.id.MultiTableBulkIdStrategy`] implementation for handling multi-table bulk HQL operations.
|`hibernate.hql.bulk_id_strategy.global_temporary.drop_tables` | `true` or `false` (default value) | For databases that don't support local tables, but just global ones, this configuration property allows you to DROP the global tables used for multi-table bulk HQL operations when the `SessionFactory` or the `EntityManagerFactory` is closed.

View File

@ -176,4 +176,18 @@ public interface SessionBuilder<T extends SessionBuilder> {
T clearEventListeners();
T jdbcTimeZone(TimeZone timeZone);
/**
* Should {@link org.hibernate.query.Query#setParameter} perform parameter validation
* when the Session is bootstrapped via JPA {@link javax.persistence.EntityManagerFactory}
*
* @param enabled {@code true} indicates the validation should be performed, {@code false} otherwise
* <p>
* The default value is {@code true}
*
* @return {@code this}, for method chaining
*/
default T setQueryParameterValidation(boolean enabled) {
return (T) this;
}
}

View File

@ -38,4 +38,18 @@ public interface StatelessSessionBuilder<T extends StatelessSessionBuilder> {
* @return {@code this}, for method chaining
*/
T tenantIdentifier(String tenantIdentifier);
/**
* Should {@link org.hibernate.query.Query#setParameter} perform parameter validation
* when the Session is bootstrapped via JPA {@link javax.persistence.EntityManagerFactory}
*
* @param enabled {@code true} indicates the validation should be performed, {@code false} otherwise
* <p>
* The default value is {@code true}
*
* @return {@code this}, for method chaining
*/
default T setQueryParameterValidation(boolean enabled) {
return (T) this;
}
}

View File

@ -555,6 +555,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
private PhysicalConnectionHandlingMode connectionHandlingMode;
private boolean wrapResultSetsEnabled;
private TimeZone jdbcTimeZone;
private boolean queryParametersValidationEnabled;
private Map<String, SQLFunction> sqlFunctions;
@ -756,6 +757,12 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
else if ( jdbcTimeZoneValue != null ) {
throw new IllegalArgumentException( "Configuration property " + JDBC_TIME_ZONE + " value [" + jdbcTimeZoneValue + "] is not supported!" );
}
this.queryParametersValidationEnabled = ConfigurationHelper.getBoolean(
VALIDATE_QUERY_PARAMETERS,
configurationSettings,
true
);
}
private static Interceptor determineInterceptor(Map configurationSettings, StrategySelector strategySelector) {
@ -1187,6 +1194,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
public TimeZone getJdbcTimeZone() {
return this.jdbcTimeZone;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return this.queryParametersValidationEnabled;
}
}
@Override
@ -1505,4 +1517,9 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
public TimeZone getJdbcTimeZone() {
return options.getJdbcTimeZone();
}
@Override
public boolean isQueryParametersValidationEnabled() {
return options.isQueryParametersValidationEnabled();
}
}

View File

@ -125,6 +125,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
private final TimeZone jdbcTimeZone;
private final Map<String, SQLFunction> sqlFunctions;
private boolean queryParametersValidationEnabled;
public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
this.serviceRegistry = state.getServiceRegistry();
@ -202,6 +203,8 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
this.sqlFunctions = state.getCustomSqlFunctionMap();
this.jdbcTimeZone = state.getJdbcTimeZone();
this.queryParametersValidationEnabled = state.isQueryParametersValidationEnabled();
}
@Override
@ -527,4 +530,9 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
public TimeZone getJdbcTimeZone() {
return jdbcTimeZone;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return queryParametersValidationEnabled;
}
}

View File

@ -174,4 +174,6 @@ public interface SessionFactoryOptionsState {
boolean isPreferUserTransaction();
TimeZone getJdbcTimeZone();
boolean isQueryParametersValidationEnabled();
}

View File

@ -369,4 +369,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
public TimeZone getJdbcTimeZone() {
return delegate.getJdbcTimeZone();
}
@Override
public boolean isQueryParametersValidationEnabled() {
return delegate.isQueryParametersValidationEnabled();
}
}

View File

@ -215,4 +215,8 @@ public interface SessionFactoryOptions {
boolean isReleaseResourcesOnCloseEnabled();
TimeZone getJdbcTimeZone();
default boolean isQueryParametersValidationEnabled(){
return isJpaBootstrap();
}
}

View File

@ -1646,4 +1646,19 @@ public interface AvailableSettings {
* @since 5.2.5
*/
String USE_LEGACY_LIMIT_HANDLERS = "hibernate.legacy_limit_handler";
/**
* Setting which indicates if {@link org.hibernate.query.Query#setParameter} should not perform parameters validation
*
* This setting is applied only when the Session is bootstrapped via JPA {@link javax.persistence.EntityManagerFactory}
*
* </p>
* Values are: {@code true} indicates the validation should be performed, {@code false} otherwise
* <p>
* The default value is {@code true} when the Session is bootstrapped via JPA {@link javax.persistence.EntityManagerFactory},
* otherwise is {@code false}
*
*/
String VALIDATE_QUERY_PARAMETERS = "hibernate.query.validate_parameters";
}

View File

@ -106,4 +106,10 @@ public abstract class AbstractDelegatingSessionBuilder implements SessionBuilder
delegate.jdbcTimeZone(timeZone);
return this;
}
@Override
public SessionBuilder setQueryParameterValidation(boolean enabled) {
delegate.setQueryParameterValidation( enabled );
return this;
}
}

View File

@ -423,6 +423,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return delegate.isAutoCloseSessionEnabled();
}
@Override
public boolean isQueryParametersValidationEnabled() {
return delegate.isQueryParametersValidationEnabled();
}
@Override
public boolean shouldAutoJoinTransaction() {
return delegate.shouldAutoJoinTransaction();

View File

@ -408,6 +408,10 @@ public interface SharedSessionContractImplementor
boolean isAutoCloseSessionEnabled();
default boolean isQueryParametersValidationEnabled(){
return getFactory().getSessionFactoryOptions().isQueryParametersValidationEnabled();
}
/**
* Get the load query influencers associated with this session.
*

View File

@ -64,4 +64,6 @@ public interface SessionCreationOptions {
AfterCompletionAction getAfterCompletionAction();
ManagedFlushChecker getManagedFlushChecker();
boolean isQueryParametersValidationEnabled();
}

View File

@ -1075,6 +1075,7 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
private boolean autoClear;
private String tenantIdentifier;
private TimeZone jdbcTimeZone;
private boolean queryParametersValidationEnabled;
private List<SessionEventListener> listeners;
@ -1100,6 +1101,7 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
this.jdbcTimeZone = sessionFactory.getSessionFactoryOptions().getJdbcTimeZone();
listeners = sessionFactory.getSessionFactoryOptions().getBaselineSessionEventsListenerBuilder().buildBaselineList();
queryParametersValidationEnabled = sessionFactory.getSessionFactoryOptions().isQueryParametersValidationEnabled();
}
@ -1143,6 +1145,11 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
: null;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return this.queryParametersValidationEnabled;
}
@Override
public boolean shouldAutoJoinTransactions() {
return autoJoinTransactions;
@ -1318,12 +1325,19 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
jdbcTimeZone = timeZone;
return (T) this;
}
@Override
public T setQueryParameterValidation(boolean enabled) {
queryParametersValidationEnabled = enabled;
return (T) this;
}
}
public static class StatelessSessionBuilderImpl implements StatelessSessionBuilder, SessionCreationOptions {
private final SessionFactoryImpl sessionFactory;
private Connection connection;
private String tenantIdentifier;
private boolean queryParametersValidationEnabled;
public StatelessSessionBuilderImpl(SessionFactoryImpl sessionFactory) {
this.sessionFactory = sessionFactory;
@ -1331,6 +1345,7 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
if ( sessionFactory.getCurrentTenantIdentifierResolver() != null ) {
tenantIdentifier = sessionFactory.getCurrentTenantIdentifierResolver().resolveCurrentTenantIdentifier();
}
queryParametersValidationEnabled = sessionFactory.getSessionFactoryOptions().isQueryParametersValidationEnabled();
}
@Override
@ -1420,6 +1435,17 @@ public final class SessionFactoryImpl implements SessionFactoryImplementor {
public ManagedFlushChecker getManagedFlushChecker() {
return null;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return queryParametersValidationEnabled;
}
@Override
public StatelessSessionBuilder setQueryParameterValidation(boolean enabled) {
queryParametersValidationEnabled = enabled;
return this;
}
}
@Override

View File

@ -232,12 +232,14 @@ public final class SessionImpl
private boolean autoClear;
private boolean autoClose;
private boolean queryParametersValidationEnabled;
private transient int dontFlushFromFind;
private transient boolean disallowOutOfTransactionUpdateOperations;
private transient boolean disallowOutOfTransactionUpdateOperations;
private transient ExceptionMapper exceptionMapper;
private transient ManagedFlushChecker managedFlushChecker;
private transient AfterCompletionAction afterCompletionAction;
private transient LoadEvent loadEvent; //cached LoadEvent instance
@ -255,6 +257,7 @@ public final class SessionImpl
this.autoClear = options.shouldAutoClear();
this.autoClose = options.shouldAutoClose();
this.queryParametersValidationEnabled = options.isQueryParametersValidationEnabled();
this.disallowOutOfTransactionUpdateOperations = !factory.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations();
this.discardOnClose = getFactory().getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled();
@ -451,6 +454,11 @@ public final class SessionImpl
return autoClose;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return queryParametersValidationEnabled;
}
@Override
public boolean isOpen() {
checkSessionFactoryOpen();
@ -2577,6 +2585,10 @@ public final class SessionImpl
null;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return session.isQueryParametersValidationEnabled();
}
}
private class LockRequestImpl implements LockRequest {

View File

@ -136,7 +136,11 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
ParameterMetadata parameterMetadata) {
this.producer = producer;
this.parameterMetadata = parameterMetadata;
this.queryParameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, producer.getFactory() );
this.queryParameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata,
producer.getFactory(),
producer.isQueryParametersValidationEnabled()
);
}
@Override

View File

@ -49,30 +49,47 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
private final SessionFactoryImplementor sessionFactory;
private final ParameterMetadata parameterMetadata;
private final boolean queryParametersValidationEnabled;
private Map<QueryParameter, QueryParameterBinding> parameterBindingMap;
private Map<QueryParameter, QueryParameterListBinding> parameterListBindingMap;
private Map<Integer, QueryParameterBinding> positionalParameterBindings;
public static QueryParameterBindingsImpl from(ParameterMetadata parameterMetadata,
SessionFactoryImplementor sessionFactory) {
public static QueryParameterBindingsImpl from(
ParameterMetadata parameterMetadata,
SessionFactoryImplementor sessionFactory,
boolean queryParametersValidationEnabled) {
if ( parameterMetadata == null ) {
return new QueryParameterBindingsImpl( sessionFactory, parameterMetadata );
return new QueryParameterBindingsImpl(
sessionFactory,
parameterMetadata,
queryParametersValidationEnabled
);
}
else {
return new QueryParameterBindingsImpl( sessionFactory, parameterMetadata.collectAllParameters(), parameterMetadata );
return new QueryParameterBindingsImpl(
sessionFactory,
parameterMetadata.collectAllParameters(),
parameterMetadata, queryParametersValidationEnabled
);
}
}
public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory, ParameterMetadata parameterMetadata) {
this( sessionFactory, Collections.emptySet(), parameterMetadata );
private QueryParameterBindingsImpl(
SessionFactoryImplementor sessionFactory,
ParameterMetadata parameterMetadata,
boolean queryParametersValidationEnabled) {
this( sessionFactory, Collections.emptySet(), parameterMetadata, queryParametersValidationEnabled );
}
public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory,
private QueryParameterBindingsImpl(
SessionFactoryImplementor sessionFactory,
Set<QueryParameter<?>> queryParameters,
ParameterMetadata parameterMetadata) {
ParameterMetadata parameterMetadata,
boolean queryParametersValidationEnabled) {
this.sessionFactory = sessionFactory;
this.parameterMetadata = parameterMetadata;
this.queryParametersValidationEnabled = queryParametersValidationEnabled;
this.positionalParameterBindings = new TreeMap<>( );
if ( queryParameters == null || queryParameters.isEmpty() ) {
@ -441,7 +458,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
}
private boolean shouldValidateBindingValue() {
return sessionFactory.getSessionFactoryOptions().isJpaBootstrap();
return sessionFactory.getSessionFactoryOptions().isJpaBootstrap() && queryParametersValidationEnabled;
}
/**

View File

@ -12,6 +12,8 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
@ -20,7 +22,6 @@ import org.junit.Test;
/**
* @author Andrea Boriero
*/
@TestForIssue(jiraKey = "HHH-11397")
public class QueryParametersValidationTest extends BaseEntityManagerFunctionalTestCase {
@Override
@ -28,6 +29,7 @@ public class QueryParametersValidationTest extends BaseEntityManagerFunctionalTe
return new Class[] {TestEntity.class};
}
@TestForIssue(jiraKey = "HHH-11397")
@Test(expected = IllegalArgumentException.class)
public void setParameterWithWrongTypeShouldThrowIllegalArgumentException() {
final EntityManager entityManager = entityManagerFactory().createEntityManager();
@ -39,6 +41,20 @@ public class QueryParametersValidationTest extends BaseEntityManagerFunctionalTe
}
}
@Test
@TestForIssue(jiraKey = "HHH-11579")
public void setParameterWithWrongTypeShouldNotThrowIllegalArgumentExceptionWhenValidationIsDisabled() {
final SessionFactory sessionFactory = entityManagerFactory().unwrap( SessionFactory.class );
final Session session = sessionFactory.withOptions().setQueryParameterValidation( false ).openSession();
try {
session.createQuery( "select e from TestEntity e where e.id = :id" ).setParameter( "id", 1 );
}
finally {
session.close();
sessionFactory.close();
}
}
@Test
public void setParameterWithCorrectTypeShouldNotThrowIllegalArgumentException() {
final EntityManager entityManager = entityManagerFactory().createEntityManager();

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.query;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
/**
* @author Andrea Boriero
*/
@TestForIssue(jiraKey = "HHH-11579")
public class QueryParametersWithDisabledValidationTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {QueryParametersValidationTest.TestEntity.class};
}
@Override
protected void addConfigOptions(Map options) {
options.put( AvailableSettings.VALIDATE_QUERY_PARAMETERS, false );
}
@Test
public void setParameterWithWrongTypeShouldNotThrowIllegalArgumentException() {
final EntityManager entityManager = entityManagerFactory().createEntityManager();
try {
entityManager.createQuery( "select e from TestEntity e where e.id = :id" ).setParameter( "id", 1 );
}
finally {
entityManager.close();
}
}
@Test
public void setParameterWithCorrectTypeShouldNotThrowIllegalArgumentException() {
final EntityManager entityManager = entityManagerFactory().createEntityManager();
try {
entityManager.createQuery( "select e from TestEntity e where e.id = :id" ).setParameter( "id", 1L );
}
finally {
entityManager.close();
}
}
@Entity(name = "TestEntity")
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
}

View File

@ -30,7 +30,7 @@ public class QueryParametersValidationTest extends BaseCoreFunctionalTestCase {
@Test
public void setParameterWithWrongTypeShouldNotThrowIllegalArgumentException() {
try (Session session = openSession();) {
try (Session session = openSession()) {
session.createQuery( "select e from TestEntity e where e.id = :id" ).setParameter( "id", 1 );
}
}