Browse Source

Polishing.

Apply fallbacks requiring fewer dependencies and without requiring BeanFactory for all fallback variants.

See #2070
pull/2076/merge
Mark Paluch 5 months ago
parent
commit
4b38241ef1
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 101
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
  2. 22
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java

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

@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
package org.springframework.data.jdbc.repository.support;
import java.io.Serializable;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
@ -38,6 +41,19 @@ import org.springframework.util.Assert; @@ -38,6 +41,19 @@ import org.springframework.util.Assert;
/**
* Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of
* repository factories via Spring configuration.
* <p>
* A partially populated factory bean can use {@link BeanFactory} to resolve missing dependencies, specifically:
* <ul>
* <li>The {@link org.springframework.data.mapping.context.MappingContext} is being derived from
* {@link RelationalMappingContext} if the {@code mappingContext} was not set.</li>
* <li>The {@link NamedParameterJdbcOperations} is looked up from a {@link BeanFactory} if {@code jdbcOperations} was
* not set.</li>
* <li>The {@link QueryMappingConfiguration} is looked up from a {@link BeanFactory} if
* {@code queryMappingConfiguration} was not set. If the {@link BeanFactory} was not set, defaults to
* {@link QueryMappingConfiguration#EMPTY}.</li>
* <li>The {@link DataAccessStrategy} is looked up from a {@link BeanFactory} if {@code dataAccessStrategy} was not set.
* If the {@link BeanFactory} was not set, then it is created using {@link Dialect}.</li>
* </ul>
*
* @author Jens Schauder
* @author Greg Turnquist
@ -52,15 +68,15 @@ import org.springframework.util.Assert; @@ -52,15 +68,15 @@ import org.springframework.util.Assert;
public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
private BeanFactory beanFactory;
private RelationalMappingContext mappingContext;
private JdbcConverter converter;
private DataAccessStrategy dataAccessStrategy;
private QueryMappingConfiguration queryMappingConfiguration;
private NamedParameterJdbcOperations operations;
private EntityCallbacks entityCallbacks;
private Dialect dialect;
private @Nullable ApplicationEventPublisher publisher;
private @Nullable BeanFactory beanFactory;
private @Nullable RelationalMappingContext mappingContext;
private @Nullable JdbcConverter converter;
private @Nullable DataAccessStrategy dataAccessStrategy;
private @Nullable QueryMappingConfiguration queryMappingConfiguration;
private @Nullable NamedParameterJdbcOperations operations;
private EntityCallbacks entityCallbacks = EntityCallbacks.create();
private @Nullable Dialect dialect;
/**
* Creates a new {@link JdbcRepositoryFactoryBean} for the given repository interface.
@ -85,6 +101,14 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -85,6 +101,14 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {
Assert.state(this.dataAccessStrategy != null, "DataAccessStrategy is required and must not be null");
Assert.state(this.mappingContext != null, "MappingContext is required and must not be null");
Assert.state(this.converter != null, "RelationalConverter is required and must not be null");
Assert.state(this.dialect != null, "Dialect is required and must not be null");
Assert.state(this.publisher != null, "ApplicationEventPublisher is required and must not be null");
Assert.state(this.operations != null, "NamedParameterJdbcOperations is required and must not be null");
Assert.state(this.queryMappingConfiguration != null, "RelationalConverter is required and must not be null");
JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(dataAccessStrategy, mappingContext,
converter, dialect, publisher, operations);
jdbcRepositoryFactory.setQueryMappingConfiguration(queryMappingConfiguration);
@ -149,6 +173,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -149,6 +173,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
super.setBeanFactory(beanFactory);
this.entityCallbacks = EntityCallbacks.create(beanFactory);
this.beanFactory = beanFactory;
}
@ -157,51 +182,53 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend @@ -157,51 +182,53 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
Assert.state(this.converter != null, "RelationalConverter is required and must not be null");
if (mappingContext == null) {
Assert.state(beanFactory != null, "If no MappingContext are set a BeanFactory must be available");
this.mappingContext = beanFactory.getBean(RelationalMappingContext.class);
if (this.mappingContext == null) {
this.mappingContext = this.converter.getMappingContext();
}
if (this.operations == null) {
Assert.state(beanFactory != null, "If no JdbcOperations are set a BeanFactory must be available");
this.operations = beanFactory.getBean(NamedParameterJdbcOperations.class);
Assert.state(this.beanFactory != null, "If no JdbcOperations are set a BeanFactory must be available");
this.operations = this.beanFactory.getBean(NamedParameterJdbcOperations.class);
}
if (this.queryMappingConfiguration == null) {
Assert.state(beanFactory != null, "If no QueryMappingConfiguration are set a BeanFactory must be available");
this.queryMappingConfiguration = beanFactory.getBeanProvider(QueryMappingConfiguration.class)
.getIfAvailable(() -> QueryMappingConfiguration.EMPTY);
if (this.beanFactory == null) {
this.queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
} else {
this.queryMappingConfiguration = beanFactory.getBeanProvider(QueryMappingConfiguration.class)
.getIfAvailable(() -> QueryMappingConfiguration.EMPTY);
}
}
if (this.dataAccessStrategy == null) {
if (this.dataAccessStrategy == null && this.beanFactory != null) {
this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class).getIfAvailable();
}
Assert.state(beanFactory != null, "If no DataAccessStrategy is set a BeanFactory must be available");
if (this.dataAccessStrategy == null) {
this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class) //
.getIfAvailable(() -> {
Assert.state(this.dialect != null, "Dialect is required and must not be null");
Assert.state(this.dialect != null, "Dialect is required and must not be null");
DataAccessStrategyFactory factory = getDataAccessStrategyFactory(this.mappingContext, this.converter,
this.dialect, this.operations, this.queryMappingConfiguration);
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext, this.converter,
this.dialect);
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(this.mappingContext, this.converter);
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(this.operations, this.dialect);
this.dataAccessStrategy = factory.create();
}
DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, this.converter,
this.operations, sqlParametersFactory, insertStrategyFactory, queryMappingConfiguration);
super.afterPropertiesSet();
}
return factory.create();
});
}
private static DataAccessStrategyFactory getDataAccessStrategyFactory(RelationalMappingContext mappingContext,
JdbcConverter converter, Dialect dialect, NamedParameterJdbcOperations operations,
QueryMappingConfiguration queryMapping) {
if (beanFactory != null) {
entityCallbacks = EntityCallbacks.create(beanFactory);
}
SqlGeneratorSource source = new SqlGeneratorSource(mappingContext, converter, dialect);
SqlParametersFactory spf = new SqlParametersFactory(mappingContext, converter);
InsertStrategyFactory isf = new InsertStrategyFactory(operations, dialect);
super.afterPropertiesSet();
return new DataAccessStrategyFactory(source, converter, operations, spf, isf, queryMapping);
}
}

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

@ -57,19 +57,19 @@ import org.springframework.test.util.ReflectionTestUtils; @@ -57,19 +57,19 @@ import org.springframework.test.util.ReflectionTestUtils;
*/
@MockitoSettings(strictness = Strictness.LENIENT)
@ExtendWith(MockitoExtension.class)
public class JdbcRepositoryFactoryBeanUnitTests {
class JdbcRepositoryFactoryBeanUnitTests {
JdbcRepositoryFactoryBean<DummyEntityRepository, DummyEntity, Long> factoryBean;
private JdbcRepositoryFactoryBean<DummyEntityRepository, DummyEntity, Long> factoryBean;
@Mock DataAccessStrategy dataAccessStrategy;
@Mock ApplicationEventPublisher publisher;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) ListableBeanFactory beanFactory;
@Mock Dialect dialect;
RelationalMappingContext mappingContext;
private RelationalMappingContext mappingContext;
@BeforeEach
public void setUp() {
void setUp() {
this.mappingContext = new JdbcMappingContext();
@ -90,7 +90,7 @@ public class JdbcRepositoryFactoryBeanUnitTests { @@ -90,7 +90,7 @@ public class JdbcRepositoryFactoryBeanUnitTests {
}
@Test // DATAJDBC-151
public void setsUpBasicInstanceCorrectly() {
void setsUpBasicInstanceCorrectly() {
factoryBean.setDataAccessStrategy(dataAccessStrategy);
factoryBean.setMappingContext(mappingContext);
@ -104,20 +104,20 @@ public class JdbcRepositoryFactoryBeanUnitTests { @@ -104,20 +104,20 @@ public class JdbcRepositoryFactoryBeanUnitTests {
}
@Test // DATAJDBC-151
public void requiresListableBeanFactory() {
void requiresListableBeanFactory() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> factoryBean.setBeanFactory(mock(BeanFactory.class)));
}
@Test // DATAJDBC-155
public void afterPropertiesThrowsExceptionWhenNoMappingContextSet() {
void afterPropertiesThrowsExceptionWhenNoMappingContextSet() {
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> factoryBean.setMappingContext(null));
}
@Test // DATAJDBC-155
public void afterPropertiesSetDefaultsNullablePropertiesCorrectly() {
void afterPropertiesSetDefaultsNullablePropertiesCorrectly() {
factoryBean.setConverter(new MappingJdbcConverter(mappingContext, dataAccessStrategy));
factoryBean.setApplicationEventPublisher(publisher);
@ -128,10 +128,8 @@ public class JdbcRepositoryFactoryBeanUnitTests { @@ -128,10 +128,8 @@ public class JdbcRepositoryFactoryBeanUnitTests {
assertThat(factoryBean.getObject()).isNotNull();
assertThat(ReflectionTestUtils.getField(factoryBean, "dataAccessStrategy"))
.isInstanceOf(DefaultDataAccessStrategy.class);
assertThat(ReflectionTestUtils.getField(factoryBean, "queryMappingConfiguration"))
.isEqualTo(QueryMappingConfiguration.EMPTY);
assertThat(ReflectionTestUtils.getField(factoryBean, "mappingContext"))
.isEqualTo(mappingContext);
assertThat(factoryBean).hasFieldOrPropertyWithValue("queryMappingConfiguration", QueryMappingConfiguration.EMPTY);
assertThat(factoryBean).hasFieldOrPropertyWithValue("mappingContext", mappingContext);
}
private static class DummyEntity {

Loading…
Cancel
Save