Polishing.

Fix merge problems.

Execute tests only for HsqlDb.

Refactoring to use lists instead of streams.
We try to avoid the latter, since they are prone to cause performance issues.

Minor refactorings.

See #771
See #230
Original pull request #1486
This commit is contained in:
Jens Schauder
2024-12-17 14:37:52 +01:00
parent 1515a3af42
commit 73d6788067
8 changed files with 299 additions and 273 deletions

View File

@@ -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<Stream<ParametrizedQuery>> {
class JdbcDeleteQueryCreator extends RelationalQueryCreator<List<ParametrizedQuery>> {
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<ParametrizedQuery> complete(@Nullable Criteria criteria, Sort sort) {
@Override
protected List<ParametrizedQuery> 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<Delete> deleteChain = new ArrayList<>();
deleteRelations(deleteChain, entity, select);
// create delete relation queries
List<Delete> 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<Delete> deleteChain, RelationalPersistentEntity<?> entity, Select parentSelect) {
List<ParametrizedQuery> queries = new ArrayList<>(deleteChain.size());
for (Delete d : deleteChain) {
queries.add(new ParametrizedQuery(renderer.render(d), parameterSource));
}
for (PersistentPropertyPath<RelationalPersistentProperty> path : context
.findPersistentPropertyPaths(entity.getType(), p -> true)) {
return queries;
}
PersistentPropertyPathExtension extPath = new PersistentPropertyPathExtension(context, path);
private void deleteRelations(List<Delete> deleteChain, RelationalPersistentEntity<?> entity, Select parentSelect) {
// prevent duplication on recursive call
if (path.getLength() > 1 && !extPath.getParentPath().isEmbedded()) {
continue;
}
for (PersistentPropertyPath<RelationalPersistentProperty> 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());
}
}
}
}

View File

@@ -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<ParametrizedQuery> 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<Collection<Object>>) queryExecution, accessor.getPageable());
}
if (getQueryMethod().isPageQuery()) {
//noinspection unchecked
return new PageQueryExecution<>((JdbcQueryExecution<Collection<Object>>) queryExecution, accessor.getPageable(),
() -> {
@@ -186,14 +196,14 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
return queryCreator.createQuery(getDynamicSort(accessor));
}
private Stream<ParametrizedQuery> createDeleteQueries(RelationalParametersParameterAccessor accessor) {
private List<ParametrizedQuery> 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<Boolean> extractor,
Supplier<RowMapper<?>> rowMapper) {

View File

@@ -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<DummyEntity, Long> {
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<DummyEntity2> list = new ArrayList<>();
@MappedCollection(idColumn = "DUMMY_ID", keyColumn = "ORDER_KEY") List<DummyEntity2> list = new ArrayList<>();
String test;

View File

@@ -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<DummyEntity, Long> {
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<ChildElement> content = new HashSet<>();
@Id private Long id;
}
static class ChildElement {
String name;
Set<GrandChildElement> content = new HashSet<>();
@Id private Long id;
}
static class GrandChildElement {
String content;
@Id private Long id;
}
}

View File

@@ -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<DummyEntity, Long> {
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<ChildElement> content = new HashSet<>();
@Id private Long id;
}
@RequiredArgsConstructor
static class ChildElement {
String name;
Set<GrandChildElement> content = new HashSet<>();
@Id private Long id;
}
@RequiredArgsConstructor
static class GrandChildElement {
String content;
@Id private Long id;
}
}

View File

@@ -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");

View File

@@ -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
);

View File

@@ -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);