Skip to content

Commit 341700e

Browse files
schaudermp911de
authored andcommitted
DATAJDBC-330 - Move default bean lookup from JdbcRepositoryConfigExtension to JdbcRepositoryFactoryBean.
So far the lookup of `NamedParameterJdbcOperations` and `DataAccessStrategy` could happend before these beans were registered resulting in failure to consider those beans. The logic is now split in two parts: If a bean name is given, this is configured on the BeanDefinition level in the JdbcRepositoryConfigExtension. If no name is give a bean is looked up by type in the JdbcRepositoryFactoryBean. This makes the code even simpler and uses standard Spring features instead of reimplementing them. Original pull request: #115.
1 parent 80e88e0 commit 341700e

File tree

7 files changed

+72
-259
lines changed

7 files changed

+72
-259
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcRepositoryConfigExtension.java

Lines changed: 7 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,14 @@
1515
*/
1616
package org.springframework.data.jdbc.repository.config;
1717

18-
import java.util.ArrayList;
19-
import java.util.Arrays;
20-
import java.util.List;
2118
import java.util.Locale;
22-
import java.util.Optional;
23-
import java.util.function.Supplier;
2419

2520
import org.springframework.beans.factory.ListableBeanFactory;
26-
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
27-
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
28-
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2921
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3022
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
31-
import org.springframework.data.jdbc.core.DataAccessStrategy;
3223
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean;
3324
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
3425
import org.springframework.data.repository.config.RepositoryConfigurationSource;
35-
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
36-
import org.springframework.lang.Nullable;
37-
import org.springframework.util.Assert;
38-
import org.springframework.util.ObjectUtils;
3926
import org.springframework.util.StringUtils;
4027

4128
/**
@@ -87,103 +74,20 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf
8774
}
8875
}
8976

90-
/*
77+
/*
9178
* (non-Javadoc)
9279
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource)
9380
*/
9481
@Override
9582
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
9683

97-
resolveReference(builder, source, "jdbcOperationsRef", "jdbcOperations", NamedParameterJdbcOperations.class, true);
98-
resolveReference(builder, source, "dataAccessStrategyRef", "dataAccessStrategy", DataAccessStrategy.class, false);
99-
}
100-
101-
private void resolveReference(BeanDefinitionBuilder builder, RepositoryConfigurationSource source,
102-
String attributeName, String propertyName, Class<?> classRef, boolean required) {
103-
104-
Optional<String> beanNameRef = source.getAttribute(attributeName).filter(StringUtils::hasText);
105-
106-
String beanName = beanNameRef.orElseGet(() -> determineMatchingBeanName(propertyName, classRef, required));
107-
108-
if (beanName != null) {
109-
builder.addPropertyReference(propertyName, beanName);
110-
} else {
111-
Assert.isTrue(!required,
112-
"The beanName must not be null when requested as 'required'. Please report this as a bug.");
113-
}
114-
115-
}
116-
117-
@Nullable
118-
private String determineMatchingBeanName(String propertyName, Class<?> classRef, boolean required) {
84+
source.getAttribute("jdbcOperationsRef") //
85+
.filter(s -> !StringUtils.isEmpty(s)) //
86+
.ifPresent(s -> builder.addPropertyReference("jdbcOperations", s));
11987

120-
if (this.beanFactory == null) {
121-
return nullOrThrowException(required,
122-
() -> new NoSuchBeanDefinitionException(classRef, "No BeanFactory available."));
123-
}
124-
125-
List<String> beanNames = Arrays.asList(beanFactory.getBeanNamesForType(classRef));
126-
127-
if (beanNames.isEmpty()) {
128-
return nullOrThrowException(required,
129-
() -> new NoSuchBeanDefinitionException(classRef, String.format("No bean of type %s available", classRef)));
130-
}
131-
132-
if (beanNames.size() == 1) {
133-
return beanNames.get(0);
134-
}
135-
136-
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
137-
138-
return nullOrThrowException(required,
139-
() -> new NoSuchBeanDefinitionException(String.format(
140-
"BeanFactory does not implement ConfigurableListableBeanFactory when trying to find bean of type %s.",
141-
classRef)));
142-
}
143-
144-
List<String> primaryBeanNames = getPrimaryBeanDefinitions(beanNames, (ConfigurableListableBeanFactory) beanFactory);
145-
146-
if (primaryBeanNames.size() == 1) {
147-
return primaryBeanNames.get(0);
148-
}
149-
150-
if (primaryBeanNames.size() > 1) {
151-
throw new NoUniqueBeanDefinitionException(classRef, primaryBeanNames.size(),
152-
"more than one 'primary' bean found among candidates: " + primaryBeanNames);
153-
}
154-
155-
for (String beanName : beanNames) {
156-
157-
if (propertyName.equals(beanName)
158-
|| ObjectUtils.containsElement(beanFactory.getAliases(beanName), propertyName)) {
159-
return beanName;
160-
}
161-
}
162-
163-
return nullOrThrowException(required,
164-
() -> new NoSuchBeanDefinitionException(String.format("No bean of name %s found.", propertyName)));
165-
}
166-
167-
private static List<String> getPrimaryBeanDefinitions(List<String> beanNames,
168-
ConfigurableListableBeanFactory beanFactory) {
169-
170-
ArrayList<String> primaryBeanNames = new ArrayList<>();
171-
for (String name : beanNames) {
172-
173-
if (beanFactory.getBeanDefinition(name).isPrimary()) {
174-
primaryBeanNames.add(name);
175-
}
176-
}
177-
return primaryBeanNames;
178-
}
179-
180-
@Nullable
181-
private static String nullOrThrowException(boolean required, Supplier<RuntimeException> exception) {
182-
183-
if (required) {
184-
throw exception.get();
185-
}
186-
return null;
88+
source.getAttribute("dataAccessStrategyRef") //
89+
.filter(s -> !StringUtils.isEmpty(s)) //
90+
.ifPresent(s -> builder.addPropertyReference("dataAccessStrategy", s));
18791
}
18892

18993
}

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.io.Serializable;
1919

20+
import org.springframework.beans.factory.BeanFactory;
2021
import org.springframework.beans.factory.annotation.Autowired;
2122
import org.springframework.context.ApplicationEventPublisher;
2223
import org.springframework.context.ApplicationEventPublisherAware;
@@ -47,6 +48,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
4748
extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> implements ApplicationEventPublisherAware {
4849

4950
private ApplicationEventPublisher publisher;
51+
private BeanFactory beanFactory;
5052
private RelationalMappingContext mappingContext;
5153
private RelationalConverter converter;
5254
private DataAccessStrategy dataAccessStrategy;
@@ -113,7 +115,6 @@ public void setQueryMappingConfiguration(QueryMappingConfiguration queryMappingC
113115
/**
114116
* @param rowMapperMap can be {@literal null}. {@link #afterPropertiesSet()} defaults to {@link RowMapperMap#EMPTY} if
115117
* {@literal null}.
116-
*
117118
* @deprecated use {@link #setQueryMappingConfiguration(QueryMappingConfiguration)} instead.
118119
*/
119120
@Deprecated
@@ -131,6 +132,14 @@ public void setConverter(RelationalConverter converter) {
131132
this.converter = converter;
132133
}
133134

135+
@Override
136+
public void setBeanFactory(BeanFactory beanFactory) {
137+
138+
super.setBeanFactory(beanFactory);
139+
140+
this.beanFactory = beanFactory;
141+
}
142+
134143
/*
135144
* (non-Javadoc)
136145
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet()
@@ -141,17 +150,36 @@ public void afterPropertiesSet() {
141150
Assert.state(this.mappingContext != null, "MappingContext is required and must not be null!");
142151
Assert.state(this.converter != null, "RelationalConverter is required and must not be null!");
143152

144-
if (dataAccessStrategy == null) {
145-
146-
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(mappingContext);
147-
this.dataAccessStrategy = new DefaultDataAccessStrategy(sqlGeneratorSource, mappingContext, converter,
148-
operations);
149-
}
153+
ensureJdbcOperationsIsInitialized();
154+
ensureDataAccessStrategyIsInitialized();
150155

151156
if (queryMappingConfiguration == null) {
152157
this.queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
153158
}
154159

155160
super.afterPropertiesSet();
156161
}
162+
163+
private void ensureJdbcOperationsIsInitialized() {
164+
165+
if (operations != null) {
166+
return;
167+
}
168+
169+
operations = beanFactory.getBean(NamedParameterJdbcOperations.class);
170+
}
171+
172+
private void ensureDataAccessStrategyIsInitialized() {
173+
174+
if (dataAccessStrategy != null) {
175+
return;
176+
}
177+
178+
dataAccessStrategy = beanFactory.getBeanProvider(DataAccessStrategy.class).getIfAvailable(() -> {
179+
180+
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(mappingContext);
181+
return new DefaultDataAccessStrategy(sqlGeneratorSource, mappingContext, converter, operations);
182+
});
183+
}
184+
157185
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.beans.factory.annotation.Autowired;
3333
import org.springframework.context.annotation.Bean;
3434
import org.springframework.context.annotation.Import;
35+
import org.springframework.context.annotation.Primary;
3536
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
3637
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
3738
import org.springframework.data.jdbc.testing.TestConfiguration;
@@ -105,6 +106,7 @@ SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) {
105106
}
106107

107108
@Bean
109+
@Primary
108110
MyBatisDataAccessStrategy dataAccessStrategy(SqlSession sqlSession) {
109111

110112
MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession);

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.beans.factory.annotation.Autowired;
3131
import org.springframework.context.annotation.Bean;
3232
import org.springframework.context.annotation.Import;
33+
import org.springframework.context.annotation.Primary;
3334
import org.springframework.data.jdbc.core.DataAccessStrategy;
3435
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
3536
import org.springframework.data.jdbc.testing.TestConfiguration;
@@ -88,8 +89,10 @@ SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) {
8889
}
8990

9091
@Bean
92+
@Primary
9193
DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, RelationalConverter converter,
9294
SqlSession sqlSession, EmbeddedDatabase db) {
95+
9396
return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, converter,
9497
new NamedParameterJdbcTemplate(db), sqlSession);
9598
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class EnableJdbcRepositoriesIntegrationTests {
7979

8080
@BeforeClass
8181
public static void setup() {
82+
8283
MAPPER_MAP.setAccessible(true);
8384
OPERATIONS.setAccessible(true);
8485
DATA_ACCESS_STRATEGY.setAccessible(true);
@@ -105,8 +106,9 @@ public void customRowMapperConfigurationGetsPickedUp() {
105106

106107
@Test // DATAJDBC-293
107108
public void jdbcOperationsRef() {
109+
108110
NamedParameterJdbcOperations operations = (NamedParameterJdbcOperations) ReflectionUtils.getField(OPERATIONS, factoryBean);
109-
assertThat(operations).isNotSameAs(defaultDataAccessStrategy).isSameAs(qualifierJdbcOperations);
111+
assertThat(operations).isNotSameAs(defaultOperations).isSameAs(qualifierJdbcOperations);
110112

111113
DataAccessStrategy dataAccessStrategy = (DataAccessStrategy) ReflectionUtils.getField(DATA_ACCESS_STRATEGY, factoryBean);
112114
assertThat(dataAccessStrategy).isNotSameAs(defaultDataAccessStrategy).isSameAs(qualifierDataAccessStrategy);

0 commit comments

Comments
 (0)