Files
ebean/ebean-test/src/test/java/org/tests/cascade/TestOrphanCollectionReplacement.java
T
Rob Bygrave 44e0585fb7 #2961 - Followup for #2952 - @OneToMany + orphanRemoval + @SoftDelete + non-BeanCollection collection results in hard deletes
As noted in comments in #2952

In the internals of SaveManyBeans we have:

- BUG: the deleteByParentId is hard delete and does not care for soft delete
- YUK: internally we have 3 ways of performing the orphan removal when we really want 2

This change fixes the BUG and fixes the YUK. It does this by removing the special case at: https://github.com/ebean-orm/ebean/blob/ebean-parent-13.11.3/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java#L347-L350 ... and replacing it with the more common orphan removal code used when we do not have BeanCollection modifications.

The result of this change is that in SaveManyBeans internals we get back to have 2 ways to remove orphans.

- A BeanCollection with modifications: Orphans explicitly deleted using the known elements removed from the collection
- All other cases: Orphans as everything NOT in the collection that is going to be updated
2023-02-10 13:08:44 +13:00

63 lines
2.0 KiB
Java

package org.tests.cascade;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import io.ebean.xtest.BaseTestCase;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
class TestOrphanCollectionReplacement extends BaseTestCase {
@Test
void replaceCollection_whenOrphan_expect_forcedInsert() {
long parentId;
{ // setup
List<COOneMany> children = new ArrayList<>();
children.add(new COOneMany("c0"));
children.add(new COOneMany("c1"));
COOne parent = new COOne("p0");
parent.setChildren(children);
DB.save(parent);
parentId = parent.getId();
}
{ // act
COOne fetchedParent = DB.find(COOne.class, parentId);
assert fetchedParent != null;
COOneMany role = new COOneMany("c2");
List<COOneMany> filtered = fetchedParent.getChildren().stream().filter(r -> "c0".equals(r.getName())).collect(Collectors.toList());
List<COOneMany> updatedRoles = new ArrayList<>();
updatedRoles.addAll(filtered);
updatedRoles.addAll(List.of(role));
fetchedParent.setChildren(updatedRoles);
LoggedSql.start();
DB.save(fetchedParent);
var sql = LoggedSql.stop();
if (isH2() || isPostgresCompatible()) { // using deleted=true vs deleted=1
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("update coone_many set deleted=true where coone_id = ? and not ( id in (?) )");
assertThat(sql.get(1)).contains(" -- bind(");
assertThat(sql.get(2)).contains("insert into coone_many (coone_id, name, deleted) values (?,?,?)");
assertThat(sql.get(3)).contains(" -- bind(");
}
}
COOne fetchedUser2 = DB.find(COOne.class, parentId);
requireNonNull(fetchedUser2);
assertEquals(2, fetchedUser2.getChildren().size());
}
}