Skip to content

Introduced queryLookupStrategy to @EnableJdbcRepositories. #1187

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
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,8 +27,8 @@
import org.springframework.context.annotation.Import;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean;
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

/**
* Annotation to enable JDBC repositories. Will scan the package of the annotated configuration class for Spring Data
Expand All @@ -39,6 +39,7 @@
* @author Mark Paluch
* @author Fei Dong
* @author Antoine Sauray
* @author Diego Krupitza
* @see AbstractJdbcConfiguration
*/
@Target(ElementType.TYPE)
Expand Down Expand Up @@ -124,11 +125,17 @@
*/
String dataAccessStrategyRef() default "";

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

/**
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
* {@link QueryLookupStrategy.Key#CREATE_IF_NOT_FOUND}.
*/
QueryLookupStrategy.Key queryLookupStrategy() default QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2021 the original author or authors.
* Copyright 2018-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,7 +21,6 @@

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.convert.EntityRowMapper;
Expand All @@ -41,7 +40,6 @@
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryCreationException;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.jdbc.core.RowMapper;
Expand All @@ -51,7 +49,7 @@
import org.springframework.util.Assert;

/**
* {@link QueryLookupStrategy} for JDBC repositories.
* Abstract {@link QueryLookupStrategy} for JDBC repositories.
*
* @author Jens Schauder
* @author Kazuki Shimizu
Expand All @@ -60,8 +58,9 @@
* @author Maciej Walkowiak
* @author Moises Cisneros
* @author Hebert Coelho
* @author Diego Krupitza
*/
class JdbcQueryLookupStrategy implements QueryLookupStrategy {
abstract class JdbcQueryLookupStrategy implements QueryLookupStrategy {

private static final Log LOG = LogFactory.getLog(JdbcQueryLookupStrategy.class);

Expand All @@ -72,12 +71,12 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
private final Dialect dialect;
private final QueryMappingConfiguration queryMappingConfiguration;
private final NamedParameterJdbcOperations operations;
private final BeanFactory beanfactory;
@Nullable private final BeanFactory beanfactory;

JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
BeanFactory beanfactory) {
@Nullable BeanFactory beanfactory) {

Assert.notNull(publisher, "ApplicationEventPublisher must not be null");
Assert.notNull(context, "RelationalMappingContextPublisher must not be null");
Expand All @@ -96,43 +95,209 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
this.beanfactory = beanfactory;
}

/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
/**
* {@link QueryLookupStrategy} to create a query from the method name.
*
* @author Diego Krupitza
*/
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
static class CreateQueryLookupStrategy extends JdbcQueryLookupStrategy {

CreateQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
@Nullable BeanFactory beanfactory) {
super(publisher, callbacks, context, converter, dialect, queryMappingConfiguration, operations, beanfactory);
}

@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {

JdbcQueryMethod queryMethod = getJdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries);

return new PartTreeJdbcQuery(getContext(), queryMethod, getDialect(), getConverter(), getOperations(),
this::createMapper);
}
}

/**
* {@link QueryLookupStrategy} that tries to detect a declared query declared via
* {@link org.springframework.data.jdbc.repository.query.Query} annotation followed by a JPA named query lookup.
*
* @author Diego Krupitza
*/
static class DeclaredQueryLookupStrategy extends JdbcQueryLookupStrategy {

DeclaredQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
@Nullable BeanFactory beanfactory) {
super(publisher, callbacks, context, converter, dialect, queryMappingConfiguration, operations, beanfactory);
}

@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {

JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries,
context);
JdbcQueryMethod queryMethod = getJdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries);

try {
if (namedQueries.hasQuery(queryMethod.getNamedQueryName()) || queryMethod.hasAnnotatedQuery()) {

if (queryMethod.hasAnnotatedQuery() && queryMethod.hasAnnotatedQueryName()) {
LOG.warn(String.format(
"Query method %s is annotated with both, a query and a query name. Using the declared query.", method));
}

StringBasedJdbcQuery query = new StringBasedJdbcQuery(queryMethod, operations, this::createMapper, converter);
query.setBeanFactory(beanfactory);
StringBasedJdbcQuery query = new StringBasedJdbcQuery(queryMethod, getOperations(), this::createMapper,
getConverter());
query.setBeanFactory(getBeanfactory());
return query;
} else {
return new PartTreeJdbcQuery(context, queryMethod, dialect, converter, operations, this::createMapper);
}
} catch (Exception e) {
throw QueryCreationException.create(queryMethod, e);

throw new IllegalStateException(
String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method));
}
}

/**
* {@link QueryLookupStrategy} to try to detect a declared query first (
* {@link org.springframework.data.jdbc.repository.query.Query}, JDBC named query). In case none is found we fall back
* on query creation.
* <p>
* Modified based on original source: {@link org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy}
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Diego Krupitza
*/
static class CreateIfNotFoundQueryLookupStrategy extends JdbcQueryLookupStrategy {

private final DeclaredQueryLookupStrategy lookupStrategy;
private final CreateQueryLookupStrategy createStrategy;

/**
* Creates a new {@link CreateIfNotFoundQueryLookupStrategy}.
*
* @param createStrategy must not be {@literal null}.
* @param lookupStrategy must not be {@literal null}.
*/
public CreateIfNotFoundQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
@Nullable BeanFactory beanfactory, CreateQueryLookupStrategy createStrategy,
DeclaredQueryLookupStrategy lookupStrategy) {

super(publisher, callbacks, context, converter, dialect, queryMappingConfiguration, operations, beanfactory);

Assert.notNull(createStrategy, "CreateQueryLookupStrategy must not be null!");
Assert.notNull(lookupStrategy, "DeclaredQueryLookupStrategy must not be null!");

this.createStrategy = createStrategy;
this.lookupStrategy = lookupStrategy;
}

@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {

try {
return lookupStrategy.resolveQuery(method, repositoryMetadata, projectionFactory, namedQueries);
} catch (IllegalStateException e) {
return createStrategy.resolveQuery(method, repositoryMetadata, projectionFactory, namedQueries);
}
}
}

/**
* Creates a {@link JdbcQueryMethod} based on the parameters
*/
JdbcQueryMethod getJdbcQueryMethod(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
return new JdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries, context);
}

/**
* Creates a {@link QueryLookupStrategy} based on the provided
* {@link org.springframework.data.repository.query.QueryLookupStrategy.Key}.
*
* @param key the key that decides what {@link QueryLookupStrategy} shozld be used.
* @param publisher must not be {@literal null}
* @param callbacks may be {@literal null}
* @param context must not be {@literal null}
* @param converter must not be {@literal null}
* @param dialect must not be {@literal null}
* @param queryMappingConfiguration must not be {@literal null}
* @param operations must not be {@literal null}
* @param beanfactory may be {@literal null}
*/
public static QueryLookupStrategy create(@Nullable Key key, ApplicationEventPublisher publisher,
@Nullable EntityCallbacks callbacks, RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
@Nullable BeanFactory beanfactory) {

Assert.notNull(publisher, "ApplicationEventPublisher must not be null");
Assert.notNull(context, "RelationalMappingContextPublisher must not be null");
Assert.notNull(converter, "JdbcConverter must not be null");
Assert.notNull(dialect, "Dialect must not be null");
Assert.notNull(queryMappingConfiguration, "QueryMappingConfiguration must not be null");
Assert.notNull(operations, "NamedParameterJdbcOperations must not be null");

CreateQueryLookupStrategy createQueryLookupStrategy = new CreateQueryLookupStrategy(publisher, callbacks, context,
converter, dialect, queryMappingConfiguration, operations, beanfactory);

DeclaredQueryLookupStrategy declaredQueryLookupStrategy = new DeclaredQueryLookupStrategy(publisher, callbacks,
context, converter, dialect, queryMappingConfiguration, operations, beanfactory);

Key cleanedKey = key != null ? key : Key.CREATE_IF_NOT_FOUND;

LOG.debug(String.format("Using the queryLookupStrategy %s", cleanedKey));

switch (cleanedKey) {
case CREATE:
return createQueryLookupStrategy;
case USE_DECLARED_QUERY:
return declaredQueryLookupStrategy;
case CREATE_IF_NOT_FOUND:
return new CreateIfNotFoundQueryLookupStrategy(publisher, callbacks, context, converter, dialect,
queryMappingConfiguration, operations, beanfactory, createQueryLookupStrategy, declaredQueryLookupStrategy);
default:
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
}
}

protected ApplicationEventPublisher getPublisher() {
return publisher;
}

protected RelationalMappingContext getContext() {
return context;
}

protected JdbcConverter getConverter() {
return converter;
}

protected Dialect getDialect() {
return dialect;
}

protected NamedParameterJdbcOperations getOperations() {
return operations;
}

@Nullable
protected BeanFactory getBeanfactory() {
return beanfactory;
}

@SuppressWarnings("unchecked")
private RowMapper<Object> createMapper(Class<?> returnedObjectType) {
RowMapper<Object> createMapper(Class<?> returnedObjectType) {

RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(returnedObjectType);

if (persistentEntity == null) {
return (RowMapper<Object>) SingleColumnRowMapper.newInstance(returnedObjectType, converter.getConversionService());
return (RowMapper<Object>) SingleColumnRowMapper.newInstance(returnedObjectType,
converter.getConversionService());
}

return (RowMapper<Object>) determineDefaultMapper(returnedObjectType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,6 +46,7 @@
* @author Christoph Strobl
* @author Mark Paluch
* @author Hebert Coelho
* @author Diego Krupitza
*/
public class JdbcRepositoryFactory extends RepositoryFactorySupport {

Expand Down Expand Up @@ -145,7 +146,7 @@ protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata)
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {

return Optional.of(new JdbcQueryLookupStrategy(publisher, entityCallbacks, context, converter, dialect,
return Optional.of(JdbcQueryLookupStrategy.create(key, publisher, entityCallbacks, context, converter, dialect,
queryMappingConfiguration, operations, beanFactory));
}

Expand Down
Loading