Skip to content

Commit 4b38241

Browse files
committed
Polishing.
Apply fallbacks requiring fewer dependencies and without requiring BeanFactory for all fallback variants. See #2070
1 parent b0d4962 commit 4b38241

File tree

2 files changed

+74
-49
lines changed

2 files changed

+74
-49
lines changed

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

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
package org.springframework.data.jdbc.repository.support;
1717

1818
import java.io.Serializable;
19+
20+
import org.jspecify.annotations.Nullable;
21+
1922
import org.springframework.beans.factory.BeanFactory;
2023
import org.springframework.context.ApplicationEventPublisher;
2124
import org.springframework.context.ApplicationEventPublisherAware;
@@ -38,6 +41,19 @@
3841
/**
3942
* Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of
4043
* repository factories via Spring configuration.
44+
* <p>
45+
* A partially populated factory bean can use {@link BeanFactory} to resolve missing dependencies, specifically:
46+
* <ul>
47+
* <li>The {@link org.springframework.data.mapping.context.MappingContext} is being derived from
48+
* {@link RelationalMappingContext} if the {@code mappingContext} was not set.</li>
49+
* <li>The {@link NamedParameterJdbcOperations} is looked up from a {@link BeanFactory} if {@code jdbcOperations} was
50+
* not set.</li>
51+
* <li>The {@link QueryMappingConfiguration} is looked up from a {@link BeanFactory} if
52+
* {@code queryMappingConfiguration} was not set. If the {@link BeanFactory} was not set, defaults to
53+
* {@link QueryMappingConfiguration#EMPTY}.</li>
54+
* <li>The {@link DataAccessStrategy} is looked up from a {@link BeanFactory} if {@code dataAccessStrategy} was not set.
55+
* If the {@link BeanFactory} was not set, then it is created using {@link Dialect}.</li>
56+
* </ul>
4157
*
4258
* @author Jens Schauder
4359
* @author Greg Turnquist
@@ -52,15 +68,15 @@
5268
public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
5369
extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> implements ApplicationEventPublisherAware {
5470

55-
private ApplicationEventPublisher publisher;
56-
private BeanFactory beanFactory;
57-
private RelationalMappingContext mappingContext;
58-
private JdbcConverter converter;
59-
private DataAccessStrategy dataAccessStrategy;
60-
private QueryMappingConfiguration queryMappingConfiguration;
61-
private NamedParameterJdbcOperations operations;
62-
private EntityCallbacks entityCallbacks;
63-
private Dialect dialect;
71+
private @Nullable ApplicationEventPublisher publisher;
72+
private @Nullable BeanFactory beanFactory;
73+
private @Nullable RelationalMappingContext mappingContext;
74+
private @Nullable JdbcConverter converter;
75+
private @Nullable DataAccessStrategy dataAccessStrategy;
76+
private @Nullable QueryMappingConfiguration queryMappingConfiguration;
77+
private @Nullable NamedParameterJdbcOperations operations;
78+
private EntityCallbacks entityCallbacks = EntityCallbacks.create();
79+
private @Nullable Dialect dialect;
6480

6581
/**
6682
* Creates a new {@link JdbcRepositoryFactoryBean} for the given repository interface.
@@ -85,6 +101,14 @@ public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
85101
@Override
86102
protected RepositoryFactorySupport doCreateRepositoryFactory() {
87103

104+
Assert.state(this.dataAccessStrategy != null, "DataAccessStrategy is required and must not be null");
105+
Assert.state(this.mappingContext != null, "MappingContext is required and must not be null");
106+
Assert.state(this.converter != null, "RelationalConverter is required and must not be null");
107+
Assert.state(this.dialect != null, "Dialect is required and must not be null");
108+
Assert.state(this.publisher != null, "ApplicationEventPublisher is required and must not be null");
109+
Assert.state(this.operations != null, "NamedParameterJdbcOperations is required and must not be null");
110+
Assert.state(this.queryMappingConfiguration != null, "RelationalConverter is required and must not be null");
111+
88112
JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(dataAccessStrategy, mappingContext,
89113
converter, dialect, publisher, operations);
90114
jdbcRepositoryFactory.setQueryMappingConfiguration(queryMappingConfiguration);
@@ -149,6 +173,7 @@ public void setBeanFactory(BeanFactory beanFactory) {
149173

150174
super.setBeanFactory(beanFactory);
151175

176+
this.entityCallbacks = EntityCallbacks.create(beanFactory);
152177
this.beanFactory = beanFactory;
153178
}
154179

@@ -157,51 +182,53 @@ public void afterPropertiesSet() {
157182

158183
Assert.state(this.converter != null, "RelationalConverter is required and must not be null");
159184

160-
if (mappingContext == null) {
161-
Assert.state(beanFactory != null, "If no MappingContext are set a BeanFactory must be available");
162-
163-
this.mappingContext = beanFactory.getBean(RelationalMappingContext.class);
185+
if (this.mappingContext == null) {
186+
this.mappingContext = this.converter.getMappingContext();
164187
}
165188

166189
if (this.operations == null) {
167190

168-
Assert.state(beanFactory != null, "If no JdbcOperations are set a BeanFactory must be available");
169-
170-
this.operations = beanFactory.getBean(NamedParameterJdbcOperations.class);
191+
Assert.state(this.beanFactory != null, "If no JdbcOperations are set a BeanFactory must be available");
192+
this.operations = this.beanFactory.getBean(NamedParameterJdbcOperations.class);
171193
}
172194

173195
if (this.queryMappingConfiguration == null) {
174-
Assert.state(beanFactory != null, "If no QueryMappingConfiguration are set a BeanFactory must be available");
175196

176-
this.queryMappingConfiguration = beanFactory.getBeanProvider(QueryMappingConfiguration.class)
177-
.getIfAvailable(() -> QueryMappingConfiguration.EMPTY);
197+
if (this.beanFactory == null) {
198+
this.queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
199+
} else {
200+
201+
this.queryMappingConfiguration = beanFactory.getBeanProvider(QueryMappingConfiguration.class)
202+
.getIfAvailable(() -> QueryMappingConfiguration.EMPTY);
203+
}
178204
}
179205

180-
if (this.dataAccessStrategy == null) {
206+
if (this.dataAccessStrategy == null && this.beanFactory != null) {
207+
this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class).getIfAvailable();
208+
}
181209

182-
Assert.state(beanFactory != null, "If no DataAccessStrategy is set a BeanFactory must be available");
210+
if (this.dataAccessStrategy == null) {
183211

184-
this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class) //
185-
.getIfAvailable(() -> {
212+
Assert.state(this.dialect != null, "Dialect is required and must not be null");
186213

187-
Assert.state(this.dialect != null, "Dialect is required and must not be null");
214+
DataAccessStrategyFactory factory = getDataAccessStrategyFactory(this.mappingContext, this.converter,
215+
this.dialect, this.operations, this.queryMappingConfiguration);
188216

189-
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext, this.converter,
190-
this.dialect);
191-
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(this.mappingContext, this.converter);
192-
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(this.operations, this.dialect);
217+
this.dataAccessStrategy = factory.create();
218+
}
193219

194-
DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, this.converter,
195-
this.operations, sqlParametersFactory, insertStrategyFactory, queryMappingConfiguration);
220+
super.afterPropertiesSet();
221+
}
196222

197-
return factory.create();
198-
});
199-
}
223+
private static DataAccessStrategyFactory getDataAccessStrategyFactory(RelationalMappingContext mappingContext,
224+
JdbcConverter converter, Dialect dialect, NamedParameterJdbcOperations operations,
225+
QueryMappingConfiguration queryMapping) {
200226

201-
if (beanFactory != null) {
202-
entityCallbacks = EntityCallbacks.create(beanFactory);
203-
}
227+
SqlGeneratorSource source = new SqlGeneratorSource(mappingContext, converter, dialect);
228+
SqlParametersFactory spf = new SqlParametersFactory(mappingContext, converter);
229+
InsertStrategyFactory isf = new InsertStrategyFactory(operations, dialect);
204230

205-
super.afterPropertiesSet();
231+
return new DataAccessStrategyFactory(source, converter, operations, spf, isf, queryMapping);
206232
}
233+
207234
}

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,19 @@
5757
*/
5858
@MockitoSettings(strictness = Strictness.LENIENT)
5959
@ExtendWith(MockitoExtension.class)
60-
public class JdbcRepositoryFactoryBeanUnitTests {
60+
class JdbcRepositoryFactoryBeanUnitTests {
6161

62-
JdbcRepositoryFactoryBean<DummyEntityRepository, DummyEntity, Long> factoryBean;
62+
private JdbcRepositoryFactoryBean<DummyEntityRepository, DummyEntity, Long> factoryBean;
6363

6464
@Mock DataAccessStrategy dataAccessStrategy;
6565
@Mock ApplicationEventPublisher publisher;
6666
@Mock(answer = Answers.RETURNS_DEEP_STUBS) ListableBeanFactory beanFactory;
6767
@Mock Dialect dialect;
6868

69-
RelationalMappingContext mappingContext;
69+
private RelationalMappingContext mappingContext;
7070

7171
@BeforeEach
72-
public void setUp() {
72+
void setUp() {
7373

7474
this.mappingContext = new JdbcMappingContext();
7575

@@ -90,7 +90,7 @@ public void setUp() {
9090
}
9191

9292
@Test // DATAJDBC-151
93-
public void setsUpBasicInstanceCorrectly() {
93+
void setsUpBasicInstanceCorrectly() {
9494

9595
factoryBean.setDataAccessStrategy(dataAccessStrategy);
9696
factoryBean.setMappingContext(mappingContext);
@@ -104,20 +104,20 @@ public void setsUpBasicInstanceCorrectly() {
104104
}
105105

106106
@Test // DATAJDBC-151
107-
public void requiresListableBeanFactory() {
107+
void requiresListableBeanFactory() {
108108

109109
assertThatExceptionOfType(IllegalArgumentException.class)
110110
.isThrownBy(() -> factoryBean.setBeanFactory(mock(BeanFactory.class)));
111111
}
112112

113113
@Test // DATAJDBC-155
114-
public void afterPropertiesThrowsExceptionWhenNoMappingContextSet() {
114+
void afterPropertiesThrowsExceptionWhenNoMappingContextSet() {
115115

116116
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> factoryBean.setMappingContext(null));
117117
}
118118

119119
@Test // DATAJDBC-155
120-
public void afterPropertiesSetDefaultsNullablePropertiesCorrectly() {
120+
void afterPropertiesSetDefaultsNullablePropertiesCorrectly() {
121121

122122
factoryBean.setConverter(new MappingJdbcConverter(mappingContext, dataAccessStrategy));
123123
factoryBean.setApplicationEventPublisher(publisher);
@@ -128,10 +128,8 @@ public void afterPropertiesSetDefaultsNullablePropertiesCorrectly() {
128128
assertThat(factoryBean.getObject()).isNotNull();
129129
assertThat(ReflectionTestUtils.getField(factoryBean, "dataAccessStrategy"))
130130
.isInstanceOf(DefaultDataAccessStrategy.class);
131-
assertThat(ReflectionTestUtils.getField(factoryBean, "queryMappingConfiguration"))
132-
.isEqualTo(QueryMappingConfiguration.EMPTY);
133-
assertThat(ReflectionTestUtils.getField(factoryBean, "mappingContext"))
134-
.isEqualTo(mappingContext);
131+
assertThat(factoryBean).hasFieldOrPropertyWithValue("queryMappingConfiguration", QueryMappingConfiguration.EMPTY);
132+
assertThat(factoryBean).hasFieldOrPropertyWithValue("mappingContext", mappingContext);
135133
}
136134

137135
private static class DummyEntity {

0 commit comments

Comments
 (0)