diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java index 51cd33c4..4fa83679 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,11 @@ import java.util.stream.Stream; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.QueryMapper; +import org.springframework.data.mapping.Parameter; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.RenderContextFactory; -import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; +import org.springframework.data.relational.core.mapping.AggregatePath; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; @@ -48,102 +49,110 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * Implementation of {@link RelationalQueryCreator} that creates {@link Stream} of deletion {@link ParametrizedQuery} + * Implementation of {@link RelationalQueryCreator} that creates {@link List} of deletion {@link ParametrizedQuery} * from a {@link PartTree}. * * @author Yunyoung LEE - * @since 2.3 + * @author Nikita Konev + * @since 3.5 */ -class JdbcDeleteQueryCreator extends RelationalQueryCreator> { +class JdbcDeleteQueryCreator extends RelationalQueryCreator> { - private final RelationalMappingContext context; - private final QueryMapper queryMapper; - private final RelationalEntityMetadata entityMetadata; - private final RenderContextFactory renderContextFactory; + private final RelationalMappingContext context; + private final QueryMapper queryMapper; + private final RelationalEntityMetadata entityMetadata; + private final RenderContextFactory renderContextFactory; - /** - * Creates new instance of this class with the given {@link PartTree}, {@link JdbcConverter}, {@link Dialect}, - * {@link RelationalEntityMetadata} and {@link RelationalParameterAccessor}. - * - * @param context - * @param tree part tree, must not be {@literal null}. - * @param converter must not be {@literal null}. - * @param dialect must not be {@literal null}. - * @param entityMetadata relational entity metadata, must not be {@literal null}. - * @param accessor parameter metadata provider, must not be {@literal null}. - */ - JdbcDeleteQueryCreator(RelationalMappingContext context, PartTree tree, JdbcConverter converter, Dialect dialect, - RelationalEntityMetadata entityMetadata, RelationalParameterAccessor accessor) { - super(tree, accessor); + /** + * Creates new instance of this class with the given {@link PartTree}, {@link JdbcConverter}, {@link Dialect}, + * {@link RelationalEntityMetadata} and {@link RelationalParameterAccessor}. + * + * @param context + * @param tree part tree, must not be {@literal null}. + * @param converter must not be {@literal null}. + * @param dialect must not be {@literal null}. + * @param entityMetadata relational entity metadata, must not be {@literal null}. + * @param accessor parameter metadata provider, must not be {@literal null}. + */ + JdbcDeleteQueryCreator(RelationalMappingContext context, PartTree tree, JdbcConverter converter, Dialect dialect, + RelationalEntityMetadata entityMetadata, RelationalParameterAccessor accessor) { - Assert.notNull(converter, "JdbcConverter must not be null"); - Assert.notNull(dialect, "Dialect must not be null"); - Assert.notNull(entityMetadata, "Relational entity metadata must not be null"); + super(tree, accessor); - this.context = context; + Assert.notNull(converter, "JdbcConverter must not be null"); + Assert.notNull(dialect, "Dialect must not be null"); + Assert.notNull(entityMetadata, "Relational entity metadata must not be null"); - this.entityMetadata = entityMetadata; - this.queryMapper = new QueryMapper(dialect, converter); - this.renderContextFactory = new RenderContextFactory(dialect); - } + this.context = context; + this.entityMetadata = entityMetadata; + this.queryMapper = new QueryMapper(converter); + this.renderContextFactory = new RenderContextFactory(dialect); + } - @Override - protected Stream complete(@Nullable Criteria criteria, Sort sort) { + @Override + protected List complete(@Nullable Criteria criteria, Sort sort) { - RelationalPersistentEntity entity = entityMetadata.getTableEntity(); - Table table = Table.create(entityMetadata.getTableName()); - MapSqlParameterSource parameterSource = new MapSqlParameterSource(); + RelationalPersistentEntity entity = entityMetadata.getTableEntity(); + Table table = Table.create(entityMetadata.getTableName()); + MapSqlParameterSource parameterSource = new MapSqlParameterSource(); - SqlContext sqlContext = new SqlContext(entity); + SqlContext sqlContext = new SqlContext(entity); - Condition condition = criteria == null ? null - : queryMapper.getMappedObject(parameterSource, criteria, table, entity); + Condition condition = criteria == null ? null + : queryMapper.getMappedObject(parameterSource, criteria, table, entity); - // create select criteria query for subselect - SelectWhere selectBuilder = StatementBuilder.select(sqlContext.getIdColumn()).from(table); - Select select = condition == null ? selectBuilder.build() : selectBuilder.where(condition).build(); + // create select criteria query for subselect + SelectWhere selectBuilder = StatementBuilder.select(sqlContext.getIdColumn()).from(table); + Select select = condition == null ? selectBuilder.build() : selectBuilder.where(condition).build(); - // create delete relation queries - List deleteChain = new ArrayList<>(); - deleteRelations(deleteChain, entity, select); + // create delete relation queries + List deleteChain = new ArrayList<>(); + deleteRelations(deleteChain, entity, select); - // crate delete query - DeleteWhere deleteBuilder = StatementBuilder.delete(table); - Delete delete = condition == null ? deleteBuilder.build() : deleteBuilder.where(condition).build(); + // crate delete query + DeleteWhere deleteBuilder = StatementBuilder.delete(table); + Delete delete = condition == null ? deleteBuilder.build() : deleteBuilder.where(condition).build(); - deleteChain.add(delete); + deleteChain.add(delete); - SqlRenderer renderer = SqlRenderer.create(renderContextFactory.createRenderContext()); - return deleteChain.stream().map(d -> new ParametrizedQuery(renderer.render(d), parameterSource)); - } + SqlRenderer renderer = SqlRenderer.create(renderContextFactory.createRenderContext()); - private void deleteRelations(List deleteChain, RelationalPersistentEntity entity, Select parentSelect) { + List queries = new ArrayList<>(deleteChain.size()); + for (Delete d : deleteChain) { + queries.add(new ParametrizedQuery(renderer.render(d), parameterSource)); + } - for (PersistentPropertyPath path : context - .findPersistentPropertyPaths(entity.getType(), p -> true)) { + return queries; + } - PersistentPropertyPathExtension extPath = new PersistentPropertyPathExtension(context, path); + private void deleteRelations(List deleteChain, RelationalPersistentEntity entity, Select parentSelect) { - // prevent duplication on recursive call - if (path.getLength() > 1 && !extPath.getParentPath().isEmbedded()) { - continue; - } + for (PersistentPropertyPath path : context + .findPersistentPropertyPaths(entity.getType(), p -> true)) { - if (extPath.isEntity() && !extPath.isEmbedded()) { + AggregatePath aggregatePath = context.getAggregatePath(path); - SqlContext sqlContext = new SqlContext(extPath.getLeafEntity()); + // prevent duplication on recursive call + if (path.getLength() > 1 && !aggregatePath.getParentPath().isEmbedded()) { + continue; + } - Condition inCondition = Conditions.in(sqlContext.getTable().column(extPath.getReverseColumnName()), - parentSelect); + if (aggregatePath.isEntity() && !aggregatePath.isEmbedded()) { - Select select = StatementBuilder - .select(sqlContext.getTable().column(extPath.getIdDefiningParentPath().getIdColumnName()) - // sqlContext.getIdColumn() - ).from(sqlContext.getTable()).where(inCondition).build(); - deleteRelations(deleteChain, extPath.getLeafEntity(), select); + SqlContext sqlContext = new SqlContext(aggregatePath.getLeafEntity()); - deleteChain.add(StatementBuilder.delete(sqlContext.getTable()).where(inCondition).build()); - } - } - } + Condition inCondition = Conditions + .in(sqlContext.getTable().column(aggregatePath.getTableInfo().reverseColumnInfo().name()), parentSelect); + + Select select = StatementBuilder.select( // + sqlContext.getTable().column(aggregatePath.getIdDefiningParentPath().getTableInfo().idColumnName()) // + ).from(sqlContext.getTable()) // + .where(inCondition) // + .build(); + deleteRelations(deleteChain, aggregatePath.getLeafEntity(), select); + + deleteChain.add(StatementBuilder.delete(sqlContext.getTable()).where(inCondition).build()); + } + } + } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java index ea90bfc5..20db4d78 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java @@ -23,8 +23,8 @@ import java.util.Collection; import java.util.List; import java.util.function.Function; import java.util.function.LongSupplier; -import java.util.stream.Stream; import java.util.function.Supplier; +import java.util.stream.Stream; import org.springframework.core.convert.converter.Converter; import org.springframework.data.domain.Pageable; @@ -58,6 +58,8 @@ import org.springframework.util.Assert; * @author Jens Schauder * @author Diego Krupitza * @author Mikhail Polivakha + * @author Yunyoung LEE + * @author Nikita Konev * @since 2.0 */ public class PartTreeJdbcQuery extends AbstractJdbcQuery { @@ -123,17 +125,23 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery { } @Override + @Nullable public Object execute(Object[] values) { RelationalParametersParameterAccessor accessor = new RelationalParametersParameterAccessor(getQueryMethod(), values); - if (tree.isDelete()) { - JdbcQueryExecution execution = createModifyingQueryExecutor(); - return createDeleteQueries(accessor) - .map(query -> execution.execute(query.getQuery(), query.getParameterSource())) - .reduce((a, b) -> b); - } + if (tree.isDelete()) { + JdbcQueryExecution execution = createModifyingQueryExecutor(); + + List queries = createDeleteQueries(accessor); + Object result = null; + for (ParametrizedQuery query : queries) { + result = execution.execute(query.getQuery(), query.getParameterSource(dialect.getLikeEscaper())); + } + + return result; + } ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); ParametrizedQuery query = createQuery(accessor, processor.getReturnedType()); @@ -153,11 +161,13 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery { JdbcQueryExecution queryExecution = getJdbcQueryExecution(extractor, rowMapper); if (getQueryMethod().isSliceQuery()) { + //noinspection unchecked return new SliceQueryExecution<>((JdbcQueryExecution>) queryExecution, accessor.getPageable()); } if (getQueryMethod().isPageQuery()) { + //noinspection unchecked return new PageQueryExecution<>((JdbcQueryExecution>) queryExecution, accessor.getPageable(), () -> { @@ -186,14 +196,14 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery { return queryCreator.createQuery(getDynamicSort(accessor)); } - private Stream createDeleteQueries(RelationalParametersParameterAccessor accessor) { + private List createDeleteQueries(RelationalParametersParameterAccessor accessor) { - RelationalEntityMetadata entityMetadata = getQueryMethod().getEntityInformation(); + RelationalEntityMetadata entityMetadata = getQueryMethod().getEntityInformation(); - JdbcDeleteQueryCreator queryCreator = new JdbcDeleteQueryCreator(context, tree, converter, dialect, entityMetadata, - accessor); - return queryCreator.createQuery(); - } + JdbcDeleteQueryCreator queryCreator = new JdbcDeleteQueryCreator(context, tree, converter, dialect, entityMetadata, + accessor); + return queryCreator.createQuery(); + } private JdbcQueryExecution getJdbcQueryExecution(@Nullable ResultSetExtractor extractor, Supplier> rowMapper) { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java index 0c085381..142c99c5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java @@ -46,9 +46,11 @@ import org.springframework.test.jdbc.JdbcTestUtils; * Very simple use cases for creation and usage of JdbcRepositories with test {@link Embedded} annotation in Entities. * * @author Bastian Wilhelm + * @author Yunyoung LEE + * @author Nikita Konev */ @IntegrationTest -public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { +class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { @Configuration @Import(TestConfiguration.class) @@ -66,7 +68,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { @Autowired Dialect dialect; @Test // DATAJDBC-111 - public void savesAnEntity() throws SQLException { + void savesAnEntity() throws SQLException { DummyEntity entity = repository.save(createDummyEntity()); @@ -83,7 +85,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void saveAndLoadAnEntity() { + void saveAndLoadAnEntity() { DummyEntity entity = repository.save(createDummyEntity()); @@ -99,7 +101,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void findAllFindsAllEntities() { + void findAllFindsAllEntities() { DummyEntity entity = repository.save(createDummyEntity()); DummyEntity other = repository.save(createDummyEntity()); @@ -112,14 +114,14 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void findByIdReturnsEmptyWhenNoneFound() { + void findByIdReturnsEmptyWhenNoneFound() { // NOT saving anything, so DB is empty assertThat(repository.findById(-1L)).isEmpty(); } @Test // DATAJDBC-111 - public void update() { + void update() { DummyEntity entity = repository.save(createDummyEntity()); @@ -139,7 +141,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void updateMany() { + void updateMany() { DummyEntity entity = repository.save(createDummyEntity()); DummyEntity other = repository.save(createDummyEntity()); @@ -163,7 +165,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void deleteById() { + void deleteById() { DummyEntity one = repository.save(createDummyEntity()); DummyEntity two = repository.save(createDummyEntity()); @@ -177,7 +179,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void deleteByEntity() { + void deleteByEntity() { DummyEntity one = repository.save(createDummyEntity()); DummyEntity two = repository.save(createDummyEntity()); DummyEntity three = repository.save(createDummyEntity()); @@ -190,7 +192,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void deleteByList() { + void deleteByList() { DummyEntity one = repository.save(createDummyEntity()); DummyEntity two = repository.save(createDummyEntity()); @@ -204,7 +206,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } @Test // DATAJDBC-111 - public void deleteAll() { + void deleteAll() { repository.save(createDummyEntity()); repository.save(createDummyEntity()); @@ -217,52 +219,53 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { assertThat(repository.findAll()).isEmpty(); } - @Test // DATAJDBC-551 - public void deleteByTest() { + @Test // GH-771 + void deleteBy() { - DummyEntity one = repository.save(createDummyEntity("root1")); - DummyEntity two = repository.save(createDummyEntity("root2")); - DummyEntity three = repository.save(createDummyEntity("root3")); + DummyEntity one = repository.save(createDummyEntity("root1")); + DummyEntity two = repository.save(createDummyEntity("root2")); + DummyEntity three = repository.save(createDummyEntity("root3")); - assertThat(repository.deleteByTest(two.getTest())).isEqualTo(1); + assertThat(repository.deleteByTest(two.getTest())).isEqualTo(1); - assertThat(repository.findAll()) // - .extracting(DummyEntity::getId) // - .containsExactlyInAnyOrder(one.getId(), three.getId()); + assertThat(repository.findAll()) // + .extracting(DummyEntity::getId) // + .containsExactlyInAnyOrder(one.getId(), three.getId()); - Long count = template.queryForObject("select count(1) from dummy_entity2", Collections.emptyMap(), Long.class); - assertThat(count).isEqualTo(4); + Long count = template.queryForObject("select count(1) from dummy_entity2", Collections.emptyMap(), Long.class); + assertThat(count).isEqualTo(4); - } + } - private static DummyEntity createDummyEntity() { - return createDummyEntity("root"); - } + private static DummyEntity createDummyEntity() { + return createDummyEntity("root"); + } - private static DummyEntity createDummyEntity(String test) { - DummyEntity entity = new DummyEntity(); - entity.setTest(test); + private static DummyEntity createDummyEntity(String test) { - final Embeddable embeddable = new Embeddable(); - embeddable.setTest("embedded"); + DummyEntity entity = new DummyEntity(); + entity.setTest(test); - final DummyEntity2 dummyEntity21 = new DummyEntity2(); - dummyEntity21.setTest("entity1"); + final Embeddable embeddable = new Embeddable(); + embeddable.setTest("embedded"); - final DummyEntity2 dummyEntity22 = new DummyEntity2(); - dummyEntity22.setTest("entity2"); + final DummyEntity2 dummyEntity21 = new DummyEntity2(); + dummyEntity21.setTest("entity1"); - embeddable.getList().add(dummyEntity21); - embeddable.getList().add(dummyEntity22); + final DummyEntity2 dummyEntity22 = new DummyEntity2(); + dummyEntity22.setTest("entity2"); - entity.setEmbeddable(embeddable); + embeddable.getList().add(dummyEntity21); + embeddable.getList().add(dummyEntity22); - return entity; - } + entity.setEmbeddable(embeddable); + + return entity; + } interface DummyEntityRepository extends CrudRepository { - int deleteByTest(String test); - } + int deleteByTest(String test); + } private static class DummyEntity { @Column("ID") @@ -298,8 +301,7 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests { } private static class Embeddable { - @MappedCollection(idColumn = "DUMMY_ID", keyColumn = "ORDER_KEY") - List list = new ArrayList<>(); + @MappedCollection(idColumn = "DUMMY_ID", keyColumn = "ORDER_KEY") List list = new ArrayList<>(); String test; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsChainHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsChainHsqlIntegrationTests.java new file mode 100644 index 00000000..fe95e85f --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsChainHsqlIntegrationTests.java @@ -0,0 +1,121 @@ +package org.springframework.data.jdbc.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +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.repository.support.JdbcRepositoryFactory; +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.repository.CrudRepository; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; + +/** + * Integration tests with collections chain. + * + * @author Yunyoung LEE + * @author Nikita Konev + */ +@IntegrationTest +@EnabledOnDatabase(DatabaseType.HSQL) +class JdbcRepositoryWithCollectionsChainHsqlIntegrationTests { + + @Autowired NamedParameterJdbcTemplate template; + @Autowired DummyEntityRepository repository; + + private static DummyEntity createDummyEntity() { + + DummyEntity entity = new DummyEntity(); + entity.name = "Entity Name"; + return entity; + } + + @Test // DATAJDBC-551 + void deleteByName() { + + ChildElement element1 = createChildElement("one"); + ChildElement element2 = createChildElement("two"); + + DummyEntity entity = createDummyEntity(); + entity.content.add(element1); + entity.content.add(element2); + + entity = repository.save(entity); + + assertThat(repository.deleteByName("Entity Name")).isEqualTo(1); + + assertThat(repository.findById(entity.id)).isEmpty(); + + Long count = template.queryForObject("select count(1) from grand_child_element", new HashMap<>(), Long.class); + assertThat(count).isEqualTo(0); + } + + private ChildElement createChildElement(String name) { + + ChildElement element = new ChildElement(); + element.name = name; + element.content.add(createGrandChildElement(name + "1")); + element.content.add(createGrandChildElement(name + "2")); + return element; + } + + private GrandChildElement createGrandChildElement(String content) { + + GrandChildElement element = new GrandChildElement(); + element.content = content; + return element; + } + + interface DummyEntityRepository extends CrudRepository { + long deleteByName(String name); + } + + @Configuration + @Import(TestConfiguration.class) + static class Config { + + @Autowired JdbcRepositoryFactory factory; + + @Bean + Class testClass() { + return JdbcRepositoryWithCollectionsChainHsqlIntegrationTests.class; + } + + @Bean + DummyEntityRepository dummyEntityRepository() { + return factory.getRepository(DummyEntityRepository.class); + } + } + + static class DummyEntity { + + String name; + Set content = new HashSet<>(); + @Id private Long id; + + } + + static class ChildElement { + + String name; + Set content = new HashSet<>(); + @Id private Long id; + } + + static class GrandChildElement { + + String content; + @Id private Long id; + } + +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsChainIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsChainIntegrationTests.java deleted file mode 100644 index 8b2c7f70..00000000 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsChainIntegrationTests.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.springframework.data.jdbc.repository; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS; - -import lombok.Data; -import lombok.RequiredArgsConstructor; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -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.repository.support.JdbcRepositoryFactory; -import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener; -import org.springframework.data.jdbc.testing.TestConfiguration; -import org.springframework.data.repository.CrudRepository; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.annotation.Transactional; - -/** - * Integration tests with collections chain. - * - * @author Yunyoung LEE - */ -@ContextConfiguration -@Transactional -@TestExecutionListeners(value = AssumeFeatureTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS) -@ExtendWith(SpringExtension.class) -public class JdbcRepositoryWithCollectionsChainIntegrationTests { - - @Autowired NamedParameterJdbcTemplate template; - @Autowired DummyEntityRepository repository; - - private static DummyEntity createDummyEntity() { - - DummyEntity entity = new DummyEntity(); - entity.setName("Entity Name"); - return entity; - } - - @Test // DATAJDBC-551 - public void deleteByName() { - - ChildElement element1 = createChildElement("one"); - ChildElement element2 = createChildElement("two"); - - DummyEntity entity = createDummyEntity(); - entity.content.add(element1); - entity.content.add(element2); - - entity = repository.save(entity); - - assertThat(repository.deleteByName("Entity Name")).isEqualTo(1); - - assertThat(repository.findById(entity.id)).isEmpty(); - - Long count = template.queryForObject("select count(1) from grand_child_element", new HashMap<>(), Long.class); - assertThat(count).isEqualTo(0); - } - - private ChildElement createChildElement(String name) { - - ChildElement element = new ChildElement(); - element.name = name; - element.content.add(createGrandChildElement(name + "1")); - element.content.add(createGrandChildElement(name + "2")); - return element; - } - - private GrandChildElement createGrandChildElement(String content) { - - GrandChildElement element = new GrandChildElement(); - element.content = content; - return element; - } - - interface DummyEntityRepository extends CrudRepository { - long deleteByName(String name); - } - - @Configuration - @Import(TestConfiguration.class) - static class Config { - - @Autowired JdbcRepositoryFactory factory; - - @Bean - Class testClass() { - return JdbcRepositoryWithCollectionsChainIntegrationTests.class; - } - - @Bean - DummyEntityRepository dummyEntityRepository() { - return factory.getRepository(DummyEntityRepository.class); - } - } - - @Data - static class DummyEntity { - - String name; - Set content = new HashSet<>(); - @Id private Long id; - - } - - @RequiredArgsConstructor - static class ChildElement { - - String name; - Set content = new HashSet<>(); - @Id private Long id; - } - - @RequiredArgsConstructor - static class GrandChildElement { - - String content; - @Id private Long id; - } - -} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java index b71d509c..dbbd56b6 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java @@ -42,9 +42,11 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; * * @author Jens Schauder * @author Thomas Lang + * @author Yunyoung LEE + * @author Nikita Konev */ @IntegrationTest -public class JdbcRepositoryWithCollectionsIntegrationTests { +class JdbcRepositoryWithCollectionsIntegrationTests { @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; @@ -57,7 +59,7 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { } @Test // DATAJDBC-113 - public void saveAndLoadEmptySet() { + void saveAndLoadEmptySet() { DummyEntity entity = repository.save(createDummyEntity()); @@ -71,7 +73,7 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { } @Test // DATAJDBC-113 - public void saveAndLoadNonEmptySet() { + void saveAndLoadNonEmptySet() { Element element1 = new Element(); Element element2 = new Element(); @@ -94,7 +96,7 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { } @Test // DATAJDBC-113 - public void findAllLoadsCollection() { + void findAllLoadsCollection() { Element element1 = new Element(); Element element2 = new Element(); @@ -117,7 +119,7 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { @Test // DATAJDBC-113 @EnabledOnFeature(SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES) - public void updateSet() { + void updateSet() { Element element1 = createElement("one"); Element element2 = createElement("two"); @@ -154,7 +156,7 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { } @Test // DATAJDBC-113 - public void deletingWithSet() { + void deletingWithSet() { Element element1 = createElement("one"); Element element2 = createElement("two"); @@ -173,8 +175,8 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { assertThat(count).isEqualTo(0); } - @Test // DATAJDBC-551 - public void deleteByName() { + @Test // GH-771 + void deleteByName() { Element element1 = createElement("one"); Element element2 = createElement("two"); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsChainHsqlIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsChainHsqlIntegrationTests-hsql.sql new file mode 100644 index 00000000..b3e6c450 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsChainHsqlIntegrationTests-hsql.sql @@ -0,0 +1,17 @@ +CREATE TABLE DUMMY_ENTITY +( + ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, + NAME VARCHAR(100) +); +CREATE TABLE CHILD_ELEMENT +( + ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, + NAME VARCHAR(100), + DUMMY_ENTITY BIGINT +); +CREATE TABLE GRAND_CHILD_ELEMENT +( + ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, + CONTENT VARCHAR(100), + CHILD_ELEMENT BIGINT +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsChainIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsChainIntegrationTests-hsql.sql deleted file mode 100644 index 3c26f132..00000000 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsChainIntegrationTests-hsql.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100)); -CREATE TABLE child_element (id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, NAME VARCHAR(100), dummy_entity BIGINT); -CREATE TABLE grand_child_element (id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CONTENT VARCHAR(100), child_element BIGINT);