Skip to content

Replace derived CriteriaQuery with String-based queries #3653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-3588-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data JPA Parent</name>
4 changes: 2 additions & 2 deletions spring-data-envers/pom.xml
Original file line number Diff line number Diff line change
@@ -5,12 +5,12 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-3588-SNAPSHOT</version>

<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-3588-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

2 changes: 1 addition & 1 deletion spring-data-jpa-distribution/pom.xml
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-3588-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

4 changes: 2 additions & 2 deletions spring-data-jpa/pom.xml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-3588-SNAPSHOT</version>

<name>Spring Data JPA</name>
<description>Spring Data module for JPA repositories.</description>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.0-GH-3588-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

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

protected ParameterBinder createBinder() {
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters());
ParameterBinder createBinder() {
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters(), false);
}

protected Query createQuery(JpaParametersParameterAccessor parameters) {
Original file line number Diff line number Diff line change
@@ -51,7 +51,6 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
private final DeclaredQuery query;
private final Lazy<DeclaredQuery> countQuery;
private final ValueExpressionDelegate valueExpressionDelegate;
private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();
private final QueryRewriter queryRewriter;
private final QuerySortRewriter querySortRewriter;
private final Lazy<ParameterBinder> countParameterBinder;
@@ -121,11 +120,9 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) {

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

QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(sortedQueryString, query);

// it is ok to reuse the binding contained in the ParameterBinder although we create a new query String because the
// parameters in the query do not change.
return parameterBinder.get().bindAndPrepare(query, metadata, accessor);
return parameterBinder.get().bindAndPrepare(query, accessor);
}

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

QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(queryString, query);

countParameterBinder.get().bind(metadata.withQuery(query), accessor, QueryParameterSetter.ErrorHandling.LENIENT);
countParameterBinder.get().bind(new QueryParameterSetter.BindableQuery(query), accessor,
QueryParameterSetter.ErrorHandling.LENIENT);

return query;
}
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAcce
* @param values must not be {@literal null}.
* @param em must not be {@literal null}.
*/
HibernateJpaParametersParameterAccessor(Parameters<?, ?> parameters, Object[] values, EntityManager em) {
HibernateJpaParametersParameterAccessor(JpaParameters parameters, Object[] values, EntityManager em) {

super(parameters, values);

Original file line number Diff line number Diff line change
@@ -15,16 +15,12 @@
*/
package org.springframework.data.jpa.repository.query;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.EntityManager;

import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.support.JpqlQueryTemplates;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.lang.Nullable;

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

private boolean distinct;
private final boolean distinct;
private final ReturnedType returnedType;

/**
* Creates a new {@link JpaCountQueryCreator}.
* Creates a new {@link JpaCountQueryCreator}
*
* @param tree
* @param type
* @param builder
* @param returnedType
* @param provider
* @param templates
* @param em
*/
public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
ParameterMetadataProvider provider) {
public JpaCountQueryCreator(PartTree tree, ReturnedType returnedType, ParameterMetadataProvider provider,
JpqlQueryTemplates templates, EntityManager em) {

super(tree, type, builder, provider);
super(tree, returnedType, provider, templates, em);

this.distinct = tree.isDistinct();
this.returnedType = returnedType;
}

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

return builder.createQuery(Long.class);
}

@Override
@SuppressWarnings("unchecked")
protected CriteriaQuery<? extends Object> complete(@Nullable Predicate predicate, Sort sort,
CriteriaQuery<? extends Object> query, CriteriaBuilder builder, Root<?> root) {

CriteriaQuery<? extends Object> select = query.select(getCountQuery(query, builder, root));
return predicate == null ? select : select.where(predicate);
}
JpqlQueryBuilder.SelectStep selectStep = JpqlQueryBuilder.selectFrom(returnedType.getDomainType());
if (this.distinct) {
selectStep = selectStep.distinct();
}

@SuppressWarnings("rawtypes")
private Expression getCountQuery(CriteriaQuery<?> query, CriteriaBuilder builder, Root<?> root) {
return distinct ? builder.countDistinct(root) : builder.count(root);
return selectStep.count();
}
}
Original file line number Diff line number Diff line change
@@ -15,18 +15,19 @@
*/
package org.springframework.data.jpa.repository.query;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.EntityManager;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

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

private final JpaEntityInformation<?, ?> entityInformation;
private final KeysetScrollPosition scrollPosition;
private final ParameterMetadataProvider provider;
private final List<ParameterBinding> syntheticBindings = new ArrayList<>();

public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
ParameterMetadataProvider provider, JpaEntityInformation<?, ?> entityInformation,
KeysetScrollPosition scrollPosition) {
public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvider provider,
JpqlQueryTemplates templates, JpaEntityInformation<?, ?> entityInformation, KeysetScrollPosition scrollPosition,
EntityManager em) {

super(tree, type, builder, provider);
super(tree, type, provider, templates, em);

this.entityInformation = entityInformation;
this.scrollPosition = scrollPosition;
this.provider = provider;
}

@Override
protected CriteriaQuery<?> complete(@Nullable Predicate predicate, Sort sort, CriteriaQuery<?> query,
CriteriaBuilder builder, Root<?> root) {
public List<ParameterBinding> getBindings() {

List<ParameterBinding> partTreeBindings = super.getBindings();
List<ParameterBinding> bindings = new ArrayList<>(partTreeBindings.size() + this.syntheticBindings.size());
bindings.addAll(partTreeBindings);
bindings.addAll(this.syntheticBindings);

return bindings;
}

@Override
protected JpqlQueryBuilder.AbstractJpqlQuery createQuery(@Nullable JpqlQueryBuilder.Predicate predicate, Sort sort) {

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

CriteriaQuery<?> queryToUse = super.complete(predicate, keysetSpec.sort(), query, builder, root);
JpqlQueryBuilder.Select query = buildQuery(keysetSpec.sort());

AtomicInteger counter = new AtomicInteger(provider.getBindings().size());
JpqlQueryBuilder.Predicate keysetPredicate = keysetSpec.createJpqlPredicate(getFrom(), getEntity(), value -> {

syntheticBindings.add(provider.nextSynthetic(value, scrollPosition));
return placeholder(counter.incrementAndGet());
});
JpqlQueryBuilder.Predicate predicateToUse = getPredicate(predicate, keysetPredicate);

if (predicateToUse != null) {
return query.where(predicateToUse);
}

return query;
}

@Nullable
private static JpqlQueryBuilder.Predicate getPredicate(@Nullable JpqlQueryBuilder.Predicate predicate,
@Nullable JpqlQueryBuilder.Predicate keysetPredicate) {

if (keysetPredicate != null) {
if (queryToUse.getRestriction() != null) {
return queryToUse.where(builder.and(queryToUse.getRestriction(), keysetPredicate));
if (predicate != null) {
return predicate.nest().and(keysetPredicate.nest());
} else {
return keysetPredicate;
}
return queryToUse.where(keysetPredicate);
}

return queryToUse;
return predicate;
}

@Override
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ protected JpaParameters(ParametersSource parametersSource,
super(parametersSource, parameterFactory);
}

private JpaParameters(List<JpaParameter> parameters) {
JpaParameters(List<JpaParameter> parameters) {
super(parameters);
}

Original file line number Diff line number Diff line change
@@ -31,14 +31,21 @@
*/
public class JpaParametersParameterAccessor extends ParametersParameterAccessor {

private final JpaParameters parameters;

/**
* Creates a new {@link ParametersParameterAccessor}.
*
* @param parameters must not be {@literal null}.
* @param values must not be {@literal null}.
*/
public JpaParametersParameterAccessor(Parameters<?, ?> parameters, Object[] values) {
public JpaParametersParameterAccessor(JpaParameters parameters, Object[] values) {
super(parameters, values);
this.parameters = parameters;
}

public JpaParameters getParameters() {
return parameters;
}

@Nullable
Loading