Skip to content
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

Introduce QueryEnhancerSelector to configure which QueryEnhancerFactory to use #3527

Open
wants to merge 3 commits into
base: 4.0.x
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -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-2989-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data JPA Parent</name>
Expand Down
4 changes: 2 additions & 2 deletions spring-data-envers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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-2989-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-2989-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-jpa-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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-2989-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jpa/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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-2989-SNAPSHOT</version>

<name>Spring Data JPA</name>
<description>Spring Data module for JPA repositories.</description>
Expand All @@ -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-2989-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%'
OR p.description LIKE "cost overrun"
""";

query = DeclaredQuery.of(s, false);
enhancer = QueryEnhancerFactory.forQuery(query);
query = DeclaredQuery.ofJpql(s);
enhancer = QueryEnhancerFactory.forQuery(query).create(query);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void doSetup() throws IOException {
select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE
union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE""";

enhancer = new JSqlParserQueryEnhancer(DeclaredQuery.of(s, true));
enhancer = new JSqlParserQueryEnhancer(DeclaredQuery.ofNative(s));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.config.BootstrapMode;
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
Expand Down Expand Up @@ -83,46 +84,39 @@
* Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So
* for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning
* for {@code PersonRepositoryImpl}.
*
* @return
*/
String repositoryImplementationPostfix() default "Impl";

/**
* Configures the location of where to find the Spring Data named queries properties file. Will default to
* {@code META-INF/jpa-named-queries.properties}.
*
* @return
*/
String namedQueriesLocation() default "";

/**
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
* {@link Key#CREATE_IF_NOT_FOUND}.
*
* @return
*/
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;

/**
* Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to
* {@link JpaRepositoryFactoryBean}.
*
* @return
*/
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;

/**
* Configure the repository base class to be used to create repository proxies for this particular configuration.
*
* @return
* @since 1.9
*/
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;

/**
* Configure a specific {@link BeanNameGenerator} to be used when creating the repository beans.
* @return the {@link BeanNameGenerator} to be used or the base {@link BeanNameGenerator} interface to indicate context default.
*
* @return the {@link BeanNameGenerator} to be used or the base {@link BeanNameGenerator} interface to indicate
* context default.
* @since 3.4
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Expand All @@ -132,22 +126,18 @@
/**
* Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories
* discovered through this annotation. Defaults to {@code entityManagerFactory}.
*
* @return
*/
String entityManagerFactoryRef() default "entityManagerFactory";

/**
* Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
* discovered through this annotation. Defaults to {@code transactionManager}.
*
* @return
*/
String transactionManagerRef() default "transactionManager";

/**
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
* repositories infrastructure.
* repository infrastructure.
*/
boolean considerNestedRepositories() default false;

Expand All @@ -169,7 +159,6 @@
* completed its bootstrap. {@link BootstrapMode#DEFERRED} is fundamentally the same as {@link BootstrapMode#LAZY},
* but triggers repository initialization when the application context finishes its bootstrap.
*
* @return
* @since 2.1
*/
BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT;
Expand All @@ -181,4 +170,12 @@
* @return a single character used for escaping.
*/
char escapeCharacter() default '\\';

/**
* Configures the {@link QueryEnhancerSelector} to select a query enhancer for query introspection and transformation.
*
* @return a {@link QueryEnhancerSelector} class providing a no-args constructor.
* @since 4.0
*/
Class<? extends QueryEnhancerSelector> queryEnhancerSelector() default QueryEnhancerSelector.DefaultQueryEnhancerSelector.class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo
builder.addPropertyReference("entityManager", entityManagerRefs.get(source));
builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\'));
builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);

if (source instanceof AnnotationRepositoryConfigurationSource) {
builder.addPropertyValue("queryEnhancerSelector",
source.getAttribute("queryEnhancerSelector", Class.class).orElse(null));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
*/
abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {

private final DeclaredQuery query;
private final Lazy<DeclaredQuery> countQuery;
private final StringQuery query;
private final Lazy<IntrospectedQuery> countQuery;
private final ValueExpressionDelegate valueExpressionDelegate;
private final QueryRewriter queryRewriter;
private final QuerySortRewriter querySortRewriter;
Expand All @@ -64,37 +64,32 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
* @param em must not be {@literal null}.
* @param queryString must not be {@literal null}.
* @param countQueryString must not be {@literal null}.
* @param queryRewriter must not be {@literal null}.
* @param valueExpressionDelegate must not be {@literal null}.
* @param queryConfiguration must not be {@literal null}.
*/
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString,
@Nullable String countQueryString, QueryRewriter queryRewriter, ValueExpressionDelegate valueExpressionDelegate) {
@Nullable String countQueryString, JpaQueryConfiguration queryConfiguration) {

super(method, em);

Assert.hasText(queryString, "Query string must not be null or empty");
Assert.notNull(valueExpressionDelegate, "ValueExpressionDelegate must not be null");
Assert.notNull(queryRewriter, "QueryRewriter must not be null");
Assert.notNull(queryConfiguration, "JpaQueryConfiguration must not be null");

this.valueExpressionDelegate = valueExpressionDelegate;
this.valueExpressionDelegate = queryConfiguration.getValueExpressionDelegate();
this.valueExpressionContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters());
this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), valueExpressionDelegate,
method.isNativeQuery());
this.query = ExpressionBasedStringQuery.create(queryString, method, queryConfiguration);

this.countQuery = Lazy.of(() -> {

if (StringUtils.hasText(countQueryString)) {

return new ExpressionBasedStringQuery(countQueryString, method.getEntityInformation(), valueExpressionDelegate,
method.isNativeQuery());
return ExpressionBasedStringQuery.create(countQueryString, method, queryConfiguration);
}

return query.deriveCountQuery(method.getCountQueryProjection());
return this.query.deriveCountQuery(method.getCountQueryProjection());
});

this.countParameterBinder = Lazy.of(() -> this.createBinder(this.countQuery.get()));

this.queryRewriter = queryRewriter;
this.queryRewriter = queryConfiguration.getQueryRewriter(method);

JpaParameters parameters = method.getParameters();

Expand All @@ -108,7 +103,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri
}
}

Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(),
Assert.isTrue(method.isNativeQuery() || !this.query.usesJdbcStyleParameters(),
"JDBC style parameters (?) are not supported for JPA queries");
}

Expand All @@ -135,7 +130,7 @@ protected ParameterBinder createBinder() {
return createBinder(query);
}

protected ParameterBinder createBinder(DeclaredQuery query) {
protected ParameterBinder createBinder(IntrospectedQuery query) {
return ParameterBinderFactory.createQueryAwareBinder(getQueryMethod().getParameters(), query,
valueExpressionDelegate, valueExpressionContextProvider);
}
Expand All @@ -159,14 +154,14 @@ protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) {
/**
* @return the query
*/
public DeclaredQuery getQuery() {
public EntityQuery getQuery() {
return query;
}

/**
* @return the countQuery
*/
public DeclaredQuery getCountQuery() {
public IntrospectedQuery getCountQuery() {
return countQuery.get();
}

Expand Down Expand Up @@ -207,16 +202,15 @@ protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nulla
}

String applySorting(CachableQuery cachableQuery) {

return QueryEnhancerFactory.forQuery(cachableQuery.getDeclaredQuery())
return cachableQuery.getDeclaredQuery().getQueryEnhancer()
.rewrite(new DefaultQueryRewriteInformation(cachableQuery.getSort(), cachableQuery.getReturnedType()));
}

/**
* Query Sort Rewriter interface.
*/
interface QuerySortRewriter {
String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType);
String getSorted(StringQuery query, Sort sort, ReturnedType returnedType);
}

/**
Expand All @@ -226,25 +220,24 @@ enum SimpleQuerySortRewriter implements QuerySortRewriter {

INSTANCE;

public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {

return QueryEnhancerFactory.forQuery(query).rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {
return query.getQueryEnhancer().rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
}
}

static class UnsortedCachingQuerySortRewriter implements QuerySortRewriter {

private volatile String cachedQueryString;

public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {

if (sort.isSorted()) {
throw new UnsupportedOperationException("NoOpQueryCache does not support sorting");
}

String cachedQueryString = this.cachedQueryString;
if (cachedQueryString == null) {
this.cachedQueryString = cachedQueryString = QueryEnhancerFactory.forQuery(query)
this.cachedQueryString = cachedQueryString = query.getQueryEnhancer()
.rewrite(new DefaultQueryRewriteInformation(sort, returnedType));
}

Expand All @@ -263,7 +256,7 @@ class CachingQuerySortRewriter implements QuerySortRewriter {
private volatile String cachedQueryString;

@Override
public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
public String getSorted(StringQuery query, Sort sort, ReturnedType returnedType) {

if (sort.isUnsorted()) {

Expand All @@ -288,21 +281,21 @@ public String getSorted(DeclaredQuery query, Sort sort, ReturnedType returnedTyp
*/
static class CachableQuery {

private final DeclaredQuery declaredQuery;
private final StringQuery query;
private final String queryString;
private final Sort sort;
private final ReturnedType returnedType;

CachableQuery(DeclaredQuery query, Sort sort, ReturnedType returnedType) {
CachableQuery(StringQuery query, Sort sort, ReturnedType returnedType) {

this.declaredQuery = query;
this.query = query;
this.queryString = query.getQueryString();
this.sort = sort;
this.returnedType = returnedType;
}

DeclaredQuery getDeclaredQuery() {
return declaredQuery;
StringQuery getDeclaredQuery() {
return query;
}

Sort getSort() {
Expand Down
Loading