Skip to content

Commit 27cae97

Browse files
committed
Replace derived CriteriaQuery with String-based queries.
Introduce new DSL to construct JPQL queries. Refactor ParameterMetadata to PartTreeParameterBinding. Disable Keyset pagination with projections for Eclipselink as Eclipselink doesn't consider type hints for JPQL queries. Closes #3588 Original pull request: #3653
1 parent 0414bb2 commit 27cae97

36 files changed

+2554
-771
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ private Query applyLockMode(Query query, JpaQueryMethod method) {
235235
return lockModeType == null ? query : query.setLockMode(lockModeType);
236236
}
237237

238-
protected ParameterBinder createBinder() {
239-
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters());
238+
ParameterBinder createBinder() {
239+
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters(), false);
240240
}
241241

242242
protected Query createQuery(JpaParametersParameterAccessor parameters) {

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

+3-7
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
5151
private final DeclaredQuery query;
5252
private final Lazy<DeclaredQuery> countQuery;
5353
private final ValueExpressionDelegate valueExpressionDelegate;
54-
private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();
5554
private final QueryRewriter queryRewriter;
5655
private final QuerySortRewriter querySortRewriter;
5756
private final Lazy<ParameterBinder> countParameterBinder;
@@ -121,11 +120,9 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) {
121120

122121
Query query = createJpaQuery(sortedQueryString, sort, accessor.getPageable(), processor.getReturnedType());
123122

124-
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(sortedQueryString, query);
125-
126123
// it is ok to reuse the binding contained in the ParameterBinder although we create a new query String because the
127124
// parameters in the query do not change.
128-
return parameterBinder.get().bindAndPrepare(query, metadata, accessor);
125+
return parameterBinder.get().bindAndPrepare(query, accessor);
129126
}
130127

131128
String getSortedQueryString(Sort sort) {
@@ -152,9 +149,8 @@ protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) {
152149
? em.createNativeQuery(queryString) //
153150
: em.createQuery(queryString, Long.class);
154151

155-
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(queryString, query);
156-
157-
countParameterBinder.get().bind(metadata.withQuery(query), accessor, QueryParameterSetter.ErrorHandling.LENIENT);
152+
countParameterBinder.get().bind(new QueryParameterSetter.BindableQuery(query), accessor,
153+
QueryParameterSetter.ErrorHandling.LENIENT);
158154

159155
return query;
160156
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAcce
5151
* @param values must not be {@literal null}.
5252
* @param em must not be {@literal null}.
5353
*/
54-
HibernateJpaParametersParameterAccessor(Parameters<?, ?> parameters, Object[] values, EntityManager em) {
54+
HibernateJpaParametersParameterAccessor(JpaParameters parameters, Object[] values, EntityManager em) {
5555

5656
super(parameters, values);
5757

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java

+18-28
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,12 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import jakarta.persistence.criteria.CriteriaBuilder;
19-
import jakarta.persistence.criteria.CriteriaQuery;
20-
import jakarta.persistence.criteria.Expression;
21-
import jakarta.persistence.criteria.Predicate;
22-
import jakarta.persistence.criteria.Root;
18+
import jakarta.persistence.EntityManager;
2319

2420
import org.springframework.data.domain.Sort;
21+
import org.springframework.data.jpa.repository.support.JpqlQueryTemplates;
2522
import org.springframework.data.repository.query.ReturnedType;
2623
import org.springframework.data.repository.query.parser.PartTree;
27-
import org.springframework.lang.Nullable;
2824

2925
/**
3026
* Special {@link JpaQueryCreator} that creates a count projecting query.
@@ -36,41 +32,35 @@
3632
*/
3733
public class JpaCountQueryCreator extends JpaQueryCreator {
3834

39-
private boolean distinct;
35+
private final boolean distinct;
36+
private final ReturnedType returnedType;
4037

4138
/**
42-
* Creates a new {@link JpaCountQueryCreator}.
39+
* Creates a new {@link JpaCountQueryCreator}
4340
*
4441
* @param tree
45-
* @param type
46-
* @param builder
42+
* @param returnedType
4743
* @param provider
44+
* @param templates
45+
* @param em
4846
*/
49-
public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
50-
ParameterMetadataProvider provider) {
47+
public JpaCountQueryCreator(PartTree tree, ReturnedType returnedType, ParameterMetadataProvider provider,
48+
JpqlQueryTemplates templates, EntityManager em) {
5149

52-
super(tree, type, builder, provider);
50+
super(tree, returnedType, provider, templates, em);
5351

5452
this.distinct = tree.isDistinct();
53+
this.returnedType = returnedType;
5554
}
5655

5756
@Override
58-
protected CriteriaQuery<? extends Object> createCriteriaQuery(CriteriaBuilder builder, ReturnedType type) {
57+
protected JpqlQueryBuilder.Select buildQuery(Sort sort) {
5958

60-
return builder.createQuery(Long.class);
61-
}
62-
63-
@Override
64-
@SuppressWarnings("unchecked")
65-
protected CriteriaQuery<? extends Object> complete(@Nullable Predicate predicate, Sort sort,
66-
CriteriaQuery<? extends Object> query, CriteriaBuilder builder, Root<?> root) {
67-
68-
CriteriaQuery<? extends Object> select = query.select(getCountQuery(query, builder, root));
69-
return predicate == null ? select : select.where(predicate);
70-
}
59+
JpqlQueryBuilder.SelectStep selectStep = JpqlQueryBuilder.selectFrom(returnedType.getDomainType());
60+
if (this.distinct) {
61+
selectStep = selectStep.distinct();
62+
}
7163

72-
@SuppressWarnings("rawtypes")
73-
private Expression getCountQuery(CriteriaQuery<?> query, CriteriaBuilder builder, Root<?> root) {
74-
return distinct ? builder.countDistinct(root) : builder.count(root);
64+
return selectStep.count();
7565
}
7666
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java

+49-16
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import jakarta.persistence.criteria.CriteriaBuilder;
19-
import jakarta.persistence.criteria.CriteriaQuery;
20-
import jakarta.persistence.criteria.Predicate;
21-
import jakarta.persistence.criteria.Root;
18+
import jakarta.persistence.EntityManager;
2219

20+
import java.util.ArrayList;
2321
import java.util.Collection;
2422
import java.util.LinkedHashSet;
23+
import java.util.List;
2524
import java.util.Set;
25+
import java.util.concurrent.atomic.AtomicInteger;
2626

2727
import org.springframework.data.domain.KeysetScrollPosition;
2828
import org.springframework.data.domain.Sort;
2929
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
30+
import org.springframework.data.jpa.repository.support.JpqlQueryTemplates;
3031
import org.springframework.data.repository.query.ReturnedType;
3132
import org.springframework.data.repository.query.parser.PartTree;
3233
import org.springframework.lang.Nullable;
@@ -41,35 +42,67 @@ class JpaKeysetScrollQueryCreator extends JpaQueryCreator {
4142

4243
private final JpaEntityInformation<?, ?> entityInformation;
4344
private final KeysetScrollPosition scrollPosition;
45+
private final ParameterMetadataProvider provider;
46+
private final List<ParameterBinding> syntheticBindings = new ArrayList<>();
4447

45-
public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
46-
ParameterMetadataProvider provider, JpaEntityInformation<?, ?> entityInformation,
47-
KeysetScrollPosition scrollPosition) {
48+
public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvider provider,
49+
JpqlQueryTemplates templates, JpaEntityInformation<?, ?> entityInformation, KeysetScrollPosition scrollPosition,
50+
EntityManager em) {
4851

49-
super(tree, type, builder, provider);
52+
super(tree, type, provider, templates, em);
5053

5154
this.entityInformation = entityInformation;
5255
this.scrollPosition = scrollPosition;
56+
this.provider = provider;
5357
}
5458

5559
@Override
56-
protected CriteriaQuery<?> complete(@Nullable Predicate predicate, Sort sort, CriteriaQuery<?> query,
57-
CriteriaBuilder builder, Root<?> root) {
60+
public List<ParameterBinding> getBindings() {
61+
62+
List<ParameterBinding> partTreeBindings = super.getBindings();
63+
List<ParameterBinding> bindings = new ArrayList<>(partTreeBindings.size() + this.syntheticBindings.size());
64+
bindings.addAll(partTreeBindings);
65+
bindings.addAll(this.syntheticBindings);
66+
67+
return bindings;
68+
}
69+
70+
@Override
71+
protected JpqlQueryBuilder.AbstractJpqlQuery createQuery(@Nullable JpqlQueryBuilder.Predicate predicate, Sort sort) {
5872

5973
KeysetScrollSpecification<Object> keysetSpec = new KeysetScrollSpecification<>(scrollPosition, sort,
6074
entityInformation);
61-
Predicate keysetPredicate = keysetSpec.createPredicate(root, builder);
6275

63-
CriteriaQuery<?> queryToUse = super.complete(predicate, keysetSpec.sort(), query, builder, root);
76+
JpqlQueryBuilder.Select query = buildQuery(keysetSpec.sort());
77+
78+
AtomicInteger counter = new AtomicInteger(provider.getBindings().size());
79+
JpqlQueryBuilder.Predicate keysetPredicate = keysetSpec.createJpqlPredicate(getFrom(), getEntity(), value -> {
80+
81+
syntheticBindings.add(provider.nextSynthetic(value, scrollPosition));
82+
return JpqlQueryBuilder.expression(render(counter.incrementAndGet()));
83+
});
84+
JpqlQueryBuilder.Predicate predicateToUse = getPredicate(predicate, keysetPredicate);
85+
86+
if (predicateToUse != null) {
87+
return query.where(predicateToUse);
88+
}
89+
90+
return query;
91+
}
92+
93+
@Nullable
94+
private static JpqlQueryBuilder.Predicate getPredicate(@Nullable JpqlQueryBuilder.Predicate predicate,
95+
@Nullable JpqlQueryBuilder.Predicate keysetPredicate) {
6496

6597
if (keysetPredicate != null) {
66-
if (queryToUse.getRestriction() != null) {
67-
return queryToUse.where(builder.and(queryToUse.getRestriction(), keysetPredicate));
98+
if (predicate != null) {
99+
return predicate.nest().and(keysetPredicate.nest());
100+
} else {
101+
return keysetPredicate;
68102
}
69-
return queryToUse.where(keysetPredicate);
70103
}
71104

72-
return queryToUse;
105+
return predicate;
73106
}
74107

75108
@Override

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,21 @@
3131
*/
3232
public class JpaParametersParameterAccessor extends ParametersParameterAccessor {
3333

34+
private final JpaParameters parameters;
35+
3436
/**
3537
* Creates a new {@link ParametersParameterAccessor}.
3638
*
3739
* @param parameters must not be {@literal null}.
3840
* @param values must not be {@literal null}.
3941
*/
40-
public JpaParametersParameterAccessor(Parameters<?, ?> parameters, Object[] values) {
42+
public JpaParametersParameterAccessor(JpaParameters parameters, Object[] values) {
4143
super(parameters, values);
44+
this.parameters = parameters;
45+
}
46+
47+
public JpaParameters getParameters() {
48+
return parameters;
4249
}
4350

4451
@Nullable

0 commit comments

Comments
 (0)