Skip to content

Commit 93272c8

Browse files
mcisnerosb57schauder
authored andcommitted
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.
1 parent a30fbf6 commit 93272c8

File tree

7 files changed

+238
-26
lines changed

7 files changed

+238
-26
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/Query.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* parameters will get bound to the arguments of the annotated method.
3232
*
3333
* @author Jens Schauder
34+
* @author Moises Cisneros
3435
*/
3536
@Retention(RetentionPolicy.RUNTIME)
3637
@Target(ElementType.METHOD)
@@ -41,7 +42,13 @@
4142
/**
4243
* The SQL statement to execute when the annotated method gets invoked.
4344
*/
44-
String value();
45+
String value() default "";
46+
47+
/**
48+
* The named query to be used. If not defined, the name of
49+
* {@code $ domainClass}.${queryMethodName}} will be used.
50+
*/
51+
String name() default "";
4552

4653
/**
4754
* Optional {@link RowMapper} to use to convert the result of the query to domain class instances. Cannot be used

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* @author Oliver Gierke
4444
* @author Mark Paluch
4545
* @author Maciej Walkowiak
46+
* @author Moises Cisneros
4647
*/
4748
@RequiredArgsConstructor
4849
class JdbcQueryLookupStrategy implements QueryLookupStrategy {
@@ -53,6 +54,7 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
5354
private final JdbcConverter converter;
5455
private final QueryMappingConfiguration queryMappingConfiguration;
5556
private final NamedParameterJdbcOperations operations;
57+
5658

5759
/*
5860
* (non-Javadoc)
@@ -62,7 +64,7 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
6264
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
6365
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
6466

65-
JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory);
67+
JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory,namedQueries);
6668

6769
RowMapper<?> mapper = queryMethod.isModifyingQuery() ? null : createMapper(queryMethod);
6870

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,30 @@
2727
import org.springframework.jdbc.core.ResultSetExtractor;
2828
import org.springframework.jdbc.core.RowMapper;
2929
import org.springframework.lang.Nullable;
30+
import org.springframework.util.StringUtils;
31+
import org.springframework.data.repository.core.NamedQueries;
3032

3133
/**
32-
* {@link QueryMethod} implementation that implements a method by executing the query from a {@link Query} annotation on
33-
* that method. Binds method arguments to named parameters in the SQL statement.
34+
* {@link QueryMethod} implementation that implements a method by executing the
35+
* query from a {@link Query} annotation on that method. Binds method arguments
36+
* to named parameters in the SQL statement.
3437
*
3538
* @author Jens Schauder
3639
* @author Kazuki Shimizu
40+
* @author Moises Cisneros
3741
* @deprecated Visibility of this class will be reduced to package private.
3842
*/
3943
@Deprecated
4044
public class JdbcQueryMethod extends QueryMethod {
4145

4246
private final Method method;
47+
private final NamedQueries namedQueries;
4348

44-
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
49+
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
50+
NamedQueries namedQueries) {
4551

4652
super(method, metadata, factory);
47-
53+
this.namedQueries = namedQueries;
4854
this.method = method;
4955
}
5056

@@ -53,13 +59,49 @@ public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFac
5359
*
5460
* @return May be {@code null}.
5561
*/
62+
5663
@Nullable
57-
String getAnnotatedQuery() {
64+
public String getAnnotatedQuery() {
65+
String annotatedValue = getQueryValue();
66+
return StringUtils.hasText(annotatedValue) ? annotatedValue : getNamedQuery();
67+
}
68+
69+
/**
70+
* Returns the annotated query with key value if it exists.
71+
*
72+
* @return May be {@code null}.
73+
*/
74+
@Nullable
75+
String getQueryValue() {
5876
return getMergedAnnotationAttribute("value");
5977
}
6078

79+
80+
/**
81+
* Returns the annotated query name.
82+
*
83+
* @return May be {@code null}.
84+
*/
85+
@Nullable
86+
String getQueryName() {
87+
return getMergedAnnotationAttribute("name");
88+
}
6189
/**
62-
* Returns the class to be used as {@link org.springframework.jdbc.core.RowMapper}
90+
* Returns the annotated query with key name if it exists.
91+
*
92+
* @return May be {@code null}.
93+
*/
94+
@Nullable
95+
String getNamedQuery() {
96+
String annotatedName = getMergedAnnotationAttribute("name");
97+
return (StringUtils.hasText(annotatedName) && this.namedQueries.hasQuery(annotatedName))
98+
? this.namedQueries.getQuery(annotatedName)
99+
: this.namedQueries.getQuery(super.getName());
100+
}
101+
102+
/*
103+
* Returns the class to be used as {@link
104+
* org.springframework.jdbc.core.RowMapper}
63105
*
64106
* @return May be {@code null}.
65107
*/
@@ -69,7 +111,8 @@ Class<? extends RowMapper> getRowMapperClass() {
69111
}
70112

71113
/**
72-
* Returns the class to be used as {@link org.springframework.jdbc.core.ResultSetExtractor}
114+
* Returns the class to be used as
115+
* {@link org.springframework.jdbc.core.ResultSetExtractor}
73116
*
74117
* @return May be {@code null}.
75118
*/

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,32 @@
2424
import org.junit.Rule;
2525
import org.junit.Test;
2626
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.beans.factory.config.PropertiesFactoryBean;
2728
import org.springframework.context.annotation.Bean;
2829
import org.springframework.context.annotation.Configuration;
2930
import org.springframework.context.annotation.Import;
31+
import org.springframework.core.io.ClassPathResource;
3032
import org.springframework.data.annotation.Id;
33+
import org.springframework.data.jdbc.repository.query.Query;
3134
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
32-
import org.springframework.data.jdbc.testing.TestConfiguration;
35+
import org.springframework.data.jdbc.testing.QueryNamedTestConfiguration;
3336
import org.springframework.data.repository.CrudRepository;
37+
import org.springframework.data.repository.core.NamedQueries;
38+
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
3439
import org.springframework.jdbc.core.JdbcTemplate;
3540
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
41+
import org.springframework.test.context.ActiveProfiles;
3642
import org.springframework.test.context.ContextConfiguration;
3743
import org.springframework.test.context.junit4.rules.SpringClassRule;
3844
import org.springframework.test.context.junit4.rules.SpringMethodRule;
3945
import org.springframework.test.jdbc.JdbcTestUtils;
4046
import org.springframework.transaction.annotation.Transactional;
4147

42-
import java.util.HashMap;
48+
import java.io.IOException;
49+
import java.util.List;
50+
51+
import javax.annotation.PostConstruct;
52+
;
4353

4454
/**
4555
* Very simple use cases for creation and usage of JdbcRepositories.
@@ -49,12 +59,14 @@
4959
@ContextConfiguration
5060
@Transactional
5161
public class JdbcRepositoryIntegrationTests {
62+
public static final String DUMMY_SELECT_NAME = "DUMMY.SELECT";
5263

5364
@Configuration
54-
@Import(TestConfiguration.class)
65+
@Import(QueryNamedTestConfiguration.class)
5566
static class Config {
5667

57-
@Autowired JdbcRepositoryFactory factory;
68+
@Autowired
69+
JdbcRepositoryFactory factory;
5870

5971
@Bean
6072
Class<?> testClass() {
@@ -64,8 +76,7 @@ Class<?> testClass() {
6476
@Bean
6577
DummyEntityRepository dummyEntityRepository() {
6678
return factory.getRepository(DummyEntityRepository.class);
67-
}
68-
79+
}
6980
}
7081

7182
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@@ -244,6 +255,13 @@ public void findByIdReturnsEmptyWhenNoneFound() {
244255
assertThat(repository.findById(-1L)).isEmpty();
245256
}
246257

258+
@Test // DATAJDBC-234
259+
public void findAllQueryName() {
260+
261+
repository.save(createDummyEntity());
262+
assertThat(repository.findAllQueryName().size() > 0);
263+
}
264+
247265
private static DummyEntity createDummyEntity() {
248266

249267
DummyEntity entity = new DummyEntity();
@@ -252,11 +270,13 @@ private static DummyEntity createDummyEntity() {
252270
}
253271

254272
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
273+
274+
@Query(name = DUMMY_SELECT_NAME)
275+
List<DummyEntity> findAllQueryName();
255276
}
256277

257278
@Data
258279
static class DummyEntity {
259-
260280
String name;
261281
@Id private Long idProp;
262282
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethodUnitTests.java

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,43 @@
2121

2222
import java.lang.reflect.Method;
2323
import java.sql.ResultSet;
24+
import java.util.Properties;
2425

2526
import org.junit.Test;
2627
import org.springframework.data.jdbc.repository.query.Query;
2728
import org.springframework.data.projection.ProjectionFactory;
29+
import org.springframework.data.repository.core.NamedQueries;
2830
import org.springframework.data.repository.core.RepositoryMetadata;
31+
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
2932
import org.springframework.jdbc.core.RowMapper;
3033

3134
/**
3235
* Unit tests for {@link JdbcQueryMethod}.
3336
*
3437
* @author Jens Schauder
3538
* @author Oliver Gierke
39+
* @author Moises Cisneros
3640
*/
3741
public class JdbcQueryMethodUnitTests {
3842

39-
public static final String DUMMY_SELECT = "SELECT something";
43+
public static final String DUMMY_SELECT_VALUE = "SELECT something";
44+
public static final String DUMMY_SELECT_NAME = "DUMMY.SELECT";
45+
public static final String DUMMY_SELECT_METHOD = "queryWhitoutQueryAnnotation";
46+
public static final String DUMMY_SELECT_NAME_VALUE= "SELECT something NAME AND VALUE";
4047

4148
@Test // DATAJDBC-165
4249
public void returnsSqlStatement() throws NoSuchMethodException {
4350

4451
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
4552

4653
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
47-
48-
JdbcQueryMethod queryMethod = new JdbcQueryMethod(JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"),
49-
metadata, mock(ProjectionFactory.class));
50-
51-
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT);
54+
Properties properties = new Properties();
55+
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
56+
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
57+
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
58+
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"), metadata,
59+
mock(ProjectionFactory.class), nameQueries);
60+
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_VALUE);
5261
}
5362

5463
@Test // DATAJDBC-165
@@ -57,15 +66,91 @@ public void returnsSpecifiedRowMapperClass() throws NoSuchMethodException {
5766
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
5867

5968
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
69+
Properties properties = new Properties();
70+
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
71+
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
6072

61-
JdbcQueryMethod queryMethod = new JdbcQueryMethod(JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"),
62-
metadata, mock(ProjectionFactory.class));
73+
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
74+
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethod"), metadata,
75+
mock(ProjectionFactory.class), nameQueries);
6376

6477
assertThat(queryMethod.getRowMapperClass()).isEqualTo(CustomRowMapper.class);
6578
}
6679

67-
@Query(value = DUMMY_SELECT, rowMapperClass = CustomRowMapper.class)
68-
private void queryMethod() {}
80+
81+
82+
83+
84+
85+
86+
87+
88+
@Test // DATAJDBC-234
89+
public void returnsSqlStatementName() throws NoSuchMethodException {
90+
91+
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
92+
93+
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
94+
95+
Properties properties = new Properties();
96+
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
97+
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
98+
99+
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
100+
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethodName"), metadata,
101+
mock(ProjectionFactory.class), nameQueries);
102+
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_VALUE);
103+
104+
}
105+
@Test // DATAJDBC-234
106+
public void returnsSqlStatementNameAndValue() throws NoSuchMethodException {
107+
108+
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
109+
110+
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
111+
112+
Properties properties = new Properties();
113+
properties.setProperty(DUMMY_SELECT_NAME, DUMMY_SELECT_VALUE);
114+
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
115+
116+
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
117+
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryMethodNameAndValue"), metadata,
118+
mock(ProjectionFactory.class), nameQueries);
119+
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_NAME_VALUE);
120+
121+
}
122+
123+
@Test // DATAJDBC-234
124+
public void returnsNullNoSqlQuery() throws NoSuchMethodException {
125+
126+
RepositoryMetadata metadata = mock(RepositoryMetadata.class);
127+
Properties properties = new Properties();
128+
properties.setProperty(DUMMY_SELECT_METHOD, DUMMY_SELECT_VALUE);
129+
NamedQueries nameQueries = new PropertiesBasedNamedQueries(properties);
130+
131+
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
132+
133+
JdbcQueryMethod queryMethod = new JdbcQueryMethod(
134+
JdbcQueryMethodUnitTests.class.getDeclaredMethod("queryWhitoutQueryAnnotation"), metadata,
135+
mock(ProjectionFactory.class), nameQueries);
136+
assertThat(queryMethod.getAnnotatedQuery()).isEqualTo(DUMMY_SELECT_VALUE);
137+
138+
}
139+
140+
@Query(value = DUMMY_SELECT_VALUE, rowMapperClass = CustomRowMapper.class)
141+
private void queryMethod() {
142+
}
143+
144+
@Query(name = DUMMY_SELECT_NAME)
145+
private void queryMethodName() {
146+
}
147+
148+
@Query(value = DUMMY_SELECT_NAME_VALUE, name = DUMMY_SELECT_NAME)
149+
private void queryMethodNameAndValue() {
150+
}
151+
152+
private void queryWhitoutQueryAnnotation() {
153+
}
69154

70155
private class CustomRowMapper implements RowMapper<Object> {
71156

0 commit comments

Comments
 (0)