diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategyFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategyFactory.java index e761884c465..d3b6e1b9284 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategyFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategyFactory.java @@ -25,6 +25,7 @@ * {@link DataAccessStrategy} for consistent access strategy creation. * * @author Mark Paluch + * @author Mikhail Polivakha * @since 3.2 */ public class DataAccessStrategyFactory { @@ -34,6 +35,7 @@ public class DataAccessStrategyFactory { private final NamedParameterJdbcOperations operations; private final SqlParametersFactory sqlParametersFactory; private final InsertStrategyFactory insertStrategyFactory; + private final QueryMappingConfiguration queryMappingConfiguration; /** * Creates a new {@link DataAccessStrategyFactory}. @@ -46,7 +48,7 @@ public class DataAccessStrategyFactory { */ public DataAccessStrategyFactory(SqlGeneratorSource sqlGeneratorSource, JdbcConverter converter, NamedParameterJdbcOperations operations, SqlParametersFactory sqlParametersFactory, - InsertStrategyFactory insertStrategyFactory) { + InsertStrategyFactory insertStrategyFactory, QueryMappingConfiguration queryMappingConfiguration) { Assert.notNull(sqlGeneratorSource, "SqlGeneratorSource must not be null"); Assert.notNull(converter, "JdbcConverter must not be null"); @@ -59,6 +61,7 @@ public DataAccessStrategyFactory(SqlGeneratorSource sqlGeneratorSource, JdbcConv this.operations = operations; this.sqlParametersFactory = sqlParametersFactory; this.insertStrategyFactory = insertStrategyFactory; + this.queryMappingConfiguration = queryMappingConfiguration; } /** @@ -70,7 +73,7 @@ public DataAccessStrategy create() { DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy(sqlGeneratorSource, this.converter.getMappingContext(), this.converter, this.operations, sqlParametersFactory, - insertStrategyFactory); + insertStrategyFactory, queryMappingConfiguration); if (this.converter.getMappingContext().isSingleQueryLoadingEnabled()) { return new SingleQueryFallbackDataAccessStrategy(sqlGeneratorSource, converter, operations, diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java index 4d210d516da..ba5c5f28900 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java @@ -60,6 +60,7 @@ * @author Radim Tlusty * @author Chirag Tailor * @author Diego Krupitza + * @author Mikhail Polivakha * @since 1.1 */ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -71,6 +72,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { private final SqlParametersFactory sqlParametersFactory; private final InsertStrategyFactory insertStrategyFactory; + private final QueryMappingConfiguration queryMappingConfiguration; + /** * Creates a {@link DefaultDataAccessStrategy} * @@ -82,7 +85,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { */ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations, SqlParametersFactory sqlParametersFactory, - InsertStrategyFactory insertStrategyFactory) { + InsertStrategyFactory insertStrategyFactory, QueryMappingConfiguration queryMappingConfiguration) { Assert.notNull(sqlGeneratorSource, "SqlGeneratorSource must not be null"); Assert.notNull(context, "RelationalMappingContext must not be null"); @@ -90,6 +93,7 @@ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation Assert.notNull(operations, "NamedParameterJdbcOperations must not be null"); Assert.notNull(sqlParametersFactory, "SqlParametersFactory must not be null"); Assert.notNull(insertStrategyFactory, "InsertStrategyFactory must not be null"); + Assert.notNull(queryMappingConfiguration, "InsertStrategyFactory must not be null"); this.sqlGeneratorSource = sqlGeneratorSource; this.context = context; @@ -97,6 +101,7 @@ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation this.operations = operations; this.sqlParametersFactory = sqlParametersFactory; this.insertStrategyFactory = insertStrategyFactory; + this.queryMappingConfiguration = queryMappingConfiguration; } @Override @@ -265,7 +270,7 @@ public T findById(Object id, Class domainType) { SqlIdentifierParameterSource parameter = sqlParametersFactory.forQueryById(id, domainType, ID_SQL_PARAMETER); try { - return operations.queryForObject(findOneSql, parameter, getEntityRowMapper(domainType)); + return operations.queryForObject(findOneSql, parameter, getRowMapper(domainType)); } catch (EmptyResultDataAccessException e) { return null; } @@ -273,7 +278,7 @@ public T findById(Object id, Class domainType) { @Override public List findAll(Class domainType) { - return operations.query(sql(domainType).getFindAll(), getEntityRowMapper(domainType)); + return operations.query(sql(domainType).getFindAll(), getRowMapper(domainType)); } @Override @@ -285,7 +290,7 @@ public List findAllById(Iterable ids, Class domainType) { SqlParameterSource parameterSource = sqlParametersFactory.forQueryByIds(ids, domainType); String findAllInListSql = sql(domainType).getFindAllInList(); - return operations.query(findAllInListSql, parameterSource, getEntityRowMapper(domainType)); + return operations.query(findAllInListSql, parameterSource, getRowMapper(domainType)); } @Override @@ -339,12 +344,12 @@ public boolean existsById(Object id, Class domainType) { @Override public List findAll(Class domainType, Sort sort) { - return operations.query(sql(domainType).getFindAll(sort), getEntityRowMapper(domainType)); + return operations.query(sql(domainType).getFindAll(sort), getRowMapper(domainType)); } @Override public List findAll(Class domainType, Pageable pageable) { - return operations.query(sql(domainType).getFindAll(pageable), getEntityRowMapper(domainType)); + return operations.query(sql(domainType).getFindAll(pageable), getRowMapper(domainType)); } @Override @@ -354,7 +359,7 @@ public Optional findOne(Query query, Class domainType) { String sqlQuery = sql(domainType).selectByQuery(query, parameterSource); try { - return Optional.ofNullable(operations.queryForObject(sqlQuery, parameterSource, getEntityRowMapper(domainType))); + return Optional.ofNullable(operations.queryForObject(sqlQuery, parameterSource, getRowMapper(domainType))); } catch (EmptyResultDataAccessException e) { return Optional.empty(); } @@ -366,7 +371,7 @@ public List findAll(Query query, Class domainType) { MapSqlParameterSource parameterSource = new MapSqlParameterSource(); String sqlQuery = sql(domainType).selectByQuery(query, parameterSource); - return operations.query(sqlQuery, parameterSource, getEntityRowMapper(domainType)); + return operations.query(sqlQuery, parameterSource, getRowMapper(domainType)); } @Override @@ -375,7 +380,7 @@ public List findAll(Query query, Class domainType, Pageable pageable) MapSqlParameterSource parameterSource = new MapSqlParameterSource(); String sqlQuery = sql(domainType).selectByQuery(query, parameterSource, pageable); - return operations.query(sqlQuery, parameterSource, getEntityRowMapper(domainType)); + return operations.query(sqlQuery, parameterSource, getRowMapper(domainType)); } @Override @@ -404,7 +409,13 @@ public long count(Query query, Class domainType) { return result; } - private EntityRowMapper getEntityRowMapper(Class domainType) { + private RowMapper getRowMapper(Class domainType) { + RowMapper targetRowMapper; + + if ((targetRowMapper = queryMappingConfiguration.getRowMapper(domainType)) != null) { + return targetRowMapper; + } + return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), converter); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/QueryMappingConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java similarity index 93% rename from spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/QueryMappingConfiguration.java rename to spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java index 8610284ece0..c750198cd8e 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/QueryMappingConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java @@ -1,4 +1,4 @@ -package org.springframework.data.jdbc.repository; +package org.springframework.data.jdbc.core.convert; import org.springframework.jdbc.core.RowMapper; import org.springframework.lang.Nullable; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java index 3b8b8efd349..07dbce4c740 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java @@ -29,6 +29,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.convert.*; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.relational.core.conversion.IdValueSource; @@ -72,9 +73,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { * uses a {@link DefaultDataAccessStrategy} */ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context, - JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession, Dialect dialect) { + JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession, + Dialect dialect, QueryMappingConfiguration queryMappingConfiguration) { return createCombinedAccessStrategy(context, converter, operations, sqlSession, NamespaceStrategy.DEFAULT_INSTANCE, - dialect); + dialect, queryMappingConfiguration); } /** @@ -83,7 +85,7 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC */ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession, - NamespaceStrategy namespaceStrategy, Dialect dialect) { + NamespaceStrategy namespaceStrategy, Dialect dialect, QueryMappingConfiguration queryMappingConfiguration) { SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, converter, dialect); SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter); @@ -94,7 +96,8 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC converter, // operations, // sqlParametersFactory, // - insertStrategyFactory // + insertStrategyFactory, // + queryMappingConfiguration // ).create(); // the DefaultDataAccessStrategy needs a reference to the returned DataAccessStrategy. This creates a dependency 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 bd725b98d33..aa41d38f12c 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.core.convert.QueryMappingConfiguration; 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 Mikhail Polivakha * @since 1.1 */ @Configuration(proxyBeanMethods = false) @@ -70,6 +72,8 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware { private ApplicationContext applicationContext; + private QueryMappingConfiguration queryMappingConfiguration; + /** * Returns the base packages to scan for JDBC mapped entities at startup. Returns the package name of the * configuration class' (the concrete class, not this one here) by default. So if you have a @@ -208,7 +212,9 @@ 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 InsertStrategyFactory(operations, dialect), + this.queryMappingConfiguration + ); return factory.create(); } @@ -232,6 +238,10 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.applicationContext = applicationContext; } + public void setQueryMappingConfiguration(Optional queryMappingConfiguration) throws BeansException { + this.queryMappingConfiguration = queryMappingConfiguration.orElse(QueryMappingConfiguration.EMPTY); + } + /** * Scans the mapping base package for classes annotated with {@link Table}. By default, it scans for entities in all * packages returned by {@link #getMappingBasePackages()}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java index 15ab15a2ed9..65c461d5055 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java @@ -3,7 +3,7 @@ import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.data.jdbc.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; import org.springframework.lang.Nullable; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java index d5f69cd82bc..2debdc24bf1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jdbc.repository.config; +import java.util.Optional; + import org.apache.ibatis.session.SqlSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -23,6 +25,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -30,6 +33,7 @@ * Configuration class tweaking Spring Data JDBC to use a {@link MyBatisDataAccessStrategy} instead of the default one. * * @author Oliver Drotbohm + * @author Mikhail Polivakha * @since 1.1 */ @Configuration(proxyBeanMethods = false) @@ -37,11 +41,13 @@ public class MyBatisJdbcConfiguration extends AbstractJdbcConfiguration { private @Autowired SqlSession session; + private @Autowired Optional queryMappingConfiguration; + @Bean @Override public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter, JdbcMappingContext context, Dialect dialect) { - return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect); + return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect, queryMappingConfiguration.orElse(QueryMappingConfiguration.EMPTY)); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java index d265b1becda..048682d0e49 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java @@ -25,7 +25,7 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.EntityRowMapper; import org.springframework.data.jdbc.core.convert.JdbcConverter; -import org.springframework.data.jdbc.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.query.AbstractJdbcQuery; import org.springframework.data.jdbc.repository.query.JdbcQueryMethod; import org.springframework.data.jdbc.repository.query.PartTreeJdbcQuery; 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 f6becf1ff6b..e7aa59d182c 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 @@ -22,7 +22,7 @@ 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.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; 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 a76db20a139..98ed44a8033 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 @@ -27,7 +27,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.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; @@ -48,6 +48,7 @@ * @author Mark Paluch * @author Hebert Coelho * @author Chirag Tailor + * @author Mikhail Polivakha */ public class JdbcRepositoryFactoryBean, S, ID extends Serializable> extends TransactionalRepositoryFactoryBeanSupport implements ApplicationEventPublisherAware { @@ -166,6 +167,10 @@ public void afterPropertiesSet() { this.operations = beanFactory.getBean(NamedParameterJdbcOperations.class); } + if (this.queryMappingConfiguration == null) { + this.queryMappingConfiguration = QueryMappingConfiguration.EMPTY; + } + if (this.dataAccessStrategy == null) { Assert.state(beanFactory != null, "If no DataAccessStrategy is set a BeanFactory must be available"); @@ -181,16 +186,12 @@ public void afterPropertiesSet() { InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(this.operations, this.dialect); DataAccessStrategyFactory factory = new DataAccessStrategyFactory(sqlGeneratorSource, this.converter, - this.operations, sqlParametersFactory, insertStrategyFactory); + this.operations, sqlParametersFactory, insertStrategyFactory, queryMappingConfiguration); return factory.create(); }); } - if (this.queryMappingConfiguration == null) { - this.queryMappingConfiguration = QueryMappingConfiguration.EMPTY; - } - if (beanFactory != null) { entityCallbacks = EntityCallbacks.create(beanFactory); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java index c5a75023a77..2a2b985a655 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java @@ -62,7 +62,7 @@ void acrossModules() { "org.springframework.data.jdbc", // Spring Data Relational "org.springframework.data.relational", // Spring Data Relational "org.springframework.data" // Spring Data Commons - ).that(onlySpringData()) // + ) .that(ignore(AuditingHandlerBeanDefinitionParser.class)) // .that(ignorePackage("org.springframework.data.aot.hint")) // ignoring aot, since it causes cycles in commons .that(ignorePackage("org.springframework.data.aot")); // ignoring aot, since it causes cycles in commons diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java index 127bee0484c..b066f4689b8 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java @@ -40,6 +40,7 @@ * @author Myat Min * @author Radim Tlusty * @author Chirag Tailor + * @author Mikhail Polivakha */ class DefaultDataAccessStrategyUnitTests { @@ -66,7 +67,8 @@ void before() { converter, // namedJdbcOperations, // sqlParametersFactory, // - insertStrategyFactory).create(); + insertStrategyFactory, + QueryMappingConfiguration.EMPTY).create(); relationResolver.setDelegate(accessStrategy); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java index d2cc4697d9d..2a131fdf627 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.testing.DatabaseType; import org.springframework.data.jdbc.testing.EnabledOnDatabase; @@ -49,6 +50,7 @@ * @author Jens Schauder * @author Greg Turnquist * @author Mark Paluch + * @author Mikhail Polivakha */ @IntegrationTest @EnabledOnDatabase(DatabaseType.HSQL) @@ -119,7 +121,7 @@ DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConv SqlSession sqlSession, EmbeddedDatabase db) { return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, converter, - new NamedParameterJdbcTemplate(db), sqlSession, HsqlDbDialect.INSTANCE); + new NamedParameterJdbcTemplate(db), sqlSession, HsqlDbDialect.INSTANCE, QueryMappingConfiguration.EMPTY); } } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/PartTreeQueryMappingConfigurationIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/PartTreeQueryMappingConfigurationIntegrationTests.java new file mode 100644 index 00000000000..164ea1f7d79 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/PartTreeQueryMappingConfigurationIntegrationTests.java @@ -0,0 +1,122 @@ +package org.springframework.data.jdbc.repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +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.convert.QueryMappingConfiguration; +import org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.data.jdbc.repository.query.PartTreeJdbcQuery; +import org.springframework.data.jdbc.testing.IntegrationTest; +import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.repository.CrudRepository; +import org.springframework.jdbc.core.RowMapper; + +/** + * Tests for mapping the results of {@link PartTreeJdbcQuery} execution via custom {@link QueryMappingConfiguration} + * + * @author Mikhail Polivakha + */ +@IntegrationTest +public class PartTreeQueryMappingConfigurationIntegrationTests { + + @Configuration + @Import(TestConfiguration.class) + @EnableJdbcRepositories( + considerNestedRepositories = true, + includeFilters = @ComponentScan.Filter(value = CarRepository.class, type = FilterType.ASSIGNABLE_TYPE)) + static class Config { + + @Bean + QueryMappingConfiguration mappers(@Qualifier("CustomRowMapperBean") CustomRowMapperBean rowMapperBean) { + return new DefaultQueryMappingConfiguration().registerRowMapper(Car.class, rowMapperBean); + } + + @Bean(value = "CustomRowMapperBean") + public CustomRowMapperBean rowMapperBean() { + return new CustomRowMapperBean(); + } + } + + @Autowired + private CarRepository carRepository; + + @Test // DATAJDBC-1006 + void testCustomQueryMappingConfiguration_predefinedPartTreeQuery() { + + // given + Car saved = carRepository.save(new Car(null, "test-model")); + + // when + Optional found = carRepository.findById(saved.getId()); + + // then + Assertions.assertThat(found).isPresent().hasValueSatisfying(car -> Assertions.assertThat(car.getModel()).isEqualTo("STUB")); + } + + @Test // DATAJDBC-1006 + void testCustomQueryMappingConfiguration_customPartTreeQuery() { + + // given + Car saved = carRepository.save(new Car(null, "test-model")); + + // when + Optional found = carRepository.findOneByModel("test-model"); + + // then + Assertions.assertThat(found).isPresent().hasValueSatisfying(car -> Assertions.assertThat(car.getModel()).isEqualTo("STUB")); + } + + public static class CustomRowMapperBean implements RowMapper { + + @Override + public Car mapRow(ResultSet rs, int rowNum) throws SQLException { + return new Car(rs.getLong("id"), "STUB"); + } + } + + interface CarRepository extends CrudRepository { + + Optional findOneByModel(String model); + } + + public static class Car { + + @Id + private Long id; + private String model; + + public Car(Long id, String model) { + this.id = id; + this.model = model; + } + + public Long getId() { + return this.id; + } + + public String getModel() { + return this.model; + } + + public void setId(Long id) { + this.id = id; + } + + public void setModel(String model) { + this.model = model; + } + } + +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java index e50a67bb99f..8345de58014 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java @@ -33,6 +33,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.core.convert.*; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; @@ -70,6 +71,7 @@ * @author Milan Milanov * @author Myeonghyeon Lee * @author Chirag Tailor + * @author Mikhail Polivakha */ class SimpleJdbcRepositoryEventsUnitTests { @@ -95,7 +97,7 @@ void before() { InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations, dialect); this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations, - sqlParametersFactory, insertStrategyFactory)); + sqlParametersFactory, insertStrategyFactory, QueryMappingConfiguration.EMPTY)); delegatingDataAccessStrategy.setDelegate(dataAccessStrategy); doReturn(true).when(dataAccessStrategy).update(any(), any()); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests.java index 4031edf4cef..b09fee6caa5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Import; import org.springframework.dao.DataAccessException; import org.springframework.data.annotation.Id; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.repository.query.Query; @@ -60,7 +61,7 @@ public class StringBasedJdbcQueryMappingConfigurationIntegrationTests { static class Config { @Bean - QueryMappingConfiguration mappers() { + QueryMappingConfiguration mappers() { return new DefaultQueryMappingConfiguration(); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/ConfigurableRowMapperMapUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/ConfigurableRowMapperMapUnitTests.java index 21edf7d9d21..a51d02b0aa6 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/ConfigurableRowMapperMapUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/ConfigurableRowMapperMapUnitTests.java @@ -19,7 +19,7 @@ import static org.mockito.Mockito.*; import org.junit.jupiter.api.Test; -import org.springframework.data.jdbc.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.jdbc.core.RowMapper; /** 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 1e3b30f4cac..fe897a542ad 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 @@ -40,7 +40,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.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean; import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.TestConfiguration; @@ -62,6 +62,7 @@ * @author Fei Dong * @author Chirag Tailor * @author Diego Krupitza + * @author Mikhail Polivakha */ @IntegrationTest public class EnableJdbcRepositoriesIntegrationTests { @@ -168,7 +169,7 @@ DataAccessStrategy defaultDataAccessStrategy( 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 InsertStrategyFactory(template, dialect), QueryMappingConfiguration.EMPTY).create(); } @Bean diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java index 2d2e12c73f3..d3f90b0ee49 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java @@ -30,7 +30,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.JdbcConverter; -import org.springframework.data.jdbc.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.mapping.callback.EntityCallbacks; 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 99fbade759b..6fd2a1d3a38 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 @@ -38,7 +38,7 @@ import org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy; import org.springframework.data.jdbc.core.convert.MappingJdbcConverter; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; -import org.springframework.data.jdbc.repository.QueryMappingConfiguration; +import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; 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 b84d93fe6b0..06874d7f8d9 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 @@ -38,6 +38,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.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.DialectResolver; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -64,6 +65,7 @@ * @author Christoph Strobl * @author Chirag Tailor * @author Christopher Klein + * @author Mikhail Polivakha */ @Configuration @ComponentScan // To pick up configuration classes (per activated profile) @@ -106,11 +108,11 @@ PlatformTransactionManager transactionManager() { @Bean DataAccessStrategy defaultDataAccessStrategy( @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context, - JdbcConverter converter, Dialect dialect) { + JdbcConverter converter, Dialect dialect, Optional queryMappingConfiguration) { return new DataAccessStrategyFactory(new SqlGeneratorSource(context, converter, dialect), converter, template, new SqlParametersFactory(context, converter), - new InsertStrategyFactory(template, dialect)).create(); + new InsertStrategyFactory(template, dialect), queryMappingConfiguration.orElse(QueryMappingConfiguration.EMPTY)).create(); } @Bean("jdbcMappingContext") diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-db2.sql new file mode 100644 index 00000000000..5bc12b55eb7 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-db2.sql @@ -0,0 +1,3 @@ +DROP TABLE car; + +CREATE TABLE car ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, model VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-h2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-h2.sql new file mode 100644 index 00000000000..7306fe6b3b1 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-h2.sql @@ -0,0 +1,4 @@ +DROP TABLE car; + + +CREATE TABLE car ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, model VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-hsql.sql new file mode 100644 index 00000000000..9d5026bc674 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-hsql.sql @@ -0,0 +1 @@ +CREATE TABLE car ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, model VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mariadb.sql new file mode 100644 index 00000000000..41797233764 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mariadb.sql @@ -0,0 +1 @@ +CREATE TABLE car ( id INT NOT NULL AUTO_INCREMENT, model VARCHAR(100), PRIMARY KEY (id)); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mssql.sql new file mode 100644 index 00000000000..60acad12fad --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mssql.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS car; +CREATE TABLE car ( id int IDENTITY(1,1) PRIMARY KEY, model VARCHAR(100)); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mysql.sql new file mode 100644 index 00000000000..41797233764 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-mysql.sql @@ -0,0 +1 @@ +CREATE TABLE car ( id INT NOT NULL AUTO_INCREMENT, model VARCHAR(100), PRIMARY KEY (id)); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-oracle.sql new file mode 100644 index 00000000000..18c251e1898 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-oracle.sql @@ -0,0 +1,2 @@ +DROP TABLE CAR; +CREATE TABLE CAR ( id NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, model VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-postgres.sql new file mode 100644 index 00000000000..0118aeda21e --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/PartTreeQueryMappingConfigurationIntegrationTests-postgres.sql @@ -0,0 +1,2 @@ +DROP TABLE car; +CREATE TABLE car ( id SERIAL PRIMARY KEY, model VARCHAR(100)); \ No newline at end of file