DATAJDBC-234 - Adds support for named queries stored in property files.
Queries are expected in a properties file on the classpath `META-INF/jdbc-named-queries.properties`. The name of the query is by default `<simple class name of the domain type>.<methodName>`. Alternatively it can be set using the `@Query` annotations `name` attribute. Original pull request: #180.
This commit is contained in:
committed by
Jens Schauder
parent
a30fbf6246
commit
93272c8461
@@ -31,6 +31,7 @@ import org.springframework.jdbc.core.RowMapper;
|
||||
* parameters will get bound to the arguments of the annotated method.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Moises Cisneros
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@@ -41,7 +42,13 @@ public @interface Query {
|
||||
/**
|
||||
* The SQL statement to execute when the annotated method gets invoked.
|
||||
*/
|
||||
String value();
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The named query to be used. If not defined, the name of
|
||||
* {@code $ domainClass}.${queryMethodName}} will be used.
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* Optional {@link RowMapper} to use to convert the result of the query to domain class instances. Cannot be used
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Maciej Walkowiak
|
||||
* @author Moises Cisneros
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class JdbcQueryLookupStrategy implements QueryLookupStrategy {
|
||||
@@ -53,6 +54,7 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
|
||||
private final JdbcConverter converter;
|
||||
private final QueryMappingConfiguration queryMappingConfiguration;
|
||||
private final NamedParameterJdbcOperations operations;
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -62,7 +64,7 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
|
||||
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory);
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory,namedQueries);
|
||||
|
||||
RowMapper<?> mapper = queryMethod.isModifyingQuery() ? null : createMapper(queryMethod);
|
||||
|
||||
|
||||
@@ -27,24 +27,30 @@ import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
|
||||
/**
|
||||
* {@link QueryMethod} implementation that implements a method by executing the query from a {@link Query} annotation on
|
||||
* that method. Binds method arguments to named parameters in the SQL statement.
|
||||
* {@link QueryMethod} implementation that implements a method by executing the
|
||||
* query from a {@link Query} annotation on that method. Binds method arguments
|
||||
* to named parameters in the SQL statement.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Kazuki Shimizu
|
||||
* @author Moises Cisneros
|
||||
* @deprecated Visibility of this class will be reduced to package private.
|
||||
*/
|
||||
@Deprecated
|
||||
public class JdbcQueryMethod extends QueryMethod {
|
||||
|
||||
private final Method method;
|
||||
private final NamedQueries namedQueries;
|
||||
|
||||
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
|
||||
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
|
||||
NamedQueries namedQueries) {
|
||||
|
||||
super(method, metadata, factory);
|
||||
|
||||
this.namedQueries = namedQueries;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@@ -53,13 +59,49 @@ public class JdbcQueryMethod extends QueryMethod {
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
|
||||
@Nullable
|
||||
String getAnnotatedQuery() {
|
||||
return getMergedAnnotationAttribute("value");
|
||||
public String getAnnotatedQuery() {
|
||||
String annotatedValue = getQueryValue();
|
||||
return StringUtils.hasText(annotatedValue) ? annotatedValue : getNamedQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class to be used as {@link org.springframework.jdbc.core.RowMapper}
|
||||
* Returns the annotated query with key value if it exists.
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
String getQueryValue() {
|
||||
return getMergedAnnotationAttribute("value");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the annotated query name.
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
String getQueryName() {
|
||||
return getMergedAnnotationAttribute("name");
|
||||
}
|
||||
/**
|
||||
* Returns the annotated query with key name if it exists.
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
String getNamedQuery() {
|
||||
String annotatedName = getMergedAnnotationAttribute("name");
|
||||
return (StringUtils.hasText(annotatedName) && this.namedQueries.hasQuery(annotatedName))
|
||||
? this.namedQueries.getQuery(annotatedName)
|
||||
: this.namedQueries.getQuery(super.getName());
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the class to be used as {@link
|
||||
* org.springframework.jdbc.core.RowMapper}
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
@@ -69,7 +111,8 @@ public class JdbcQueryMethod extends QueryMethod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class to be used as {@link org.springframework.jdbc.core.ResultSetExtractor}
|
||||
* Returns the class to be used as
|
||||
* {@link org.springframework.jdbc.core.ResultSetExtractor}
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
|
||||
@@ -24,22 +24,32 @@ import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.jdbc.testing.QueryNamedTestConfiguration;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
;
|
||||
|
||||
/**
|
||||
* Very simple use cases for creation and usage of JdbcRepositories.
|
||||
@@ -49,12 +59,14 @@ import java.util.HashMap;
|
||||
@ContextConfiguration
|
||||
@Transactional
|
||||
public class JdbcRepositoryIntegrationTests {
|
||||
public static final String DUMMY_SELECT_NAME = "DUMMY.SELECT";
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
@Import(QueryNamedTestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
@Autowired
|
||||
JdbcRepositoryFactory factory;
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
@@ -64,8 +76,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Bean
|
||||
DummyEntityRepository dummyEntityRepository() {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
|
||||
@@ -244,6 +255,13 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(repository.findById(-1L)).isEmpty();
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-234
|
||||
public void findAllQueryName() {
|
||||
|
||||
repository.save(createDummyEntity());
|
||||
assertThat(repository.findAllQueryName().size() > 0);
|
||||
}
|
||||
|
||||
private static DummyEntity createDummyEntity() {
|
||||
|
||||
DummyEntity entity = new DummyEntity();
|
||||
@@ -252,11 +270,13 @@ public class JdbcRepositoryIntegrationTests {
|
||||
}
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
|
||||
|
||||
@Query(name = DUMMY_SELECT_NAME)
|
||||
List<DummyEntity> findAllQueryName();
|
||||
}
|
||||
|
||||
@Data
|
||||
static class DummyEntity {
|
||||
|
||||
String name;
|
||||
@Id private Long idProp;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,14 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
@@ -33,10 +36,14 @@ import org.springframework.jdbc.core.RowMapper;
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @author Moises Cisneros
|
||||
*/
|
||||
public class JdbcQueryMethodUnitTests {
|
||||
|
||||
public static final String DUMMY_SELECT = "SELECT something";
|
||||
public static final String DUMMY_SELECT_VALUE = "SELECT something";
|
||||
public static final String DUMMY_SELECT_NAME = "DUMMY.SELECT";
|
||||
public static final String DUMMY_SELECT_METHOD = "queryWhitoutQueryAnnotation";
|
||||
public static final String DUMMY_SELECT_NAME_VALUE= "SELECT something NAME AND VALUE";
|
||||
|
||||
@Test // DATAJDBC-165
|
||||
public void returnsSqlStatement() throws NoSuchMethodException {
|
||||
@@ -44,11 +51,13 @@ public class JdbcQueryMethodUnitTests {
|
||||
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
|
||||
|
||||
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"),
|
||||
metadata, mock(ProjectionFactory.class));
|
||||
|
||||
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT);
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
|
||||
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
|
||||
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"), metadata,
|
||||
mock(ProjectionFactory.class), nameQueries);
|
||||
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_VALUE);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-165
|
||||
@@ -57,15 +66,91 @@ public class JdbcQueryMethodUnitTests {
|
||||
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
|
||||
|
||||
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
|
||||
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"),
|
||||
metadata, mock(ProjectionFactory.class));
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
|
||||
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"), metadata,
|
||||
mock(ProjectionFactory.class), nameQueries);
|
||||
|
||||
assertThat(queryMethod.getRowMapperClass()).isEqualTo(CustomRowMapper.class);
|
||||
}
|
||||
|
||||
@Query(value = DUMMY_SELECT, rowMapperClass = CustomRowMapper.class)
|
||||
private void queryMethod() {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test // DATAJDBC-234
|
||||
public void returnsSqlStatementName() throws NoSuchMethodException {
|
||||
|
||||
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
|
||||
|
||||
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
|
||||
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
|
||||
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethodName"), metadata,
|
||||
mock(ProjectionFactory.class), nameQueries);
|
||||
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_VALUE);
|
||||
|
||||
}
|
||||
@Test // DATAJDBC-234
|
||||
public void returnsSqlStatementNameAndValue() throws NoSuchMethodException {
|
||||
|
||||
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
|
||||
|
||||
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
|
||||
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
|
||||
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethodNameAndValue"), metadata,
|
||||
mock(ProjectionFactory.class), nameQueries);
|
||||
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_NAME_VALUE);
|
||||
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-234
|
||||
public void returnsNullNoSqlQuery() throws NoSuchMethodException {
|
||||
|
||||
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(DUMMY_SELECT_METHOD, DUMMY_SELECT_VALUE);
|
||||
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
|
||||
|
||||
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
|
||||
|
||||
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
|
||||
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryWhitoutQueryAnnotation"), metadata,
|
||||
mock(ProjectionFactory.class), nameQueries);
|
||||
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_VALUE);
|
||||
|
||||
}
|
||||
|
||||
@Query(value = DUMMY_SELECT_VALUE, rowMapperClass = CustomRowMapper.class)
|
||||
private void queryMethod() {
|
||||
}
|
||||
|
||||
@Query(name = DUMMY_SELECT_NAME)
|
||||
private void queryMethodName() {
|
||||
}
|
||||
|
||||
@Query(value = DUMMY_SELECT_NAME_VALUE, name = DUMMY_SELECT_NAME)
|
||||
private void queryMethodNameAndValue() {
|
||||
}
|
||||
|
||||
private void queryWhitoutQueryAnnotation() {
|
||||
}
|
||||
|
||||
private class CustomRowMapper implements RowMapper<Object> {
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2017-2019 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.testing;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
|
||||
|
||||
/**
|
||||
* Infrastructure configuration for integration tests.
|
||||
*
|
||||
* @author Moises Cisneros
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan // To pick up configuration classes (per activated profile)
|
||||
@Import(TestConfiguration.class)
|
||||
public class QueryNamedTestConfiguration {
|
||||
|
||||
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
|
||||
@PostConstruct()
|
||||
public void factory() throws IOException {
|
||||
PropertiesFactoryBean properties = new PropertiesFactoryBean();
|
||||
properties.setLocation(new ClassPathResource("META-INF/jdbc-named-queries.properties"));
|
||||
NamedQueries namedQueries = null;
|
||||
properties.afterPropertiesSet();
|
||||
namedQueries = new PropertiesBasedNamedQueries(properties.getObject());
|
||||
factory.setNamedQueries(namedQueries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DUMMY.SELECT=SELECT * FROM DUMMY_ENTITY
|
||||
Reference in New Issue
Block a user