Skip to content

Commit d016d2e

Browse files
schauderodrotbohm
authored andcommitted
DATAJPA-1233 - Parameter setting for count query is no more lenient.
For count queries parameters get set even when it might fail. Exceptions during parameter setting for count queries get ignored.
1 parent d131c95 commit d016d2e

File tree

7 files changed

+321
-42
lines changed

7 files changed

+321
-42
lines changed

src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java

+7-8
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,8 @@
1919
import javax.persistence.Query;
2020
import javax.persistence.Tuple;
2121

22-
import org.springframework.data.repository.query.EvaluationContextProvider;
23-
import org.springframework.data.repository.query.ParameterAccessor;
24-
import org.springframework.data.repository.query.ParametersParameterAccessor;
25-
import org.springframework.data.repository.query.ResultProcessor;
26-
import org.springframework.data.repository.query.ReturnedType;
22+
import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling;
23+
import org.springframework.data.repository.query.*;
2724
import org.springframework.expression.spel.standard.SpelExpressionParser;
2825
import org.springframework.util.Assert;
2926

@@ -105,9 +102,11 @@ protected Query doCreateCountQuery(Object[] values) {
105102
String queryString = countQuery.getQueryString();
106103
EntityManager em = getEntityManager();
107104

108-
return parameterBinder.get().bind(
109-
getQueryMethod().isNativeQuery() ? em.createNativeQuery(queryString) : em.createQuery(queryString, Long.class),
110-
values);
105+
Query query = getQueryMethod().isNativeQuery() //
106+
? em.createNativeQuery(queryString) //
107+
: em.createQuery(queryString, Long.class);
108+
109+
return parameterBinder.get().bind(query, values, ErrorHandling.LENIENT);
111110
}
112111

113112
/**

src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
2424
import org.springframework.data.jpa.provider.QueryExtractor;
25+
import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling;
2526
import org.springframework.data.repository.query.Parameters;
2627
import org.springframework.data.repository.query.QueryCreationException;
2728
import org.springframework.data.repository.query.RepositoryQuery;
@@ -168,6 +169,6 @@ protected TypedQuery<Long> doCreateCountQuery(Object[] values) {
168169
countQuery = em.createQuery(QueryUtils.createCountQueryFor(queryString, countProjection), Long.class);
169170
}
170171

171-
return parameterBinder.get().bind(countQuery, values);
172+
return parameterBinder.get().bind(countQuery, values, ErrorHandling.LENIENT);
172173
}
173174
}

src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import javax.persistence.Query;
1919

20+
import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling;
2021
import org.springframework.data.repository.query.ParametersParameterAccessor;
2122
import org.springframework.util.Assert;
2223

@@ -52,13 +53,17 @@ public ParameterBinder(JpaParameters parameters, Iterable<QueryParameterSetter>
5253
this.parameterSetters = parameterSetters;
5354
}
5455

55-
public <T extends Query> T bind(T jpaQuery, Object[] values) {
56+
public <T extends Query> T bind(T jpaQuery, Object[] values, ErrorHandling errorHandling) {
5657

57-
parameterSetters.forEach(it -> it.setParameter(jpaQuery, values));
58+
parameterSetters.forEach(it -> it.setParameter(jpaQuery, values, errorHandling));
5859

5960
return jpaQuery;
6061
}
6162

63+
public <T extends Query> T bind(T jpaQuery, Object[] values) {
64+
return bind(jpaQuery, values, ErrorHandling.STRICT);
65+
}
66+
6267
/**
6368
* Binds the parameters to the given query and applies special parameter types (e.g. pagination).
6469
*

src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java

+51-11
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18+
import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.LENIENT;
19+
1820
import java.util.Date;
1921
import java.util.function.Function;
2022

@@ -23,6 +25,8 @@
2325
import javax.persistence.TemporalType;
2426
import javax.persistence.criteria.ParameterExpression;
2527

28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2630
import org.springframework.lang.Nullable;
2731
import org.springframework.util.Assert;
2832

@@ -36,10 +40,10 @@
3640
*/
3741
interface QueryParameterSetter {
3842

39-
void setParameter(Query query, Object[] values);
43+
void setParameter(Query query, Object[] values, ErrorHandling errorHandling);
4044

4145
/** Noop implementation */
42-
QueryParameterSetter NOOP = (query, values) -> {};
46+
QueryParameterSetter NOOP = (query, values, errorHandling) -> {};
4347

4448
/**
4549
* {@link QueryParameterSetter} for named or indexed parameters that might have a {@link TemporalType} specified.
@@ -70,7 +74,7 @@ class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter {
7074
* @see org.springframework.data.jpa.repository.query.QueryParameterSetter#setParameter(javax.persistence.Query, java.lang.Object[])
7175
*/
7276
@SuppressWarnings("unchecked")
73-
public void setParameter(Query query, Object[] values) {
77+
public void setParameter(Query query, Object[] values, ErrorHandling errorHandling) {
7478

7579
Object value = valueExtractor.apply(values);
7680

@@ -82,24 +86,33 @@ public void setParameter(Query query, Object[] values) {
8286
// fixed.
8387

8488
if (parameter instanceof ParameterExpression) {
85-
query.setParameter((Parameter<Date>) parameter, (Date) value, temporalType);
89+
errorHandling.execute(() -> query.setParameter((Parameter<Date>) parameter, (Date) value, temporalType));
8690
} else if (parameter.getName() != null && QueryUtils.hasNamedParameter(query)) {
87-
query.setParameter(parameter.getName(), (Date) value, temporalType);
91+
errorHandling.execute(() -> query.setParameter(parameter.getName(), (Date) value, temporalType));
8892
} else {
89-
if (query.getParameters().size() >= parameter.getPosition() || registerExcessParameters(query)) {
90-
query.setParameter(parameter.getPosition(), (Date) value, temporalType);
93+
94+
Integer position = parameter.getPosition();
95+
if (position != null && (query.getParameters().size() >= parameter.getPosition()
96+
|| registerExcessParameters(query) || errorHandling == LENIENT)) {
97+
98+
errorHandling.execute(() -> query.setParameter(parameter.getPosition(), (Date) value, temporalType));
9199
}
92100
}
93101

94102
} else {
95103

96104
if (parameter instanceof ParameterExpression) {
97-
query.setParameter((Parameter<Object>) parameter, value);
105+
errorHandling.execute(() -> query.setParameter((Parameter<Object>) parameter, value));
98106
} else if (parameter.getName() != null && QueryUtils.hasNamedParameter(query)) {
99-
query.setParameter(parameter.getName(), value);
107+
errorHandling.execute(() -> query.setParameter(parameter.getName(), value));
108+
100109
} else {
101-
if (query.getParameters().size() >= parameter.getPosition() || registerExcessParameters(query)) {
102-
query.setParameter(parameter.getPosition(), value);
110+
111+
Integer position = parameter.getPosition();
112+
if (position != null && (query.getParameters().size() >= position || errorHandling == LENIENT
113+
|| registerExcessParameters(query))) {
114+
115+
errorHandling.execute(() -> query.setParameter(position, value));
103116
}
104117
}
105118
}
@@ -117,4 +130,31 @@ private boolean registerExcessParameters(Query query) {
117130
return query.getParameters().size() == 0 && query.getClass().getName().startsWith("org.eclipse");
118131
}
119132
}
133+
134+
enum ErrorHandling {
135+
136+
STRICT {
137+
138+
@Override
139+
public void execute(Runnable block) {
140+
block.run();
141+
}
142+
},
143+
144+
LENIENT {
145+
@Override
146+
public void execute(Runnable block) {
147+
148+
try {
149+
block.run();
150+
} catch (RuntimeException rex) {
151+
LOG.info("Silently ignoring", rex);
152+
}
153+
}
154+
};
155+
156+
private static final Logger LOG = LoggerFactory.getLogger(ErrorHandling.class);
157+
158+
abstract void execute(Runnable block);
159+
}
120160
}

src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

+26-20
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,17 @@
1515
*/
1616
package org.springframework.data.jpa.repository;
1717

18-
import static org.assertj.core.api.Assertions.*;
19-
import static org.springframework.data.domain.Example.*;
20-
import static org.springframework.data.domain.ExampleMatcher.*;
21-
import static org.springframework.data.domain.Sort.Direction.*;
22-
import static org.springframework.data.jpa.domain.Specification.*;
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20+
import static org.springframework.data.domain.Example.of;
21+
import static org.springframework.data.domain.ExampleMatcher.matching;
22+
import static org.springframework.data.domain.Sort.Direction.ASC;
23+
import static org.springframework.data.domain.Sort.Direction.DESC;
2324
import static org.springframework.data.jpa.domain.Specification.not;
25+
import static org.springframework.data.jpa.domain.Specification.where;
2426
import static org.springframework.data.jpa.domain.sample.UserSpecifications.*;
2527

26-
import java.util.ArrayList;
27-
import java.util.Arrays;
28-
import java.util.Collection;
29-
import java.util.Collections;
30-
import java.util.HashSet;
31-
import java.util.List;
32-
import java.util.Set;
28+
import java.util.*;
3329
import java.util.stream.Stream;
3430

3531
import javax.persistence.EntityManager;
@@ -48,14 +44,9 @@
4844
import org.springframework.dao.DataAccessException;
4945
import org.springframework.dao.IncorrectResultSizeDataAccessException;
5046
import org.springframework.dao.InvalidDataAccessApiUsageException;
51-
import org.springframework.data.domain.Example;
52-
import org.springframework.data.domain.ExampleMatcher;
53-
import org.springframework.data.domain.Page;
54-
import org.springframework.data.domain.PageImpl;
55-
import org.springframework.data.domain.PageRequest;
56-
import org.springframework.data.domain.Pageable;
57-
import org.springframework.data.domain.Slice;
58-
import org.springframework.data.domain.Sort;
47+
import org.springframework.data.domain.*;
48+
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
49+
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
5950
import org.springframework.data.domain.Sort.Direction;
6051
import org.springframework.data.domain.Sort.Order;
6152
import org.springframework.data.domain.ExampleMatcher.*;
@@ -2092,6 +2083,21 @@ public void handlesColonsFollowedByIntegerInStringLiteral() {
20922083
assertThat(users).extracting(User::getId).containsExactly(expected.getId());
20932084
}
20942085

2086+
@Test // DATAJPA-1233
2087+
public void handlesCountQueriesWithLessParametersSingleParam() {
2088+
repository.findAllOrderedBySpecialNameSingleParam("Oliver", PageRequest.of(2, 3));
2089+
}
2090+
2091+
@Test // DATAJPA-1233
2092+
public void handlesCountQueriesWithLessParametersMoreThanOne() {
2093+
repository.findAllOrderedBySpecialNameMultipleParams("Oliver", "x", PageRequest.of(2, 3));
2094+
}
2095+
2096+
@Test // DATAJPA-1233
2097+
public void handlesCountQueriesWithLessParametersMoreThanOneIndexed() {
2098+
repository.findAllOrderedBySpecialNameMultipleParamsIndexed("Oliver", "x", PageRequest.of(2, 3));
2099+
}
2100+
20952101
private Page<User> executeSpecWithSort(Sort sort) {
20962102

20972103
flushTestUsers();

0 commit comments

Comments
 (0)