diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java index f7e7af35f2..effc00071f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java @@ -22,6 +22,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jdbc.core.convert.DataAccessStrategy; +import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.relational.core.query.Query; import org.springframework.lang.Nullable; @@ -34,6 +36,7 @@ * @author Chirag Tailor * @author Diego Krupitza * @author Myeonghyeon Lee + * @author Tomohiko Ozawa */ public interface JdbcAggregateOperations { @@ -312,4 +315,18 @@ default <T> void delete(T aggregateRoot, Class<T> domainType) { default <T> void deleteAll(Iterable<? extends T> aggregateRoots, Class<T> domainType) { deleteAll(aggregateRoots); } + + /** + * Returns the {@link JdbcConverter}. + * + * @return the {@link JdbcConverter}. + */ + JdbcConverter getConverter(); + + /** + * Return the {@link DataAccessStrategy} + * + * @return the {@link DataAccessStrategy} + */ + DataAccessStrategy getDataAccessStrategy(); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java index efa04a8d8b..75dea3166d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java @@ -67,6 +67,7 @@ * @author Myeonghyeon Lee * @author Chirag Tailor * @author Diego Krupitza + * @author Tomohiko Ozawa */ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -81,6 +82,57 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { private EntityCallbacks entityCallbacks = EntityCallbacks.create(); + /** + * Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationContext}, {@link RelationalMappingContext} and + * {@link DataAccessStrategy}. + * + * @param publisher must not be {@literal null}. + * @param dataAccessStrategy must not be {@literal null}. + * @since 3.3 + */ + public JdbcAggregateTemplate(ApplicationContext publisher, JdbcConverter converter, + DataAccessStrategy dataAccessStrategy) { + + Assert.notNull(publisher, "ApplicationContext must not be null"); + Assert.notNull(converter, "RelationalConverter must not be null"); + Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null"); + + this.eventDelegate.setPublisher(publisher); + this.converter = converter; + this.accessStrategy = dataAccessStrategy; + this.context = converter.getMappingContext(); + + this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context); + + this.executor = new AggregateChangeExecutor(converter, accessStrategy); + + setEntityCallbacks(EntityCallbacks.create(publisher)); + } + + /** + * Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationEventPublisher}, + * {@link RelationalMappingContext} and {@link DataAccessStrategy}. + * + * @param publisher must not be {@literal null}. + * @param dataAccessStrategy must not be {@literal null}. + * @since 3.3 + */ + public JdbcAggregateTemplate(ApplicationEventPublisher publisher, JdbcConverter converter, + DataAccessStrategy dataAccessStrategy) { + + Assert.notNull(publisher, "ApplicationEventPublisher must not be null"); + Assert.notNull(converter, "RelationalConverter must not be null"); + Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null"); + + this.eventDelegate.setPublisher(publisher); + this.converter = converter; + this.accessStrategy = dataAccessStrategy; + this.context = converter.getMappingContext(); + + this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context); + this.executor = new AggregateChangeExecutor(converter, accessStrategy); + } + /** * Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationContext}, {@link RelationalMappingContext} and * {@link DataAccessStrategy}. @@ -89,7 +141,10 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { * @param context must not be {@literal null}. * @param dataAccessStrategy must not be {@literal null}. * @since 1.1 + * @deprecated since 3.3, use {@link JdbcAggregateTemplate(ApplicationContext, JdbcConverter, DataAccessStrategy)} + * instead. */ + @Deprecated(since = "3.3") public JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingContext context, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) { @@ -117,7 +172,10 @@ public JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingCont * @param publisher must not be {@literal null}. * @param context must not be {@literal null}. * @param dataAccessStrategy must not be {@literal null}. + * @deprecated since 3.3, use {@link JdbcAggregateTemplate(ApplicationEventPublisher, JdbcConverter, + * DataAccessStrategy)} instead. */ + @Deprecated(since = "3.3") public JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMappingContext context, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) { @@ -656,9 +714,19 @@ private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, MutableA return null; } - private record EntityAndPreviousVersion<T> (T entity, @Nullable Number version) { + private record EntityAndPreviousVersion<T>(T entity, @Nullable Number version) { } - private record EntityAndChangeCreator<T> (T entity, Function<T, RootAggregateChange<T>> changeCreator) { + private record EntityAndChangeCreator<T>(T entity, Function<T, RootAggregateChange<T>> changeCreator) { + } + + @Override + public DataAccessStrategy getDataAccessStrategy() { + return accessStrategy; + } + + @Override + public JdbcConverter getConverter() { + return converter; } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/dialect/DialectResolver.java similarity index 99% rename from spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java rename to spring-data-jdbc/src/main/java/org/springframework/data/jdbc/dialect/DialectResolver.java index c55240754b..6ecc3e1ba1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/dialect/DialectResolver.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jdbc.repository.config; +package org.springframework.data.jdbc.dialect; import java.sql.Connection; import java.sql.DatabaseMetaData; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java index bd725b98d3..b3501ab0e3 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java @@ -40,6 +40,7 @@ import org.springframework.data.jdbc.core.dialect.JdbcDialect; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; +import org.springframework.data.jdbc.dialect.DialectResolver; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.RelationalManagedTypes; import org.springframework.data.relational.core.conversion.RelationalConverter; @@ -61,6 +62,7 @@ * @author Christoph Strobl * @author Myeonghyeon Lee * @author Chirag Tailor + * @author Tomohiko Ozawa * @since 1.1 */ @Configuration(proxyBeanMethods = false) @@ -191,7 +193,7 @@ private List<Object> storeConverters(Dialect dialect) { public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationContext applicationContext, JdbcMappingContext mappingContext, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) { - return new JdbcAggregateTemplate(applicationContext, mappingContext, converter, dataAccessStrategy); + return new JdbcAggregateTemplate(applicationContext, converter, dataAccessStrategy); } /** @@ -207,8 +209,7 @@ public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations op SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, jdbcConverter, dialect); DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, jdbcConverter, operations, - new SqlParametersFactory(context, jdbcConverter), - new InsertStrategyFactory(operations, dialect)); + new SqlParametersFactory(context, jdbcConverter), new InsertStrategyFactory(operations, dialect)); return factory.create(); } @@ -219,8 +220,7 @@ public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations op * @param operations the {@link NamedParameterJdbcOperations} allowing access to a {@link java.sql.Connection}. * @return the {@link Dialect} to be used. * @since 2.0 - * @throws org.springframework.data.jdbc.repository.config.DialectResolver.NoDialectException if the {@link Dialect} - * cannot be determined. + * @throws DialectResolver.NoDialectException if the {@link Dialect} cannot be determined. */ @Bean public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java index f250e84f37..25bb882ecb 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java @@ -122,7 +122,9 @@ /** * Configures the name of the {@link org.springframework.data.jdbc.core.convert.DataAccessStrategy} bean definition to * be used to create repositories discovered through this annotation. Defaults to {@code defaultDataAccessStrategy}. + * @deprecated since 3.3 use {@link #jdbcAggregateOperationsRef()} instead */ + @Deprecated(since = "3.3") String dataAccessStrategyRef() default ""; /** @@ -133,6 +135,12 @@ */ String transactionManagerRef() default "transactionManager"; + /** + * Configure the name of the {@link org.springframework.data.jdbc.core.JdbcAggregateOperations} bean definition to be + * used to create repositories discovered through this annotation. + */ + String jdbcAggregateOperationsRef() default ""; + /** * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to * {@link QueryLookupStrategy.Key#CREATE_IF_NOT_FOUND}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcRepositoryConfigExtension.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcRepositoryConfigExtension.java index 19535f2568..3d825a9602 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcRepositoryConfigExtension.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcRepositoryConfigExtension.java @@ -40,6 +40,7 @@ * @author Fei Dong * @author Mark Paluch * @author Antoine Sauray + * @author Tomohiko Ozawa */ public class JdbcRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport { @@ -79,9 +80,15 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef"); builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME)); - builder.addPropertyValue("mappingContext", new RuntimeBeanReference(JdbcMappingContext.class)); - builder.addPropertyValue("dialect", new RuntimeBeanReference(Dialect.class)); - builder.addPropertyValue("converter", new RuntimeBeanReference(JdbcConverter.class)); + Optional<String> jdbcAggregateOperationsRef = source.getAttribute("jdbcAggregateOperationsRef"); + + if (jdbcAggregateOperationsRef.isPresent()) { + builder.addPropertyReference("jdbcAggregateOperations", jdbcAggregateOperationsRef.get()); + } else { + builder.addPropertyValue("mappingContext", new RuntimeBeanReference(JdbcMappingContext.class)); + builder.addPropertyValue("dialect", new RuntimeBeanReference(Dialect.class)); + builder.addPropertyValue("converter", new RuntimeBeanReference(JdbcConverter.class)); + } } /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java index f6becf1ff6..42b6c98a94 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java @@ -19,9 +19,11 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.jdbc.core.JdbcAggregateOperations; import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.dialect.DialectResolver; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.relational.core.dialect.Dialect; @@ -48,6 +50,7 @@ * @author Hebert Coelho * @author Diego Krupitza * @author Christopher Klein + * @author Tomohiko Ozawa */ public class JdbcRepositoryFactory extends RepositoryFactorySupport { @@ -62,6 +65,20 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport { private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; private EntityCallbacks entityCallbacks; + public JdbcRepositoryFactory(ApplicationEventPublisher publisher, JdbcAggregateOperations jdbcAggregateOperations, + NamedParameterJdbcOperations operations) { + Assert.notNull(publisher, "ApplicationEventPublisher must not be null"); + Assert.notNull(jdbcAggregateOperations, "JdbcAggregateOperations must not be null"); + Assert.notNull(operations, "NamedParameterJdbcOperations must not be null"); + + this.converter = jdbcAggregateOperations.getConverter(); + this.accessStrategy = jdbcAggregateOperations.getDataAccessStrategy(); + this.context = jdbcAggregateOperations.getConverter().getMappingContext(); + this.dialect = DialectResolver.getDialect(operations.getJdbcOperations()); + this.operations = operations; + this.publisher = publisher; + } + /** * Creates a new {@link JdbcRepositoryFactory} for the given {@link DataAccessStrategy}, * {@link RelationalMappingContext} and {@link ApplicationEventPublisher}. @@ -114,7 +131,7 @@ public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) { @Override protected Object getTargetRepository(RepositoryInformation repositoryInformation) { - JdbcAggregateTemplate template = new JdbcAggregateTemplate(publisher, context, converter, accessStrategy); + JdbcAggregateTemplate template = new JdbcAggregateTemplate(publisher, converter, accessStrategy); if (entityCallbacks != null) { template.setEntityCallbacks(entityCallbacks); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java index a76db20a13..b975ec413d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java @@ -21,6 +21,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.data.jdbc.core.JdbcAggregateOperations; +import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategyFactory; import org.springframework.data.jdbc.core.convert.InsertStrategyFactory; @@ -48,6 +50,7 @@ * @author Mark Paluch * @author Hebert Coelho * @author Chirag Tailor + * @author Tomohiko Ozawa */ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> implements ApplicationEventPublisherAware { @@ -59,6 +62,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend private DataAccessStrategy dataAccessStrategy; private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; private NamedParameterJdbcOperations operations; + private JdbcAggregateOperations aggregateOperations; private EntityCallbacks entityCallbacks; private Dialect dialect; @@ -85,8 +89,7 @@ public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { @Override protected RepositoryFactorySupport doCreateRepositoryFactory() { - JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(dataAccessStrategy, mappingContext, - converter, dialect, publisher, operations); + JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(publisher, aggregateOperations, operations); jdbcRepositoryFactory.setQueryMappingConfiguration(queryMappingConfiguration); jdbcRepositoryFactory.setEntityCallbacks(entityCallbacks); jdbcRepositoryFactory.setBeanFactory(beanFactory); @@ -138,6 +141,10 @@ public void setJdbcOperations(NamedParameterJdbcOperations operations) { this.operations = operations; } + public void setJdbcAggregateOperations(JdbcAggregateOperations jdbcAggregateOperations) { + this.aggregateOperations = jdbcAggregateOperations; + } + public void setConverter(JdbcConverter converter) { Assert.notNull(converter, "JdbcConverter must not be null"); @@ -156,9 +163,6 @@ public void setBeanFactory(BeanFactory beanFactory) { @Override public void afterPropertiesSet() { - 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"); - if (this.operations == null) { Assert.state(beanFactory != null, "If no JdbcOperations are set a BeanFactory must be available"); @@ -166,25 +170,33 @@ public void afterPropertiesSet() { this.operations = beanFactory.getBean(NamedParameterJdbcOperations.class); } - if (this.dataAccessStrategy == null) { + if (aggregateOperations == 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"); + + if (this.dataAccessStrategy == null) { + + Assert.state(beanFactory != null, "If no DataAccessStrategy is set a BeanFactory must be available"); - Assert.state(beanFactory != null, "If no DataAccessStrategy is set a BeanFactory must be available"); + this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class) // + .getIfAvailable(() -> { - 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"); + 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); - 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); + DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, this.converter, + this.operations, sqlParametersFactory, insertStrategyFactory); - DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, this.converter, - this.operations, sqlParametersFactory, insertStrategyFactory); + return factory.create(); + }); + } - return factory.create(); - }); + aggregateOperations = new JdbcAggregateTemplate(publisher, converter, dataAccessStrategy); } if (this.queryMappingConfiguration == null) { diff --git a/spring-data-jdbc/src/main/resources/META-INF/spring.factories b/spring-data-jdbc/src/main/resources/META-INF/spring.factories index cc0d5cce5e..290e0c60d0 100644 --- a/spring-data-jdbc/src/main/resources/META-INF/spring.factories +++ b/spring-data-jdbc/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory -org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider=org.springframework.data.jdbc.repository.config.DialectResolver.DefaultDialectProvider +org.springframework.data.jdbc.dialect.DialectResolver$JdbcDialectProvider=org.springframework.data.jdbc.dialect.DialectResolver.DefaultDialectProvider diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java index 7ccf063687..0319185e8a 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java @@ -22,22 +22,13 @@ import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import java.time.LocalDateTime; +import java.util.*; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; import java.util.function.Function; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -51,8 +42,6 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Persistable; import org.springframework.data.domain.Sort; -import org.springframework.data.jdbc.core.convert.DataAccessStrategy; -import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.testing.EnabledOnFeature; import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.TestClass; @@ -917,9 +906,8 @@ void readOnlyGetsLoadedButNotWritten() { template.save(entity); - assertThat( - jdbcTemplate.queryForObject("SELECT read_only FROM with_read_only", Collections.emptyMap(), String.class)) - .isEqualTo("from-db"); + assertThat(jdbcTemplate.queryForObject("SELECT read_only FROM with_read_only", Collections.emptyMap(), + String.class)).isEqualTo("from-db"); } @Test @@ -1258,7 +1246,8 @@ void recordOfSet() { @Test // GH-1656 void mapWithEnumKey() { - EnumMapOwner enumMapOwner = template.save(new EnumMapOwner(null, "OwnerName", Map.of(Color.BLUE, new MapElement("Element")))); + EnumMapOwner enumMapOwner = template.save( + new EnumMapOwner(null, "OwnerName", Map.of(Color.BLUE, new MapElement("Element")))); Iterable<EnumMapOwner> enumMapOwners = template.findAll(EnumMapOwner.class); @@ -2086,7 +2075,6 @@ record Book(String name) { record EnumMapOwner(@Id Long id, String name, Map<Color, MapElement> map) { } - @Configuration @Import(TestConfiguration.class) static class Config { @@ -2095,12 +2083,6 @@ static class Config { TestClass testClass() { return TestClass.of(JdbcAggregateTemplateIntegrationTests.class); } - - @Bean - JdbcAggregateOperations operations(ApplicationEventPublisher publisher, RelationalMappingContext context, - DataAccessStrategy dataAccessStrategy, JdbcConverter converter) { - return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy); - } } @ContextConfiguration(classes = Config.class) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java index 3f8f6ce935..5560ea19f8 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java @@ -23,18 +23,14 @@ import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.core.convert.DataAccessStrategy; -import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.testing.DatabaseType; import org.springframework.data.jdbc.testing.EnabledOnDatabase; import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.TestConfiguration; -import org.springframework.data.relational.core.mapping.RelationalMappingContext; /** * Integration tests for {@link JdbcAggregateTemplate} and it's handling of immutable entities. @@ -602,11 +598,5 @@ static class Config { Class<?> testClass() { return ImmutableAggregateTemplateHsqlIntegrationTests.class; } - - @Bean - JdbcAggregateOperations operations(ApplicationEventPublisher publisher, RelationalMappingContext context, - DataAccessStrategy dataAccessStrategy, JdbcConverter converter) { - return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy); - } } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java index 8bd2d96a99..b62eb70b6a 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java @@ -16,22 +16,16 @@ package org.springframework.data.jdbc.core; import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.core.convert.DataAccessStrategy; -import org.springframework.data.jdbc.core.convert.JdbcConverter; -import org.springframework.data.jdbc.testing.EnabledOnFeature; import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.TestConfiguration; import org.springframework.data.relational.core.mapping.NamingStrategy; -import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; /** @@ -86,12 +80,6 @@ Class<?> testClass() { return JdbcAggregateTemplateSchemaIntegrationTests.class; } - @Bean - JdbcAggregateOperations operations(ApplicationEventPublisher publisher, RelationalMappingContext context, - DataAccessStrategy dataAccessStrategy, JdbcConverter converter) { - return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy); - } - @Bean NamingStrategy namingStrategy() { return new NamingStrategy() { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java index d9e6ed3368..e155cb00b7 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java @@ -74,7 +74,7 @@ public void setUp() { RelationalMappingContext mappingContext = new RelationalMappingContext(); JdbcConverter converter = new MappingJdbcConverter(mappingContext, relationResolver); - template = new JdbcAggregateTemplate(eventPublisher, mappingContext, converter, dataAccessStrategy); + template = new JdbcAggregateTemplate(eventPublisher, converter, dataAccessStrategy); template.setEntityCallbacks(callbacks); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java index 1e3b30f4ca..81d4f5d059 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java @@ -27,12 +27,14 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; +import org.springframework.data.jdbc.core.JdbcAggregateOperations; import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategyFactory; @@ -40,6 +42,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; import org.springframework.data.jdbc.core.convert.SqlParametersFactory; +import org.springframework.data.jdbc.dialect.DialectResolver; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean; import org.springframework.data.jdbc.testing.IntegrationTest; @@ -71,6 +74,8 @@ public class EnableJdbcRepositoriesIntegrationTests { static final Field OPERATIONS = ReflectionUtils.findField(JdbcRepositoryFactoryBean.class, "operations"); static final Field DATA_ACCESS_STRATEGY = ReflectionUtils.findField(JdbcRepositoryFactoryBean.class, "dataAccessStrategy"); + static final Field AGGREGATE_OPERATIONS = ReflectionUtils.findField(JdbcRepositoryFactoryBean.class, + "aggregateOperations"); public static final RowMapper DUMMY_ENTITY_ROW_MAPPER = mock(RowMapper.class); public static final RowMapper STRING_ROW_MAPPER = mock(RowMapper.class); @@ -81,9 +86,15 @@ public class EnableJdbcRepositoriesIntegrationTests { @Autowired @Qualifier("defaultDataAccessStrategy") DataAccessStrategy defaultDataAccessStrategy; @Autowired + @Qualifier("jdbcAggregateOperations") + JdbcAggregateOperations defaultJdbcAggregateOperations; + @Autowired @Qualifier("qualifierJdbcOperations") NamedParameterJdbcOperations qualifierJdbcOperations; @Autowired @Qualifier("qualifierDataAccessStrategy") DataAccessStrategy qualifierDataAccessStrategy; + @Autowired + @Qualifier("qualifierJdbcAggregateOperations") + JdbcAggregateOperations qualifierJdbcAggregateOperations; @BeforeAll public static void setup() { @@ -91,6 +102,7 @@ public static void setup() { MAPPER_MAP.setAccessible(true); OPERATIONS.setAccessible(true); DATA_ACCESS_STRATEGY.setAccessible(true); + AGGREGATE_OPERATIONS.setAccessible(true); } @Test // DATAJDBC-100 @@ -125,6 +137,19 @@ public void jdbcOperationsRef() { assertThat(dataAccessStrategy).isNotSameAs(defaultDataAccessStrategy).isSameAs(qualifierDataAccessStrategy); } + @Test // GH-1704 + public void jdbcAggregateOperationsRef() { + + JdbcAggregateOperations aggregateOperations = (JdbcAggregateOperations) ReflectionUtils.getField( + AGGREGATE_OPERATIONS, factoryBean); + assertThat(aggregateOperations).isNotSameAs(defaultJdbcAggregateOperations) + .isSameAs(qualifierJdbcAggregateOperations); + + DataAccessStrategy dataAccessStrategy = (DataAccessStrategy) ReflectionUtils.getField(DATA_ACCESS_STRATEGY, + factoryBean); + assertThat(dataAccessStrategy).isNotSameAs(defaultDataAccessStrategy).isSameAs(qualifierDataAccessStrategy); + } + interface DummyRepository extends CrudRepository<DummyEntity, Long> { } @@ -145,6 +170,7 @@ public void setId(Long id) { @EnableJdbcRepositories(considerNestedRepositories = true, includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = DummyRepository.class), jdbcOperationsRef = "qualifierJdbcOperations", dataAccessStrategyRef = "qualifierDataAccessStrategy", + jdbcAggregateOperationsRef = "qualifierJdbcAggregateOperations", repositoryBaseClass = DummyRepositoryBaseClass.class) @Import(TestConfiguration.class) static class Config { @@ -167,14 +193,19 @@ DataAccessStrategy defaultDataAccessStrategy( @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context, JdbcConverter converter, Dialect dialect) { return new DataAccessStrategyFactory(new SqlGeneratorSource(context, converter, dialect), converter, template, - new SqlParametersFactory(context, converter), - new InsertStrategyFactory(template, dialect)).create(); + new SqlParametersFactory(context, converter), new InsertStrategyFactory(template, dialect)).create(); } @Bean Dialect jdbcDialect(@Qualifier("qualifierJdbcOperations") NamedParameterJdbcOperations operations) { return DialectResolver.getDialect(operations.getJdbcOperations()); } + + @Bean("qualifierJdbcAggregateOperations") + JdbcAggregateOperations jdbcAggregateOperations(ApplicationContext publisher, JdbcConverter converter, + @Qualifier("qualifierDataAccessStrategy") DataAccessStrategy dataAccessStrategy) { + return new JdbcAggregateTemplate(publisher, converter, dataAccessStrategy); + } } private static class DummyRepositoryBaseClass<T, ID> implements CrudRepository<T, ID> { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java index 99fbade759..839d4ad313 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java @@ -40,8 +40,11 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.H2Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; +import org.springframework.jdbc.core.ConnectionCallback; +import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.test.util.ReflectionTestUtils; @@ -66,6 +69,11 @@ public class JdbcRepositoryFactoryBeanUnitTests { @Mock(answer = Answers.RETURNS_DEEP_STUBS) ListableBeanFactory beanFactory; @Mock Dialect dialect; + @Mock + JdbcOperations operations; + @Mock + NamedParameterJdbcOperations namedParameterJdbcOperations; + RelationalMappingContext mappingContext; @BeforeEach @@ -76,7 +84,9 @@ public void setUp() { // Setup standard configuration factoryBean = new JdbcRepositoryFactoryBean<>(DummyEntityRepository.class); - when(beanFactory.getBean(NamedParameterJdbcOperations.class)).thenReturn(mock(NamedParameterJdbcOperations.class)); + when(operations.execute(any(ConnectionCallback.class))).thenReturn(H2Dialect.INSTANCE); + when(namedParameterJdbcOperations.getJdbcOperations()).thenReturn(operations); + when(beanFactory.getBean(NamedParameterJdbcOperations.class)).thenReturn(namedParameterJdbcOperations); ObjectProvider<DataAccessStrategy> provider = mock(ObjectProvider.class); when(beanFactory.getBeanProvider(DataAccessStrategy.class)).thenReturn(provider); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java index b84d93fe6b..c7721b00b9 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -34,11 +35,13 @@ import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; import org.springframework.data.convert.CustomConversions; +import org.springframework.data.jdbc.core.JdbcAggregateOperations; +import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.*; import org.springframework.data.jdbc.core.dialect.JdbcDialect; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; -import org.springframework.data.jdbc.repository.config.DialectResolver; +import org.springframework.data.jdbc.dialect.DialectResolver; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.core.dialect.Dialect; @@ -169,6 +172,12 @@ Dialect jdbcDialect(NamedParameterJdbcOperations operations) { return DialectResolver.getDialect(operations.getJdbcOperations()); } + @Bean + JdbcAggregateOperations jdbcAggregateOperations(ApplicationContext publisher, JdbcConverter converter, + @Qualifier("defaultDataAccessStrategy") DataAccessStrategy dataAccessStrategy) { + return new JdbcAggregateTemplate(publisher, converter, dataAccessStrategy); + } + @Lazy @Bean TestDatabaseFeatures features(NamedParameterJdbcOperations operations) {