Fix various issues caused by invalid constant assumptions

Reverts #2354
Fixes #2326
Fixes #2816
Fixes #1413
Fixes #1429
Closes #2000
This commit is contained in:
Jan-Willem Gmelig Meyling 2021-06-04 00:29:48 +02:00
parent fe514f40cf
commit ff3d0c8a17
27 changed files with 159 additions and 201 deletions

0
.github/touched-file-so-ci-runs vendored Normal file
View File

0
.github/touched-file-so-ci-runs2 vendored Normal file
View File

View File

@ -56,6 +56,10 @@ A huge thanks goes out to all contributors that made this release possible in th
* [#2663](https://github.com/querydsl/querydsl/issues/2663) - Fix issues with the JPA implementation of `InsertClause`.
* [#2706](https://github.com/querydsl/querydsl/pull/2706) - Fix a memory leak in `TemplateFactory`.
* [#2467](https://github.com/querydsl/querydsl/issues/2467) - Prevent `ExtendedBeanSerializer` from generating `toString` method twice
* [#2326](https://github.com/querydsl/querydsl/issues/2326) - Use JPA indexed parameters instead of HQL's legacy positional parameters
* [#2816](https://github.com/querydsl/querydsl/issues/2816) - Generated JPA query with incorrect argument binding indexes
* [#1413](https://github.com/querydsl/querydsl/issues/1413) - Incorrect parameter values with Hibernate custom types
* [#1429](https://github.com/querydsl/querydsl/issues/1429) - Reusing of constants in JPQL generation causes issues with hibernate query caching
#### Breaking changes
@ -91,6 +95,8 @@ A huge thanks goes out to all contributors that made this release possible in th
* Removal of `HibernateDomainExporter` in `querysql-jpa-codegen`. `HibernateDomainExporter` only supported Hibernate 4, which QueryDSL no longer actively supports. Instead, use the `JPADomainExporter` with Hibernate.
* `ComparableExpression#coalesce` (and subtypes) no longer return a mutable `Coalesce` expression, but instead return a typed expression.
If you need the Coalesce builder, use `new Coalesce<T>().add(expression)` instead.
* `getConstantToNamedLabel`, `getConstantToNumberedLabel` and `getConstantToAllLabels` that were temporarily introduced to `SerializerBase` and `JPQLSerializer`
in QueryDSL 4.3.0 to eventually replace `getConstantToLabel` are now removed in favor of `getConstants`.
#### Deprecations
* `AbstractJPAQuery#fetchResults` and `AbstractJPAQuery#fetchCount` are now deprecated for queries that have multiple group by
@ -99,6 +105,7 @@ A huge thanks goes out to all contributors that made this release possible in th
If you want a reliable way of computing the result count for a paginated result for even the most complicated queries,
we recommend using the [Blaze-Persistence QueryDSL integration](https://persistence.blazebit.com/documentation/1.5/core/manual/en_US/#querydsl-integration).
`BlazeJPAQuery` properly implements both `fetchResults` and `fetchCount` and even comes with a `page` method.
* `getConstantToLabel` which was deprecated in QueryDSL 4.3.0 is no longer deprecated.
#### Dependency updates

View File

@ -13,20 +13,32 @@
*/
package com.querydsl.core.support;
import com.querydsl.core.JoinFlag;
import com.querydsl.core.QueryFlag;
import com.querydsl.core.types.Constant;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Operation;
import com.querydsl.core.types.Operator;
import com.querydsl.core.types.Ops;
import com.querydsl.core.types.ParamExpression;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathType;
import com.querydsl.core.types.Template;
import com.querydsl.core.types.TemplateExpression;
import com.querydsl.core.types.Templates;
import com.querydsl.core.types.Visitor;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.querydsl.core.JoinFlag;
import com.querydsl.core.QueryFlag;
import com.querydsl.core.types.*;
import static java.util.Collections.unmodifiableMap;
/**
* {@code SerializerBase} is a stub for Serializer implementations which serialize query metadata to Strings
*
@ -46,8 +58,9 @@ public abstract class SerializerBase<S extends SerializerBase<S>> implements Vis
private String anonParamPrefix = "_";
private Map<Object, String> constantToNamedLabel;
private Map<Object, Integer> constantToNumberedLabel;
protected final List<Object> constants = new LinkedList<>();
protected final Map<Object, String> constantToLabel = new IdentityHashMap<>();
@SuppressWarnings("unchecked")
private final S self = (S) this;
@ -79,35 +92,8 @@ public abstract class SerializerBase<S extends SerializerBase<S>> implements Vis
return constantPrefix;
}
@Deprecated
public Map<Object, String> getConstantToLabel() {
return getConstantToAllLabels();
}
public Map<Object, String> getConstantToNamedLabel() {
if (constantToNamedLabel == null) {
constantToNamedLabel = new HashMap<Object, String>(4);
}
return constantToNamedLabel;
}
public Map<Object, Integer> getConstantToNumberedLabel() {
if (constantToNumberedLabel == null) {
constantToNumberedLabel = new HashMap<Object, Integer>(4);
}
return constantToNumberedLabel;
}
public Map<Object, String> getConstantToAllLabels() {
Map<Object, String> namedLabels = getConstantToNamedLabel();
Map<Object, Integer> numberedLabels = getConstantToNumberedLabel();
Map<Object, String> allLabels = new HashMap<Object, String>(namedLabels);
for (Map.Entry<Object, Integer> entry : numberedLabels.entrySet()) {
allLabels.put(entry.getKey(), entry.getValue().toString());
}
return unmodifiableMap(allLabels);
return constantToLabel;
}
protected int getLength() {
@ -223,13 +209,37 @@ public abstract class SerializerBase<S extends SerializerBase<S>> implements Vis
}
public void visitConstant(Object constant) {
if (!getConstantToAllLabels().containsKey(constant)) {
final String constLabel = constantPrefix + (getConstantToNamedLabel().size() + 1);
getConstantToNamedLabel().put(constant, constLabel);
append(constLabel);
} else {
append(getConstantToNamedLabel().get(constant));
}
final String constantLabel = getConstantToLabel().computeIfAbsent(constant, this::getConstantLabel);
constants.add(constant);
serializeConstant(constants.size(), constantLabel);
}
/**
* Serialize the constant as parameter to the query. The default implementation writes the
* label name for the constants. Some dialects may replace this by indexed based or
* positional parameterization.
* Dialects may also use this to prefix the parameter with for example ":" or "?".
*
* @param parameterIndex index at which this constant occurs in {@link #getConstants()}
* @param constantLabel label under which this constant occurs in {@link #getConstantToLabel()}
*/
protected void serializeConstant(int parameterIndex, String constantLabel) {
append(constantLabel);
}
/**
* Generate a constant value under which to register a new constant in {@link #getConstantToLabel()}.
*
* @param value the constant value or parameter to create a constant for
* @return the generated label
*/
@NotNull
protected String getConstantLabel(Object value) {
return constantPrefix + (getConstantToLabel().size() + 1);
}
public List<Object> getConstants() {
return constants;
}
@Override
@ -240,8 +250,9 @@ public abstract class SerializerBase<S extends SerializerBase<S>> implements Vis
} else {
paramLabel = paramPrefix + param.getName();
}
getConstantToNamedLabel().put(param, paramLabel);
append(paramLabel);
getConstantToLabel().put(param, paramLabel);
constants.add(param);
serializeConstant(constants.size(), paramLabel);
return null;
}

View File

@ -135,7 +135,7 @@ public abstract class AbstractJDOQuery<T, Q extends AbstractJDOQuery<T, Q>> exte
JDOQLSerializer serializer = new JDOQLSerializer(getTemplates(), source);
serializer.serialize(queryMixin.getMetadata(), forCount, false);
logQuery(serializer.toString(), serializer.getConstantToAllLabels());
logQuery(serializer.toString(), serializer.getConstantToLabel());
// create Query
Query query = persistenceManager.newQuery(serializer.toString());

View File

@ -79,29 +79,20 @@ public final class JDOQLSerializer extends SerializerBase<JDOQLSerializer> {
public JDOQLSerializer(JDOQLTemplates templates, Expression<?> candidate) {
super(templates);
this.candidatePath = candidate;
this.constantToLabel.push(new HashMap<Object,String>());
this.constantToLabel.push(new LinkedHashMap<>());
}
public Expression<?> getCandidatePath() {
return candidatePath;
}
@Override
public List<Object> getConstants() {
return constants;
}
@Override
public Map<Object,String> getConstantToLabel() {
return getConstantToAllLabels();
}
@Override
public Map<Object,String> getConstantToAllLabels() {
return constantToLabel.peek();
}
@Override
public Map<Object,String> getConstantToNamedLabel() {
return constantToLabel.peek();
}
@ -114,7 +105,7 @@ public final class JDOQLSerializer extends SerializerBase<JDOQLSerializer> {
final Predicate having = metadata.getHaving();
final List<OrderSpecifier<?>> orderBy = metadata.getOrderBy();
constantToLabel.push(new HashMap<Object,String>());
constantToLabel.push(new LinkedHashMap<Object,String>());
// select
boolean skippedSelect = false;
@ -197,7 +188,7 @@ public final class JDOQLSerializer extends SerializerBase<JDOQLSerializer> {
}
// parameters
if (!getConstantToAllLabels().isEmpty()) {
if (!getConstantToLabel().isEmpty()) {
insert(position, serializeParameters(metadata.getParams()));
}
@ -222,8 +213,7 @@ public final class JDOQLSerializer extends SerializerBase<JDOQLSerializer> {
final StringBuilder b = new StringBuilder();
b.append(PARAMETERS);
boolean first = true;
final List<Map.Entry<Object, String>> entries = new ArrayList<Map.Entry<Object, String>>(getConstantToAllLabels().entrySet());
entries.sort(comparator);
final Collection<Map.Entry<Object, String>> entries = getConstantToLabel().entrySet();
for (Map.Entry<Object, String> entry : entries) {
if (!first) {
b.append(COMMA);

View File

@ -64,7 +64,7 @@ public class JDODeleteClause implements DeleteClause<JDODeleteClause> {
JDOQLSerializer serializer = new JDOQLSerializer(templates, entity);
serializer.handle(metadata.getWhere());
query.setFilter(serializer.toString());
Map<Object,String> constToLabel = serializer.getConstantToAllLabels();
Map<Object,String> constToLabel = serializer.getConstantToLabel();
try {
if (!constToLabel.isEmpty()) {

View File

@ -379,14 +379,7 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
if (wrap) {
append("(");
}
append("?");
if (!getConstantToAllLabels().containsKey(constant)) {
Integer constLabel = getConstantToNumberedLabel().size() + 1;
getConstantToNumberedLabel().put(constant, constLabel);
append(constLabel.toString());
} else {
append(getConstantToAllLabels().get(constant));
}
super.visitConstant(constant);
if (wrap) {
append(")");
}
@ -398,16 +391,9 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
}
@Override
public Void visit(ParamExpression<?> param, Void context) {
protected void serializeConstant(int parameterIndex, String constantLabel) {
append("?");
if (!getConstantToAllLabels().containsKey(param)) {
final Integer paramLabel = getConstantToNumberedLabel().size() + 1;
getConstantToNumberedLabel().put(param, paramLabel);
append(paramLabel.toString());
} else {
append(getConstantToAllLabels().get(param));
}
return null;
append(Integer.toString(parameterIndex));
}
@Override

View File

@ -197,15 +197,17 @@ public final class NativeSQLSerializer extends SQLSerializer {
first = false;
}
append(")");
} else if (!getConstantToAllLabels().containsKey(constant)) {
final Integer constLabel = getConstantToNumberedLabel().size() + 1;
getConstantToNumberedLabel().put(constant, constLabel);
append("?" + constLabel);
} else {
append("?" + getConstantToAllLabels().get(constant));
super.visitConstant(constant);
}
}
@Override
protected void serializeConstant(int parameterIndex, String constantLabel) {
append("?");
append(Integer.toString(parameterIndex));
}
@Override
protected void visitOperation(Class<?> type, Operator operator, List<? extends Expression<?>> args) {
if (operator == SQLOps.ALL

View File

@ -100,9 +100,9 @@ public abstract class AbstractHibernateQuery<T, Q extends AbstractHibernateQuery
private Query createQuery(@Nullable QueryModifiers modifiers, boolean forCount) {
JPQLSerializer serializer = serialize(forCount);
String queryString = serializer.toString();
logQuery(queryString, serializer.getConstantToAllLabels());
logQuery(queryString);
Query query = session.createQuery(queryString);
HibernateUtil.setConstants(query, serializer.getConstantToNamedLabel(), serializer.getConstantToNumberedLabel(),
HibernateUtil.setConstants(query, serializer.getConstants(),
getMetadata().getParams());
if (fetchSize > 0) {
query.setFetchSize(fetchSize);
@ -208,7 +208,7 @@ public abstract class AbstractHibernateQuery<T, Q extends AbstractHibernateQuery
}
}
protected void logQuery(String queryString, Map<Object, String> parameters) {
protected void logQuery(String queryString) {
if (logger.isLoggable(Level.FINE)) {
String normalizedQuery = queryString.replace('\n', ' ');
logger.fine(normalizedQuery);

View File

@ -13,14 +13,6 @@
*/
package com.querydsl.jpa.hibernate;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.LockMode;
import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import com.querydsl.core.JoinType;
import com.querydsl.core.dml.DeleteClause;
import com.querydsl.core.support.QueryMixin;
@ -31,6 +23,13 @@ import com.querydsl.jpa.HQLTemplates;
import com.querydsl.jpa.JPAQueryMixin;
import com.querydsl.jpa.JPQLSerializer;
import com.querydsl.jpa.JPQLTemplates;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.query.Query;
import java.util.HashMap;
import java.util.Map;
/**
* DeleteClause implementation for Hibernate
@ -75,8 +74,7 @@ public class HibernateDeleteClause implements DeleteClause<HibernateDeleteClause
for (Map.Entry<Path<?>, LockMode> entry : lockModes.entrySet()) {
query.setLockMode(entry.getKey().toString(), entry.getValue());
}
HibernateUtil.setConstants(query, serializer.getConstantToNamedLabel(), serializer.getConstantToNumberedLabel(),
queryMixin.getMetadata().getParams());
HibernateUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
return query.executeUpdate();
}

View File

@ -86,13 +86,12 @@ public class HibernateInsertClause implements
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates, null);
serializer.serializeForInsert(queryMixin.getMetadata(), inserts.isEmpty() ? columns : inserts.keySet(), values, subQuery, inserts);
Map<Object, String> constants = serializer.getConstantToLabel();
Query query = session.createQuery(serializer.toString());
for (Map.Entry<Path<?>, LockMode> entry : lockModes.entrySet()) {
query.setLockMode(entry.getKey().toString(), entry.getValue());
}
HibernateUtil.setConstants(query, constants, queryMixin.getMetadata().getParams());
HibernateUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
return query.executeUpdate();
}

View File

@ -13,16 +13,6 @@
*/
package com.querydsl.jpa.hibernate;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.LockMode;
import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import com.querydsl.core.JoinType;
import com.querydsl.core.dml.UpdateClause;
import com.querydsl.core.support.QueryMixin;
@ -35,6 +25,15 @@ import com.querydsl.jpa.HQLTemplates;
import com.querydsl.jpa.JPAQueryMixin;
import com.querydsl.jpa.JPQLSerializer;
import com.querydsl.jpa.JPQLTemplates;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.query.Query;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* UpdateClause implementation for Hibernate
@ -83,8 +82,7 @@ public class HibernateUpdateClause implements
for (Map.Entry<Path<?>, LockMode> entry : lockModes.entrySet()) {
query.setLockMode(entry.getKey().toString(), entry.getValue());
}
HibernateUtil.setConstants(query, serializer.getConstantToNamedLabel(), serializer.getConstantToNumberedLabel(),
queryMixin.getMetadata().getParams());
HibernateUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
return query.executeUpdate();
}

View File

@ -17,7 +17,6 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import org.hibernate.query.Query;
import org.hibernate.type.*;
import com.querydsl.core.types.ParamExpression;
@ -61,47 +60,26 @@ public final class HibernateUtil {
}
public static void setConstants(
Query<?> query,
Map<Object, String> namedConstants,
org.hibernate.query.Query<?> query,
List<Object> constants,
Map<ParamExpression<?>, Object> params
) {
setConstants(query, namedConstants, new HashMap<Object, Integer>(), params);
}
for (int i = 0; i < constants.size(); i++) {
Object val = constants.get(i);
public static void setConstants(
Query<?> query,
Map<Object, String> namedConstants,
Map<Object, Integer> numberedConstants,
Map<ParamExpression<?>, Object> params
) {
for (Map.Entry<Object, String> entry : namedConstants.entrySet()) {
String key = entry.getValue();
Object val = entry.getKey();
if (val instanceof Param) {
Param<?> param = (Param<?>) val;
val = params.get(val);
if (val == null) {
throw new ParamNotSetException((Param<?>) entry.getKey());
throw new ParamNotSetException(param);
}
}
setValueWithNamedLabel(query, key, val);
}
for (Map.Entry<Object, Integer> entry : numberedConstants.entrySet()) {
Integer key = entry.getValue();
Object val = entry.getKey();
if (val instanceof Param) {
val = params.get(val);
if (val == null) {
throw new ParamNotSetException((Param<?>) entry.getKey());
}
}
setValueWithNumberedLabel(query, key, val);
setValueWithNumberedLabel(query, i + 1, val);
}
}
private static void setValueWithNamedLabel(Query<?> query, String key, Object val) {
private static void setValueWithNumberedLabel(org.hibernate.query.Query<?> query, Integer key, Object val) {
if (val instanceof Collection<?>) {
query.setParameterList(key, (Collection<?>) val);
} else if (val instanceof Object[] && !BUILT_IN.contains(val.getClass())) {
@ -113,16 +91,8 @@ public final class HibernateUtil {
}
}
private static void setValueWithNumberedLabel(Query<?> query, Integer key, Object val) {
if (val instanceof Number && TYPES.containsKey(val.getClass())) {
query.setParameter(key, val, getType(val.getClass()));
} else {
query.setParameter(key, val);
}
}
public static Type getType(Class<?> clazz) {
return TYPES.get(clazz);
}
}

View File

@ -84,11 +84,10 @@ public abstract class AbstractHibernateSQLQuery<T, Q extends AbstractHibernateSQ
private Query createQuery(boolean forCount) {
NativeSQLSerializer serializer = (NativeSQLSerializer) serialize(forCount);
String queryString = serializer.toString();
logQuery(queryString, serializer.getConstantToAllLabels());
logQuery(queryString);
org.hibernate.query.NativeQuery query = session.createSQLQuery(queryString);
// set constants
HibernateUtil.setConstants(query, serializer.getConstantToNamedLabel(), serializer.getConstantToNumberedLabel(),
queryMixin.getMetadata().getParams());
HibernateUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
if (!forCount) {
Map<Expression<?>, List<String>> aliases = serializer.getAliases();
@ -201,7 +200,7 @@ public abstract class AbstractHibernateSQLQuery<T, Q extends AbstractHibernateSQ
}
}
protected void logQuery(String queryString, Map<Object, String> parameters) {
protected void logQuery(String queryString) {
if (logger.isLoggable(Level.FINE)) {
String normalizedQuery = queryString.replace('\n', ' ');
logger.fine(normalizedQuery);

View File

@ -128,9 +128,9 @@ public abstract class AbstractJPAQuery<T, Q extends AbstractJPAQuery<T, Q>> exte
protected Query createQuery(@Nullable QueryModifiers modifiers, boolean forCount) {
JPQLSerializer serializer = serialize(forCount);
String queryString = serializer.toString();
logQuery(queryString, serializer.getConstantToAllLabels());
logQuery(queryString);
Query query = entityManager.createQuery(queryString);
JPAUtil.setConstants(query, serializer.getConstantToAllLabels(), getMetadata().getParams());
JPAUtil.setConstants(query, serializer.getConstants(), getMetadata().getParams());
if (modifiers != null && modifiers.isRestricting()) {
Integer limit = modifiers.getLimitAsInteger();
Integer offset = modifiers.getOffsetAsInteger();
@ -306,7 +306,7 @@ public abstract class AbstractJPAQuery<T, Q extends AbstractJPAQuery<T, Q>> exte
}
protected void logQuery(String queryString, Map<Object, String> parameters) {
protected void logQuery(String queryString) {
if (logger.isLoggable(Level.FINEST)) {
String normalizedQuery = queryString.replace('\n', ' ');
logger.finest(normalizedQuery);

View File

@ -13,8 +13,6 @@
*/
package com.querydsl.jpa.impl;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
@ -60,13 +58,12 @@ public class JPADeleteClause implements DeleteClause<JPADeleteClause> {
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForDelete(queryMixin.getMetadata());
Map<Object,String> constants = serializer.getConstantToAllLabels();
Query query = entityManager.createQuery(serializer.toString());
if (lockMode != null) {
query.setLockMode(lockMode);
}
JPAUtil.setConstants(query, constants, queryMixin.getMetadata().getParams());
JPAUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
return query.executeUpdate();
}

View File

@ -75,13 +75,12 @@ public class JPAInsertClause implements InsertClause<JPAInsertClause> {
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForInsert(queryMixin.getMetadata(), inserts.isEmpty() ? columns : inserts.keySet(), values, subQuery, inserts);
Map<Object,String> constants = serializer.getConstantToLabel();
Query query = entityManager.createQuery(serializer.toString());
if (lockMode != null) {
query.setLockMode(lockMode);
}
JPAUtil.setConstants(query, constants, queryMixin.getMetadata().getParams());
JPAUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
return query.executeUpdate();
}

View File

@ -67,13 +67,12 @@ public class JPAUpdateClause implements UpdateClause<JPAUpdateClause> {
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForUpdate(queryMixin.getMetadata(), updates);
Map<Object,String> constants = serializer.getConstantToAllLabels();
Query query = entityManager.createQuery(serializer.toString());
if (lockMode != null) {
query.setLockMode(lockMode);
}
JPAUtil.setConstants(query, constants, queryMixin.getMetadata().getParams());
JPAUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
return query.executeUpdate();
}

View File

@ -13,6 +13,7 @@
*/
package com.querydsl.jpa.impl;
import java.util.List;
import java.util.Map;
import javax.persistence.Parameter;
@ -33,19 +34,22 @@ public final class JPAUtil {
private JPAUtil() { }
public static void setConstants(Query query, Map<Object,String> constants, Map<ParamExpression<?>, Object> params) {
public static void setConstants(Query query, List<Object> constants, Map<ParamExpression<?>, Object> params) {
boolean hasParameters = !query.getParameters().isEmpty();
for (Map.Entry<Object,String> entry : constants.entrySet()) {
String key = entry.getValue();
Object val = entry.getKey();
if (Param.class.isInstance(val)) {
for (int i = 0; i < constants.size(); i++) {
Object val = constants.get(i);
if (val instanceof Param) {
Param<?> param = (Param<?>) val;
val = params.get(val);
if (val == null) {
throw new ParamNotSetException((Param<?>) entry.getKey());
throw new ParamNotSetException(param);
}
}
if (hasParameters) {
Parameter parameter = query.getParameter(Integer.parseInt(key));
Parameter parameter = query.getParameter(i + 1);
Class parameterType = parameter != null ? parameter.getParameterType() : null;
if (parameterType != null && !parameterType.isInstance(val)) {
if (val instanceof Number && Number.class.isAssignableFrom(parameterType)) {
@ -53,7 +57,8 @@ public final class JPAUtil {
}
}
}
query.setParameter(Integer.parseInt(key), val);
query.setParameter(i + 1, val);
}
}

View File

@ -94,7 +94,7 @@ public abstract class AbstractJPASQLQuery<T, Q extends AbstractJPASQLQuery<T, Q>
private Query createQuery(boolean forCount) {
NativeSQLSerializer serializer = (NativeSQLSerializer) serialize(forCount);
String queryString = serializer.toString();
logQuery(queryString, serializer.getConstantToAllLabels());
logQuery(queryString);
Expression<?> projection = queryMixin.getMetadata().getProjection();
Query query;
@ -152,7 +152,7 @@ public abstract class AbstractJPASQLQuery<T, Q extends AbstractJPASQLQuery<T, Q>
// set constants
JPAUtil.setConstants(query, serializer.getConstantToAllLabels(), queryMixin.getMetadata().getParams());
JPAUtil.setConstants(query, serializer.getConstants(), queryMixin.getMetadata().getParams());
this.projection = null; // necessary when query is reused
if (!forCount && projection instanceof FactoryExpression) {
@ -280,7 +280,7 @@ public abstract class AbstractJPASQLQuery<T, Q extends AbstractJPASQLQuery<T, Q>
}
protected void logQuery(String queryString, Map<Object, String> parameters) {
protected void logQuery(String queryString) {
if (logger.isLoggable(Level.FINE)) {
String normalizedQuery = queryString.replace('\n', ' ');
logger.fine(normalizedQuery);

View File

@ -57,7 +57,7 @@ public class FeaturesTest extends AbstractQueryTest {
public void argumentHandling() {
// Kitty is reused, so it should be used via one named parameter
assertToString(
"cat.name = ?1 or cust.name.firstName = ?2 or kitten.name = ?1",
"cat.name = ?1 or cust.name.firstName = ?2 or kitten.name = ?3",
cat.name.eq("Kitty").or(cust.name.firstName.eq("Hans")).or(kitten.name.eq("Kitty")));
}

View File

@ -55,8 +55,7 @@ public class IntegrationBase extends ParsingTest implements HibernateTest {
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
serializer.serialize(getMetadata(), false, null);
Query query = session.createQuery(serializer.toString());
HibernateUtil.setConstants(query, serializer.getConstantToNamedLabel(),
serializer.getConstantToNumberedLabel(), getMetadata().getParams());
HibernateUtil.setConstants(query, serializer.getConstants(), getMetadata().getParams());
query.list();
} catch (Exception e) {
e.printStackTrace();

View File

@ -51,7 +51,7 @@ public class JPAIntegrationBase extends ParsingTest implements JPATest {
JPQLSerializer serializer = new JPQLSerializer(templates);
serializer.serialize(getMetadata(), false, null);
Query query = em.createQuery(serializer.toString());
JPAUtil.setConstants(query, serializer.getConstantToAllLabels(), getMetadata().getParams());
JPAUtil.setConstants(query, serializer.getConstants(), getMetadata().getParams());
try {
query.getResultList();
} catch (Exception e) {

View File

@ -58,7 +58,7 @@ public class JPQLSerializerTest {
.when(cat.toes.eq(3)).then(3)
.otherwise(4);
serializer.handle(expr);
assertEquals("case when (cat.toes = ?1) then ?1 when (cat.toes = ?2) then ?2 else ?3 end", serializer.toString());
assertEquals("case when (cat.toes = ?1) then ?2 when (cat.toes = ?3) then ?4 else ?5 end", serializer.toString());
}
@Test
@ -69,7 +69,7 @@ public class JPQLSerializerTest {
.when(cat.toes.eq(3)).then(3)
.otherwise(4);
serializer.handle(expr);
assertEquals("case when (cat.toes = ?1) then ?1 when (cat.toes = ?2) then ?2 else 4 end", serializer.toString());
assertEquals("case when (cat.toes = ?1) then ?2 when (cat.toes = ?3) then ?4 else 4 end", serializer.toString());
}
@Test
@ -80,7 +80,7 @@ public class JPQLSerializerTest {
.when(cat.toes.eq(3)).then(cat.id.multiply(3))
.otherwise(4);
serializer.handle(expr);
assertEquals("case when (cat.toes = ?1) then (cat.id * ?1) when (cat.toes = ?2) then (cat.id * ?2) else ?3 end", serializer.toString());
assertEquals("case when (cat.toes = ?1) then (cat.id * ?2) when (cat.toes = ?3) then (cat.id * ?4) else ?5 end", serializer.toString());
}
@Test
@ -91,7 +91,7 @@ public class JPQLSerializerTest {
.when(cat.toes.eq(3)).then(cat.id.multiply(3))
.otherwise(4);
serializer.handle(expr);
assertEquals("case when (cat.toes = ?1) then (cat.id * ?1) when (cat.toes = ?2) then (cat.id * ?2) else 4 end", serializer.toString());
assertEquals("case when (cat.toes = ?1) then (cat.id * ?2) when (cat.toes = ?3) then (cat.id * ?4) else 4 end", serializer.toString());
}
@Test
@ -140,7 +140,7 @@ public class JPQLSerializerTest {
serializer.handle(doublePath.add(1));
serializer.handle(doublePath.between((float) 1.0, 1L));
serializer.handle(doublePath.lt((byte) 1));
for (Object constant : serializer.getConstantToAllLabels().keySet()) {
for (Object constant : serializer.getConstants()) {
assertEquals(Double.class, constant.getClass());
}
}
@ -194,7 +194,7 @@ public class JPQLSerializerTest {
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
serializer.handle(Expressions.stringPath("str").contains("abc!"));
assertEquals("str like ?1 escape '!'", serializer.toString());
assertEquals("%abc!!%", serializer.getConstantToAllLabels().keySet().iterator().next().toString());
assertEquals("%abc!!%", serializer.getConstants().get(0).toString());
}
@Test
@ -202,7 +202,7 @@ public class JPQLSerializerTest {
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
serializer.handle(Expressions.stringPath("str").containsIgnoreCase("ABc!"));
assertEquals("lower(str) like ?1 escape '!'", serializer.toString());
assertEquals("%abc!!%", serializer.getConstantToAllLabels().keySet().iterator().next().toString());
assertEquals("%abc!!%", serializer.getConstants().get(0).toString());
}
@Test
@ -210,7 +210,7 @@ public class JPQLSerializerTest {
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
QCat cat = QCat.cat;
serializer.handle(cat.name.substring(cat.name.length().subtract(1), 1));
assertEquals("substring(cat.name,length(cat.name) + ?1,?2 - (length(cat.name) - ?2))", serializer.toString());
assertEquals("substring(cat.name,length(cat.name) + ?1,?2 - (length(cat.name) - ?3))", serializer.toString());
}
@Test

View File

@ -50,17 +50,17 @@ public class MathTest extends AbstractQueryTest {
@Test
public void add_and_compare() {
assertToString("cat.bodyWeight + ?1 < ?1", cat.bodyWeight.add(10.0).lt(10.0));
assertToString("cat.bodyWeight + ?1 < ?2", cat.bodyWeight.add(10.0).lt(10.0));
}
@Test
public void subtract_and_compare() {
assertToString("cat.bodyWeight - ?1 < ?1", cat.bodyWeight.subtract(10.0).lt(10.0));
assertToString("cat.bodyWeight - ?1 < ?2", cat.bodyWeight.subtract(10.0).lt(10.0));
}
@Test
public void multiply_and_compare() {
assertToString("cat.bodyWeight * ?1 < ?1", cat.bodyWeight.multiply(10.0).lt(10.0));
assertToString("cat.bodyWeight * ?1 < ?2", cat.bodyWeight.multiply(10.0).lt(10.0));
}
@Test

View File

@ -47,8 +47,6 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
protected final LinkedList<Path<?>> constantPaths = new LinkedList<Path<?>>();
protected final List<Object> constants = new ArrayList<Object>();
protected final Set<Path<?>> withAliases = new HashSet<>();
protected final boolean dml;
@ -105,10 +103,6 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
append(templates.quoteIdentifier(table, precededByDot));
}
public List<Object> getConstants() {
return constants;
}
public List<Path<?>> getConstantPaths() {
return constantPaths;
}
@ -801,7 +795,7 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
if (!first) {
append(COMMA);
}
append("?");
serializeConstant(constants.size() + 1, null);
constants.add(o);
if (first && (constantPaths.size() < constants.size())) {
constantPaths.add(null);
@ -823,7 +817,7 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
Expression type = Expressions.constant(typeName);
super.visitOperation(constant.getClass(), SQLOps.CAST, Arrays.<Expression<?>> asList(Q, type));
} else {
append("?");
serializeConstant(constants.size() + 1, null);
}
constants.add(constant);
if (constantPaths.size() < constants.size()) {
@ -834,14 +828,19 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
@Override
public Void visit(ParamExpression<?> param, Void context) {
append("?");
constants.add(param);
serializeConstant(constants.size(), null);
if (constantPaths.size() < constants.size()) {
constantPaths.add(null);
}
return null;
}
@Override
protected void serializeConstant(int parameterIndex, String constantLabel) {
append("?");
}
@Override
public Void visit(Path<?> path, Void context) {
if (dml) {