diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java
index a9ee66a01f..b595c7e160 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java
@@ -44,7 +44,6 @@
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.config.internal.ConfigurationServiceImpl;
import org.hibernate.engine.config.spi.ConfigurationService;
-import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
@@ -53,6 +52,7 @@
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
@@ -564,6 +564,7 @@ public static class SessionFactoryOptionsStateStandardImpl implements SessionFac
private boolean wrapResultSetsEnabled;
private TimeZone jdbcTimeZone;
private boolean queryParametersValidationEnabled;
+ private LiteralHandlingMode criteriaLiteralHandlingMode;
private Map sqlFunctions;
@@ -776,6 +777,13 @@ else if ( jdbcTimeZoneValue != null ) {
configurationSettings,
true
);
+
+ this.criteriaLiteralHandlingMode = strategySelector.resolveStrategy(
+ LiteralHandlingMode.class,
+ configurationSettings.get( CRITERIA_LITERAL_HANDLING_MODE ),
+ LiteralHandlingMode.AUTO,
+ (clazz) -> LiteralHandlingMode.AUTO
+ );
}
private static Interceptor determineInterceptor(Map configurationSettings, StrategySelector strategySelector) {
@@ -1216,6 +1224,11 @@ public TimeZone getJdbcTimeZone() {
public boolean isQueryParametersValidationEnabled() {
return this.queryParametersValidationEnabled;
}
+
+ @Override
+ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return this.criteriaLiteralHandlingMode;
+ }
}
@Override
@@ -1544,4 +1557,9 @@ public TimeZone getJdbcTimeZone() {
public boolean isQueryParametersValidationEnabled() {
return options.isQueryParametersValidationEnabled();
}
+
+ @Override
+ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return options.getCriteriaLiteralHandlingMode();
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsImpl.java
index 55e0505f6b..6cd6d4e3d4 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsImpl.java
@@ -28,6 +28,7 @@
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
@@ -127,6 +128,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
private final Map sqlFunctions;
private boolean queryParametersValidationEnabled;
+ private LiteralHandlingMode criteriaLiteralHandlingMode;
public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
this.serviceRegistry = state.getServiceRegistry();
@@ -207,6 +209,7 @@ public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
this.jdbcTimeZone = state.getJdbcTimeZone();
this.queryParametersValidationEnabled = state.isQueryParametersValidationEnabled();
+ this.criteriaLiteralHandlingMode = state.getCriteriaLiteralHandlingMode();
}
@Override
@@ -542,4 +545,9 @@ public TimeZone getJdbcTimeZone() {
public boolean isQueryParametersValidationEnabled() {
return queryParametersValidationEnabled;
}
+
+ @Override
+ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return criteriaLiteralHandlingMode;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsState.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsState.java
index a1158c811c..cb397230f9 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsState.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsState.java
@@ -28,6 +28,7 @@
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
@@ -178,4 +179,8 @@ public interface SessionFactoryOptionsState {
TimeZone getJdbcTimeZone();
boolean isQueryParametersValidationEnabled();
+
+ default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return LiteralHandlingMode.AUTO;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java
index bd62dbfbfc..5daafb23a8 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java
@@ -27,6 +27,7 @@
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
@@ -383,4 +384,9 @@ public TimeZone getJdbcTimeZone() {
public boolean isQueryParametersValidationEnabled() {
return delegate.isQueryParametersValidationEnabled();
}
+
+ @Override
+ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return delegate.getCriteriaLiteralHandlingMode();
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java
index 052f700427..5ddcc351c9 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java
@@ -27,6 +27,7 @@
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
@@ -223,4 +224,8 @@ default boolean doesConnectionProviderDisableAutoCommit() {
default boolean isQueryParametersValidationEnabled(){
return isJpaBootstrap();
}
+
+ default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return LiteralHandlingMode.AUTO;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
index 3ff3d7cd2c..67d224d6dd 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
@@ -1690,4 +1690,24 @@ public interface AvailableSettings {
*
*/
String VALIDATE_QUERY_PARAMETERS = "hibernate.query.validate_parameters";
+
+ /**
+ * By default, Criteria queries uses bind parameters for any literal that is not a numeric value.
+ *
+ * However, to increase the likelihood of JDBC statement caching,
+ * you might want to use bind parameters for numeric values too.
+ * The {@link org.hibernate.query.criteria.LiteralHandlingMode#BIND} mode will use bind variables for any literal value.
+ *
+ * The {@link org.hibernate.query.criteria.LiteralHandlingMode#INLINE} mode will inline literal values as-is.
+ * To prevent SQL injection, never use {@link org.hibernate.query.criteria.LiteralHandlingMode#INLINE} with String variables.
+ * Always use constants with the {@link org.hibernate.query.criteria.LiteralHandlingMode#INLINE} mode.
+ *
+ * Valid options are defined by the {@link org.hibernate.query.criteria.LiteralHandlingMode} enum.
+ *
+ * The default value is {@link org.hibernate.query.criteria.LiteralHandlingMode#AUTO}
+ *
+ * @since 5.2.12
+ * @see org.hibernate.query.criteria.LiteralHandlingMode
+ */
+ String CRITERIA_LITERAL_HANDLING_MODE = "hibernate.criteria.literal_handling_mode";
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
index c293374f6d..029bdbbc26 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -2921,6 +2921,24 @@ public boolean isLegacyLimitHandlerBehaviorEnabled() {
return legacyLimitHandlerBehavior;
}
+ /**
+ * Inline String literal.
+ *
+ * @return escaped String
+ */
+ public String inlineLiteral(String literal) {
+ return String.format( "\'%s\'", escapeLiteral( literal ) );
+ }
+
+ /**
+ * Escape String literal.
+ *
+ * @return escaped String
+ */
+ protected String escapeLiteral(String literal) {
+ return literal.replace("'", "''");
+ }
+
private void resolveLegacyLimitHandlerBehavior(ServiceRegistry serviceRegistry) {
// HHH-11194
// Temporary solution to set whether legacy limit handler behavior should be used.
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java
index fdade45ab2..8d6084de19 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java
@@ -583,4 +583,9 @@ public boolean dropConstraints() {
protected MySQLStorageEngine getDefaultMySQLStorageEngine() {
return MyISAMStorageEngine.INSTANCE;
}
+
+ @Override
+ protected String escapeLiteral(String literal) {
+ return super.escapeLiteral( literal ).replace("\\", "\\\\");
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/LiteralHandlingMode.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/LiteralHandlingMode.java
new file mode 100644
index 0000000000..8b384ce017
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/LiteralHandlingMode.java
@@ -0,0 +1,29 @@
+/*
+ * 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 .
+ */
+package org.hibernate.query.criteria;
+
+/**
+ * This enum defines how literals are handled by JPA Criteria.
+ *
+ * By default ({@code AUTO}), Criteria queries uses bind parameters for any literal that is not a numeric value.
+ *
+ * However, to increase the likelihood of JDBC statement caching,
+ * you might want to use bind parameters for numeric values too.
+ * The {@code BIND} mode will use bind variables for any literal value.
+ *
+ * The {@code INLINE} mode will inline literal values as-is.
+ * To prevent SQL injection, never use {@code INLINE} with String variables.
+ * Always use constants with the {@code INLINE} mode.
+ *
+ * @author Vlad Mihalcea
+ */
+public enum LiteralHandlingMode {
+
+ AUTO,
+ BIND,
+ INLINE
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java
index 674658ae92..74ea10f606 100644
--- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java
+++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java
@@ -14,9 +14,13 @@
import javax.persistence.TypedQuery;
import javax.persistence.criteria.ParameterExpression;
+import org.hibernate.SessionFactory;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.StringHelper;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.type.Type;
@@ -47,6 +51,14 @@ public QueryImplementor compile(CompilableCriteria criteria) {
final Map, ExplicitParameterInfo>> explicitParameterInfoMap = new HashMap<>();
final List implicitParameterBindings = new ArrayList<>();
+ final SessionFactoryImplementor sessionFactory = entityManager.getSessionFactory();
+
+ final LiteralHandlingMode criteriaLiteralHandlingMode = sessionFactory
+ .getSessionFactoryOptions()
+ .getCriteriaLiteralHandlingMode();
+
+ final Dialect dialect = sessionFactory.getServiceRegistry().getService( JdbcServices.class ).getDialect();
+
RenderingContext renderingContext = new RenderingContext() {
private int aliasCount;
private int explicitParameterCount;
@@ -122,6 +134,16 @@ public String getCastType(Class javaType) {
}
return hibernateType.getName();
}
+
+ @Override
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ @Override
+ public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return criteriaLiteralHandlingMode;
+ }
};
return criteria.interpret( renderingContext ).buildCompiledQuery(
diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/RenderingContext.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/RenderingContext.java
index 586c8139d3..871ecb7b37 100644
--- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/RenderingContext.java
+++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/RenderingContext.java
@@ -8,6 +8,9 @@
import javax.persistence.criteria.ParameterExpression;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.query.criteria.LiteralHandlingMode;
+
/**
* Used to provide a context and services to the rendering.
*
@@ -19,7 +22,7 @@ public interface RenderingContext {
*
* @return The generated correlation name
*/
- public String generateAlias();
+ String generateAlias();
/**
* Register parameters explicitly encountered in the criteria query.
@@ -28,7 +31,7 @@ public interface RenderingContext {
*
* @return The JPA-QL parameter name
*/
- public ExplicitParameterInfo registerExplicitParameter(ParameterExpression> criteriaQueryParameter);
+ ExplicitParameterInfo registerExplicitParameter(ParameterExpression> criteriaQueryParameter);
/**
* Register a parameter that was not part of the criteria query (at least not as a parameter).
@@ -38,7 +41,7 @@ public interface RenderingContext {
*
* @return The JPA-QL parameter name
*/
- public String registerLiteralParameterBinding(Object literal, Class javaType);
+ String registerLiteralParameterBinding(Object literal, Class javaType);
/**
* Given a java type, determine the proper cast type name.
@@ -47,5 +50,21 @@ public interface RenderingContext {
*
* @return The cast type name.
*/
- public String getCastType(Class javaType);
+ String getCastType(Class javaType);
+
+ /**
+ * Current Dialect.
+ *
+ * @return Dialect
+ */
+ Dialect getDialect();
+
+ /**
+ * How literals are going to be handled.
+ *
+ * @return literal handling strategy
+ */
+ default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
+ return LiteralHandlingMode.AUTO;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/LiteralExpression.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/LiteralExpression.java
index 2f9d0160af..0a611ec980 100644
--- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/LiteralExpression.java
+++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/LiteralExpression.java
@@ -8,6 +8,7 @@
import java.io.Serializable;
+import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.ParameterRegistry;
import org.hibernate.query.criteria.internal.ValueHandlerFactory;
@@ -46,11 +47,31 @@ public void registerParameters(ParameterRegistry registry) {
@SuppressWarnings({ "unchecked" })
public String render(RenderingContext renderingContext) {
- if ( ValueHandlerFactory.isNumeric( literal ) ) {
- return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
- }
- // else...
+ LiteralHandlingMode literalHandlingMode = renderingContext.getCriteriaLiteralHandlingMode();
+
+ switch ( literalHandlingMode ) {
+ case AUTO:
+ if ( ValueHandlerFactory.isNumeric( literal ) ) {
+ return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal );
+ }
+ else {
+ return bindLiteral( renderingContext );
+ }
+ case BIND:
+ return bindLiteral( renderingContext );
+ case INLINE:
+ Object literalValue = literal;
+ if ( String.class.equals( literal.getClass() ) ) {
+ literalValue = renderingContext.getDialect().inlineLiteral( (String) literal );
+ }
+ return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literalValue );
+ default:
+ throw new IllegalArgumentException( "Unexpected LiteralHandlingMode: " + literalHandlingMode );
+ }
+ }
+
+ private String bindLiteral(RenderingContext renderingContext) {
final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() );
return ':' + parameterName;
}
diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/AbstractCriteriaLiteralHandlingModeTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/AbstractCriteriaLiteralHandlingModeTest.java
new file mode 100644
index 0000000000..246bee0b25
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/AbstractCriteriaLiteralHandlingModeTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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 .
+ */
+package org.hibernate.jpa.test.criteria.literal;
+
+import java.util.List;
+import java.util.Map;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Tuple;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+
+import org.hibernate.testing.RequiresDialect;
+import org.hibernate.test.util.jdbc.PreparedStatementSpyConnectionProvider;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author Vlad Mihalcea
+ */
+public abstract class AbstractCriteriaLiteralHandlingModeTest extends BaseEntityManagerFunctionalTestCase {
+
+ private PreparedStatementSpyConnectionProvider connectionProvider;
+
+ @Override
+ protected Map getConfig() {
+ Map config = super.getConfig();
+ config.put(
+ org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER,
+ connectionProvider
+ );
+ return config;
+ }
+
+ @Override
+ public void buildEntityManagerFactory() throws Exception {
+ connectionProvider = new PreparedStatementSpyConnectionProvider();
+ super.buildEntityManagerFactory();
+ }
+
+ @Override
+ public void releaseResources() {
+ super.releaseResources();
+ connectionProvider.stop();
+ }
+
+ @Override
+ protected Class>[] getAnnotatedClasses() {
+ return new Class[] {
+ Book.class
+ };
+ }
+
+ @Before
+ public void init() {
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ Book book = new Book();
+ book.id = 1;
+ book.name = bookName();
+
+ entityManager.persist( book );
+ } );
+ }
+
+ @Test
+ public void testLiteralHandlingMode() throws Exception {
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+
+ final CriteriaQuery query = cb.createQuery( Tuple.class );
+
+ final Root entity = query.from( Book.class );
+ query.where(
+ cb.and(
+ cb.equal(
+ entity.get( "id" ),
+ cb.literal( 1 )
+ ),
+ cb.equal(
+ entity.get( "name" ),
+ cb.literal( bookName() )
+ )
+ )
+ );
+
+ query.multiselect(
+ cb.literal( "abc" ),
+ entity.get( "name" )
+ );
+
+ connectionProvider.clear();
+ List tuples = entityManager.createQuery( query )
+ .getResultList();
+ assertEquals( 1, tuples.size() );
+
+ assertNotNull( connectionProvider.getPreparedStatement( expectedSQL() ) );
+ } );
+ }
+
+ protected abstract String expectedSQL();
+
+ @Entity(name = "Book")
+ public static class Book {
+
+ @Id
+ private Integer id;
+
+ private String name;
+ }
+
+ protected String bookName() {
+ return "Vlad's High-Performance Java Persistence";
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeAutoTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeAutoTest.java
new file mode 100644
index 0000000000..f9b8133277
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeAutoTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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 .
+ */
+package org.hibernate.jpa.test.criteria.literal;
+
+import org.hibernate.dialect.H2Dialect;
+
+import org.hibernate.testing.RequiresDialect;
+
+import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author Vlad Mihalcea
+ */
+@RequiresDialect(H2Dialect.class)
+public class CriteriaLiteralHandlingModeAutoTest extends AbstractCriteriaLiteralHandlingModeTest {
+
+ protected String expectedSQL() {
+ return "select 'abc' as col_0_0_, abstractcr0_.name as col_1_0_ from Book abstractcr0_ where abstractcr0_.id=1 and abstractcr0_.name=?";
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeBindTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeBindTest.java
new file mode 100644
index 0000000000..9bc5d34a95
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeBindTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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 .
+ */
+package org.hibernate.jpa.test.criteria.literal;
+
+import java.util.Map;
+
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.query.criteria.LiteralHandlingMode;
+
+import org.hibernate.testing.RequiresDialect;
+
+/**
+ * @author Vlad Mihalcea
+ */
+@RequiresDialect(H2Dialect.class)
+public class CriteriaLiteralHandlingModeBindTest extends AbstractCriteriaLiteralHandlingModeTest {
+
+ @Override
+ protected Map getConfig() {
+ Map config = super.getConfig();
+ config.put(
+ AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE,
+ LiteralHandlingMode.BIND
+ );
+ return config;
+ }
+
+ protected String expectedSQL() {
+ return "select 'abc' as col_0_0_, abstractcr0_.name as col_1_0_ from Book abstractcr0_ where abstractcr0_.id=? and abstractcr0_.name=?";
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeInlineTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeInlineTest.java
new file mode 100644
index 0000000000..8d566cecda
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralHandlingModeInlineTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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 .
+ */
+package org.hibernate.jpa.test.criteria.literal;
+
+import java.util.Map;
+
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.query.criteria.LiteralHandlingMode;
+
+import org.hibernate.testing.RequiresDialect;
+
+/**
+ * @author Vlad Mihalcea
+ */
+@RequiresDialect(H2Dialect.class)
+public class CriteriaLiteralHandlingModeInlineTest extends AbstractCriteriaLiteralHandlingModeTest {
+
+ @Override
+ protected Map getConfig() {
+ Map config = super.getConfig();
+ config.put(
+ AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE,
+ LiteralHandlingMode.INLINE
+ );
+ return config;
+ }
+
+ protected String expectedSQL() {
+ return "select 'abc' as col_0_0_, abstractcr0_.name as col_1_0_ from Book abstractcr0_ where abstractcr0_.id=1 and abstractcr0_.name='Vlad''s High-Performance Java Persistence'";
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/MySQLCriteriaLiteralHandlingModeInlineTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/MySQLCriteriaLiteralHandlingModeInlineTest.java
new file mode 100644
index 0000000000..9e008ccf78
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/MySQLCriteriaLiteralHandlingModeInlineTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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 .
+ */
+package org.hibernate.jpa.test.criteria.literal;
+
+import java.util.Map;
+
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.query.criteria.LiteralHandlingMode;
+
+import org.hibernate.testing.RequiresDialect;
+
+/**
+ * @author Vlad Mihalcea
+ */
+@RequiresDialect(MySQLDialect.class)
+public class MySQLCriteriaLiteralHandlingModeInlineTest extends AbstractCriteriaLiteralHandlingModeTest {
+
+ @Override
+ protected Map getConfig() {
+ Map config = super.getConfig();
+ config.put(
+ AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE,
+ LiteralHandlingMode.INLINE
+ );
+ return config;
+ }
+
+ protected String expectedSQL() {
+ return "select 'abc' as col_0_0_, abstractcr0_.name as col_1_0_ from Book abstractcr0_ where abstractcr0_.id=1 and abstractcr0_.name='Vlad\\\\''s High-Performance Java Persistence'";
+ }
+
+ @Override
+ protected String bookName() {
+ return "Vlad\\'s High-Performance Java Persistence";
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/nulliteral/CriteriaLiteralsTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/nulliteral/CriteriaLiteralsTest.java
index d562e58a59..ea84fdb2c7 100644
--- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/nulliteral/CriteriaLiteralsTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/nulliteral/CriteriaLiteralsTest.java
@@ -11,6 +11,7 @@
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
+import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@@ -141,6 +142,50 @@ public void testLiteralsInWhereClause() throws Exception {
} );
}
+ @Test
+ public void testNumericLiteralsInWhereClause() throws Exception {
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ testNumericLiterals(
+ entityManager,
+ "select 'abc' as col_0_0_, criteriali0_.name as col_1_0_ from Book criteriali0_ where criteriali0_.id=1"
+ );
+ } );
+ }
+
+ @Test
+ public void testNumericLiteralsInWhereClauseUsingBindParameters() throws Exception {
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ testNumericLiterals(
+ entityManager,
+ "select 'abc' as col_0_0_, criteriali0_.name as col_1_0_ from Book criteriali0_ where criteriali0_.id=1"
+ );
+ } );
+ }
+
+ private void testNumericLiterals(EntityManager entityManager, String expectedSQL) {
+ final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+
+ final CriteriaQuery query = cb.createQuery( Tuple.class );
+
+ final Root entity = query.from( Book.class );
+ query.where( cb.equal(
+ entity.get( "id" ),
+ cb.literal( 1 )
+ ) );
+
+ query.multiselect(
+ cb.literal( "abc" ),
+ entity.get( "name" )
+ );
+
+ connectionProvider.clear();
+ List tuples = entityManager.createQuery( query )
+ .getResultList();
+ assertEquals( 1, tuples.size() );
+
+ assertNotNull( connectionProvider.getPreparedStatement(expectedSQL) );
+ }
+
@Test
public void testCriteriaParameters() throws Exception {
doInJPA( this::entityManagerFactory, entityManager -> {