Skip to content

Commit e6b3b53

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 0fccd8d commit e6b3b53

36 files changed

+2550
-768
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
@@ -242,8 +242,8 @@ private Query applyLockMode(Query query, JpaQueryMethod method) {
242242
return lockModeType == null ? query : query.setLockMode(lockModeType);
243243
}
244244

245-
protected ParameterBinder createBinder() {
246-
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters());
245+
ParameterBinder createBinder() {
246+
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters(), false);
247247
}
248248

249249
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;
@@ -124,11 +123,9 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) {
124123
String sortedQueryString = getSortedQueryString(sort, returnedType);
125124
Query query = createJpaQuery(sortedQueryString, sort, accessor.getPageable(), returnedType);
126125

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

134131
String getSortedQueryString(Sort sort, ReturnedType returnedType) {
@@ -157,9 +154,8 @@ protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) {
157154
? em.createNativeQuery(queryStringToUse) //
158155
: em.createQuery(queryStringToUse, Long.class);
159156

160-
QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(queryString, query);
161-
162-
countParameterBinder.get().bind(metadata.withQuery(query), accessor, QueryParameterSetter.ErrorHandling.LENIENT);
157+
countParameterBinder.get().bind(new QueryParameterSetter.BindableQuery(query), accessor,
158+
QueryParameterSetter.ErrorHandling.LENIENT);
163159

164160
return query;
165161
}

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

+17-27
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.
@@ -37,39 +33,33 @@
3733
public class JpaCountQueryCreator extends JpaQueryCreator {
3834

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

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

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

+51-16
Original file line numberDiff line numberDiff line change
@@ -15,16 +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;
22+
import java.util.LinkedHashSet;
23+
import java.util.List;
24+
import java.util.Set;
25+
import java.util.concurrent.atomic.AtomicInteger;
2426

2527
import org.springframework.data.domain.KeysetScrollPosition;
2628
import org.springframework.data.domain.Sort;
2729
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
30+
import org.springframework.data.jpa.repository.support.JpqlQueryTemplates;
2831
import org.springframework.data.repository.query.ReturnedType;
2932
import org.springframework.data.repository.query.parser.PartTree;
3033
import org.springframework.lang.Nullable;
@@ -39,35 +42,67 @@ class JpaKeysetScrollQueryCreator extends JpaQueryCreator {
3942

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

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

47-
super(tree, type, builder, provider);
52+
super(tree, type, provider, templates, em);
4853

4954
this.entityInformation = entityInformation;
5055
this.scrollPosition = scrollPosition;
56+
this.provider = provider;
5157
}
5258

5359
@Override
54-
protected CriteriaQuery<?> complete(@Nullable Predicate predicate, Sort sort, CriteriaQuery<?> query,
55-
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) {
5672

5773
KeysetScrollSpecification<Object> keysetSpec = new KeysetScrollSpecification<>(scrollPosition, sort,
5874
entityInformation);
59-
Predicate keysetPredicate = keysetSpec.createPredicate(root, builder);
6075

61-
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) {
6296

6397
if (keysetPredicate != null) {
64-
if (queryToUse.getRestriction() != null) {
65-
return queryToUse.where(builder.and(queryToUse.getRestriction(), keysetPredicate));
98+
if (predicate != null) {
99+
return predicate.nest().and(keysetPredicate.nest());
100+
} else {
101+
return keysetPredicate;
66102
}
67-
return queryToUse.where(keysetPredicate);
68103
}
69104

70-
return queryToUse;
105+
return predicate;
71106
}
72107

73108
@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)