Merge pull request #2273 from ebean-orm/feature/2272

#2272 Fix for collection with orphanRemoval (such that equals/hashCode isn't use)
This commit is contained in:
Rob Bygrave
2021-07-28 17:14:09 +12:00
committed by GitHub
9 changed files with 258 additions and 167 deletions
@@ -3,9 +3,7 @@ package io.ebean.common;
import io.ebean.bean.EntityBean;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.*;
/**
* Holds sets of additions and deletions from a 'owner' List Set or Map.
@@ -23,19 +21,19 @@ class ModifyHolder<E> implements Serializable {
/**
* Deletions list for manyToMany persistence.
*/
private Set<E> modifyDeletions = new LinkedHashSet<>();
private Map<E,Object> modifyDeletions = new IdentityHashMap<>();
/**
* Additions list for manyToMany persistence.
*/
private Set<E> modifyAdditions = new LinkedHashSet<>();
private Map<E,Object> modifyAdditions = new IdentityHashMap<>();
private boolean touched;
void reset() {
touched = false;
modifyDeletions = new LinkedHashSet<>();
modifyAdditions = new LinkedHashSet<>();
modifyDeletions = new IdentityHashMap<>();
modifyAdditions = new IdentityHashMap<>();
}
/**
@@ -50,51 +48,46 @@ class ModifyHolder<E> implements Serializable {
}
private boolean undoDeletion(E bean) {
return (bean != null) && modifyDeletions.remove(bean);
return (bean != null) && modifyDeletions.remove(bean) != null;
}
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
}
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
// Insert
modifyAdditions.add(bean);
modifyAdditions.put(bean, bean);
}
}
}
private boolean undoAddition(Object bean) {
return (bean != null) && modifyAdditions.remove(bean);
return (bean != null) && modifyAdditions.remove(bean) != null;
}
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
}
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
modifyDeletions.put((E) bean, bean);
}
}
}
Set<E> getModifyAdditions() {
return modifyAdditions;
return modifyAdditions.keySet();
}
Set<E> getModifyRemovals() {
return modifyDeletions;
return modifyDeletions.keySet();
}
/**
@@ -295,7 +295,16 @@ public class SaveManyBeans extends SaveManyBase {
}
transaction.depth(+1);
if (deletions != null && !deletions.isEmpty()) {
for (Object other : deletions) {
EntityBean otherDelete = (EntityBean) other;
// the object from the 'other' side of the ManyToMany
// build a intersection row for 'delete'
IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
persister.executeOrQueue(sqlDelete, transaction, queue);
}
}
if (additions != null && !additions.isEmpty()) {
for (Object other : additions) {
EntityBean otherBean = (EntityBean) other;
@@ -318,16 +327,6 @@ public class SaveManyBeans extends SaveManyBase {
}
}
}
if (deletions != null && !deletions.isEmpty()) {
for (Object other : deletions) {
EntityBean otherDelete = (EntityBean) other;
// the object from the 'other' side of the ManyToMany
// build a intersection row for 'delete'
IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
persister.executeOrQueue(sqlDelete, transaction, queue);
}
}
// decrease the depth back to what it was
transaction.depth(-1);
}
@@ -4,7 +4,6 @@ import io.ebean.bean.BeanCollection;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -13,9 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BeanListTest {
private Object object1 = new Object();
private Object object2 = new Object();
private Object object3 = new Object();
private final Object object1 = new Object();
private final Object object2 = new Object();
private final Object object3 = new Object();
private List<Object> all() {
List<Object> all = new ArrayList<>();
@@ -33,7 +32,7 @@ public class BeanListTest {
}
@Test
public void test_setModifyListening_null() throws Exception {
public void test_setModifyListening_null() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(null);
@@ -45,7 +44,7 @@ public class BeanListTest {
}
@Test
public void test_setModifyListening_none() throws Exception {
public void test_setModifyListening_none() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.NONE);
@@ -57,28 +56,28 @@ public class BeanListTest {
}
@Test
public void testAdd() throws Exception {
public void testAdd() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.add(object1);
assertThat(list.getModifyAdditions()).containsExactly(object1);
assertThat(list.getModifyAdditions()).containsOnly(object1);
assertThat(list.getModifyRemovals()).isEmpty();
list.add(object1);
assertThat(list.getModifyAdditions()).containsExactly(object1);
assertThat(list.getModifyAdditions()).containsOnly(object1);
list.add(object2);
assertThat(list.getModifyAdditions()).containsExactly(object1, object2);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2);
list.remove(object1);
assertThat(list.getModifyAdditions()).containsExactly(object2);
assertThat(list.getModifyAdditions()).containsOnly(object2);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void testAddAll_given_emptyStart() throws Exception {
public void testAddAll_given_emptyStart() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -86,12 +85,12 @@ public class BeanListTest {
// act
list.addAll(all());
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void test_removals_DeleteThenAddBack_expect_noChange() throws Exception {
public void test_removals_DeleteThenAddBack_expect_noChange() {
BeanList<Object> list = new BeanList<>(some());
list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS);
@@ -106,33 +105,33 @@ public class BeanListTest {
}
@Test
public void test_sort_whenAll_expect_noChange() throws Exception {
public void test_sort_whenAll_expect_noChange() {
BeanList<Object> list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
// act
Collections.sort(list, Comparator.comparingInt(Object::hashCode));
list.sort(Comparator.comparingInt(Object::hashCode));
assertThat(list.getModifyRemovals()).isEmpty();
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
public void test_sort_whenRemovals_expect_noChange() throws Exception {
public void test_sort_whenRemovals_expect_noChange() {
BeanList<Object> list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS);
// act
Collections.sort(list, Comparator.comparingInt(Object::hashCode));
list.sort(Comparator.comparingInt(Object::hashCode));
assertThat(list.getModifyRemovals()).isEmpty();
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
public void testAdd_given_someAlreadyIn() throws Exception {
public void testAdd_given_someAlreadyIn() {
BeanList<Object> list = new BeanList<>(some());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -143,12 +142,12 @@ public class BeanListTest {
assertThat(list.contains(object2)).isTrue();
list.add(object2); // object2 added as List allows duplicates
assertThat(list.getModifyAdditions()).containsExactly(object1, object2);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void testAddSome_given_someAlreadyIn() throws Exception {
public void testAddSome_given_someAlreadyIn() {
BeanList<Object> list = new BeanList<>(some());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -156,43 +155,43 @@ public class BeanListTest {
// act
list.addAll(all());
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void testRemove_given_beansInAdditions() throws Exception {
public void testRemove_given_beansInAdditions() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.addAll(all());
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
list.remove(object2);
list.remove(object3);
assertThat(list.getModifyAdditions()).containsExactly(object1);
assertThat(list.getModifyAdditions()).containsOnly(object1);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void testRemoveAll_given_beansInAdditions() throws Exception {
public void testRemoveAll_given_beansInAdditions() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.addAll(all());
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
list.removeAll(some());
assertThat(list.getModifyAdditions()).containsExactly(object1);
assertThat(list.getModifyAdditions()).containsOnly(object1);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void testRemove_given_beansNotInAdditions() throws Exception {
public void testRemove_given_beansNotInAdditions() {
BeanList<Object> list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -203,11 +202,11 @@ public class BeanListTest {
// assert
assertThat(list.getModifyAdditions()).isEmpty();
assertThat(list.getModifyRemovals()).containsExactly(object2, object3);
assertThat(list.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
public void testRemoveAll_given_beansNotInAdditions() throws Exception {
public void testRemoveAll_given_beansNotInAdditions() {
BeanList<Object> list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -217,11 +216,11 @@ public class BeanListTest {
// assert
assertThat(list.getModifyAdditions()).isEmpty();
assertThat(list.getModifyRemovals()).containsExactly(object2, object3);
assertThat(list.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
public void testClear() throws Exception {
public void testClear() {
BeanList<Object> list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -230,12 +229,12 @@ public class BeanListTest {
list.clear();
//assert
assertThat(list.getModifyRemovals()).containsExactly(object1, object2, object3);
assertThat(list.getModifyRemovals()).containsOnly(object1, object2, object3);
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
public void testClear_given_someBeansInAdditions() throws Exception {
public void testClear_given_someBeansInAdditions() {
BeanList<Object> list = new BeanList<>();
list.add(object1);
@@ -247,27 +246,27 @@ public class BeanListTest {
list.clear();
//assert
assertThat(list.getModifyRemovals()).containsExactly(object1);
assertThat(list.getModifyRemovals()).containsOnly(object1);
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
public void testRetainAll_given_beansInAdditions() throws Exception {
public void testRetainAll_given_beansInAdditions() {
BeanList<Object> list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.addAll(all());
assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
list.retainAll(some());
assertThat(list.getModifyAdditions()).containsExactly(object2, object3);
assertThat(list.getModifyAdditions()).containsOnly(object2, object3);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
public void testRetainAll_given_someBeansInAdditions() throws Exception {
public void testRetainAll_given_someBeansInAdditions() {
BeanList<Object> list = new BeanList<>();
list.add(object1);
@@ -278,12 +277,12 @@ public class BeanListTest {
// act
list.retainAll(some());
assertThat(list.getModifyAdditions()).containsExactly(object3);
assertThat(list.getModifyRemovals()).containsExactly(object1);
assertThat(list.getModifyAdditions()).containsOnly(object3);
assertThat(list.getModifyRemovals()).containsOnly(object1);
}
@Test
public void testRetainAll_given_noBeansInAdditions() throws Exception {
public void testRetainAll_given_noBeansInAdditions() {
BeanList<Object> list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -291,7 +290,7 @@ public class BeanListTest {
// act
list.retainAll(some());
assertThat(list.getModifyRemovals()).containsExactly(object1);
assertThat(list.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -34,30 +34,30 @@ public class BeanMapTest {
}
@Test
public void testAdd() throws Exception {
public void testAdd() {
BeanMap<String, Object> map = new BeanMap<>();
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
map.put("1", object1);
map.put("4", null);
assertThat(map.getModifyAdditions()).containsExactly(object1);
assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
map.put("1", object1);
map.put("4", null);
assertThat(map.getModifyAdditions()).containsExactly(object1);
assertThat(map.getModifyAdditions()).containsOnly(object1);
map.put("2", object2);
assertThat(map.getModifyAdditions()).containsExactly(object1, object2);
assertThat(map.getModifyAdditions()).containsOnly(object1, object2);
map.remove("1");
assertThat(map.getModifyAdditions()).containsExactly(object2);
assertThat(map.getModifyAdditions()).containsOnly(object2);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
public void testAddAll_given_emptyStart() throws Exception {
public void testAddAll_given_emptyStart() {
BeanMap<String, Object> set = new BeanMap<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -65,28 +65,28 @@ public class BeanMapTest {
// act
set.putAll(all());
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testAdd_given_someAlreadyIn() throws Exception {
public void testAdd_given_someAlreadyIn() {
BeanMap<String, Object> map = new BeanMap<>(some());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
// act
assertThat(map.values().contains(object1)).isFalse();
assertThat(map.containsValue(object1)).isFalse();
map.put("1", object1);
assertThat(map.values().contains(object2)).isTrue();
assertThat(map.containsValue(object2)).isTrue();
map.put("2", object2);
assertThat(map.getModifyAdditions()).containsExactly(object1);
assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
public void testAddSome_given_someAlreadyIn() throws Exception {
public void testAddSome_given_someAlreadyIn() {
BeanMap<String, Object> map = new BeanMap<>(some());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -94,44 +94,44 @@ public class BeanMapTest {
// act
map.putAll(all());
assertThat(map.getModifyAdditions()).containsExactly(object1);
assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
public void testRemove_given_beansInAdditions() throws Exception {
public void testRemove_given_beansInAdditions() {
BeanMap<String, Object> map = new BeanMap<>();
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
map.putAll(all());
assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
map.remove("2");
map.remove("3");
assertThat(map.getModifyAdditions()).containsExactly(object1);
assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
public void testRemoveAll_given_beansInAdditions() throws Exception {
public void testRemoveAll_given_beansInAdditions() {
BeanMap<String, Object> map = new BeanMap<>();
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
map.putAll(all());
assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
map.remove("2");
map.remove("3");
assertThat(map.getModifyAdditions()).containsExactly(object1);
assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
public void testRemove_given_beansNotInAdditions() throws Exception {
public void testRemove_given_beansNotInAdditions() {
BeanMap<String, Object> map = new BeanMap<>(all());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -142,11 +142,11 @@ public class BeanMapTest {
// assert
assertThat(map.getModifyAdditions()).isEmpty();
assertThat(map.getModifyRemovals()).containsExactly(object2, object3);
assertThat(map.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
public void testRemoveAll_given_beansNotInAdditions() throws Exception {
public void testRemoveAll_given_beansNotInAdditions() {
BeanMap<String, Object> map = new BeanMap<>(all());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -157,11 +157,11 @@ public class BeanMapTest {
// assert
assertThat(map.getModifyAdditions()).isEmpty();
assertThat(map.getModifyRemovals()).containsExactly(object2, object3);
assertThat(map.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
public void testClear() throws Exception {
public void testClear() {
BeanMap<String, Object> map = new BeanMap<>(all());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -170,12 +170,12 @@ public class BeanMapTest {
map.clear();
//assert
assertThat(map.getModifyRemovals()).containsExactly(object1, object2, object3);
assertThat(map.getModifyRemovals()).containsOnly(object1, object2, object3);
assertThat(map.getModifyAdditions()).isEmpty();
}
@Test
public void testClear_given_someBeansInAdditions() throws Exception {
public void testClear_given_someBeansInAdditions() {
BeanMap<String, EBasic> map = newModifyListeningMap();
map.put("2", object2);
@@ -185,7 +185,7 @@ public class BeanMapTest {
map.clear();
//assert
assertThat(map.getModifyRemovals()).containsExactly(object1);
assertThat(map.getModifyRemovals()).containsOnly(object1);
assertThat(map.getModifyAdditions()).isEmpty();
}
@@ -228,7 +228,7 @@ public class BeanMapTest {
assertThat(map).doesNotContainKeys("1");
assertThat(map.get("1")).isNull();
assertThat(map.getModifyRemovals()).containsExactly(object1);
assertThat(map.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -245,7 +245,7 @@ public class BeanMapTest {
assertThat(map).isEmpty();
assertThat(keySet).isEmpty();
assertThat(map.getModifyRemovals()).containsExactly(object1, object2);
assertThat(map.getModifyRemovals()).containsOnly(object1, object2);
}
@Test
@@ -258,20 +258,14 @@ public class BeanMapTest {
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
final Set<String> keySet = map.keySet();
final Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
final String key = iterator.next();
if (key.equals("2")) {
iterator.remove();
}
}
keySet.removeIf(key -> key.equals("2"));
assertThat(map).hasSize(2);
assertThat(keySet).hasSize(2);
assertThat(keySet).containsExactly("1", "3");
assertThat(map).containsKeys("1", "3");
assertThat(map.getModifyRemovals()).containsExactly(object2);
assertThat(map.getModifyRemovals()).containsOnly(object2);
}
@Test
@@ -294,7 +288,7 @@ public class BeanMapTest {
assertThat(keySet).containsExactly("1", "4");
assertThat(map).containsKeys("1", "4");
assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5);
}
@@ -318,7 +312,7 @@ public class BeanMapTest {
assertThat(keySet).containsExactly("2", "3", "5");
assertThat(map).containsKeys("2", "3", "5");
assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
assertThat(map.getModifyRemovals()).containsOnly(object1, object4);
}
@Test(expected = UnsupportedOperationException.class)
@@ -348,7 +342,7 @@ public class BeanMapTest {
assertThat(entries).isEmpty();
assertThat(map).isEmpty();
assertThat(map.getModifyRemovals()).containsExactly(object1);
assertThat(map.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -365,7 +359,7 @@ public class BeanMapTest {
assertThat(existed22).isFalse();
assertThat(map).hasSize(4);
assertThat(map.getModifyRemovals()).containsExactly(object1);
assertThat(map.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -395,7 +389,7 @@ public class BeanMapTest {
}
assertThat(map).hasSize(3);
assertThat(entries).hasSize(3);
assertThat(map.getModifyRemovals()).containsExactly(object2, object5);
assertThat(map.getModifyRemovals()).containsOnly(object2, object5);
}
@Test
@@ -406,7 +400,7 @@ public class BeanMapTest {
entries.removeAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
assertThat(map).hasSize(3);
assertThat(entries).hasSize(3);
assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
assertThat(map.getModifyRemovals()).containsOnly(object1, object4);
}
@Test
@@ -417,7 +411,7 @@ public class BeanMapTest {
entries.retainAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
assertThat(map).hasSize(2);
assertThat(entries).hasSize(2);
assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5);
}
private BeanMap<String, EBasic> newModifyListeningMap() {
@@ -31,28 +31,28 @@ public class BeanSetTest {
}
@Test
public void testAdd() throws Exception {
public void testAdd() {
BeanSet<Object> set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.add(object1);
assertThat(set.getModifyAdditions()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
set.add(object1);
assertThat(set.getModifyAdditions()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object1);
set.add(object2);
assertThat(set.getModifyAdditions()).containsExactly(object1, object2);
assertThat(set.getModifyAdditions()).containsOnly(object1, object2);
set.remove(object1);
assertThat(set.getModifyAdditions()).containsExactly(object2);
assertThat(set.getModifyAdditions()).containsOnly(object2);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testAddAll_given_emptyStart() throws Exception {
public void testAddAll_given_emptyStart() {
BeanSet<Object> set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -60,12 +60,12 @@ public class BeanSetTest {
// act
set.addAll(all());
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testAdd_given_someAlreadyIn() throws Exception {
public void testAdd_given_someAlreadyIn() {
BeanSet<Object> set = new BeanSet<>(some());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -76,12 +76,12 @@ public class BeanSetTest {
assertThat(set.contains(object2)).isTrue();
set.add(object2);
assertThat(set.getModifyAdditions()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testAddSome_given_someAlreadyIn() throws Exception {
public void testAddSome_given_someAlreadyIn() {
BeanSet<Object> set = new BeanSet<>(some());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -89,43 +89,43 @@ public class BeanSetTest {
// act
set.addAll(all());
assertThat(set.getModifyAdditions()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testRemove_given_beansInAdditions() throws Exception {
public void testRemove_given_beansInAdditions() {
BeanSet<Object> set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.addAll(all());
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
set.remove(object2);
set.remove(object3);
assertThat(set.getModifyAdditions()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testRemoveAll_given_beansInAdditions() throws Exception {
public void testRemoveAll_given_beansInAdditions() {
BeanSet<Object> set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.addAll(all());
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
set.removeAll(some());
assertThat(set.getModifyAdditions()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testRemove_given_beansNotInAdditions() throws Exception {
public void testRemove_given_beansNotInAdditions() {
BeanSet<Object> set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -136,11 +136,11 @@ public class BeanSetTest {
// assert
assertThat(set.getModifyAdditions()).isEmpty();
assertThat(set.getModifyRemovals()).containsExactly(object2, object3);
assertThat(set.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
public void testRemoveAll_given_beansNotInAdditions() throws Exception {
public void testRemoveAll_given_beansNotInAdditions() {
BeanSet<Object> set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -150,11 +150,11 @@ public class BeanSetTest {
// assert
assertThat(set.getModifyAdditions()).isEmpty();
assertThat(set.getModifyRemovals()).containsExactly(object2, object3);
assertThat(set.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
public void testClear() throws Exception {
public void testClear() {
BeanSet<Object> set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -163,12 +163,12 @@ public class BeanSetTest {
set.clear();
//assert
assertThat(set.getModifyRemovals()).containsExactly(object1, object2, object3);
assertThat(set.getModifyRemovals()).containsOnly(object1, object2, object3);
assertThat(set.getModifyAdditions()).isEmpty();
}
@Test
public void testClear_given_someBeansInAdditions() throws Exception {
public void testClear_given_someBeansInAdditions() {
BeanSet<Object> set = new BeanSet<>();
set.add(object1);
@@ -180,27 +180,27 @@ public class BeanSetTest {
set.clear();
//assert
assertThat(set.getModifyRemovals()).containsExactly(object1);
assertThat(set.getModifyRemovals()).containsOnly(object1);
assertThat(set.getModifyAdditions()).isEmpty();
}
@Test
public void testRetainAll_given_beansInAdditions() throws Exception {
public void testRetainAll_given_beansInAdditions() {
BeanSet<Object> set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.addAll(all());
assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
set.retainAll(some());
assertThat(set.getModifyAdditions()).containsExactly(object2, object3);
assertThat(set.getModifyAdditions()).containsOnly(object2, object3);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
public void testRetainAll_given_someBeansInAdditions() throws Exception {
public void testRetainAll_given_someBeansInAdditions() {
BeanSet<Object> set = new BeanSet<>();
set.add(object1);
@@ -211,12 +211,12 @@ public class BeanSetTest {
// act
set.retainAll(some());
assertThat(set.getModifyAdditions()).containsExactly(object3);
assertThat(set.getModifyRemovals()).containsExactly(object1);
assertThat(set.getModifyAdditions()).containsOnly(object3);
assertThat(set.getModifyRemovals()).containsOnly(object1);
}
@Test
public void testRetainAll_given_noBeansInAdditions() throws Exception {
public void testRetainAll_given_noBeansInAdditions() {
BeanSet<Object> set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -224,7 +224,7 @@ public class BeanSetTest {
// act
set.retainAll(some());
assertThat(set.getModifyRemovals()).containsExactly(object1);
assertThat(set.getModifyRemovals()).containsOnly(object1);
}
}
@@ -1,14 +1,15 @@
package org.tests.m2m;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.DB;
import org.junit.Test;
import org.tests.model.basic.MRole;
import org.tests.model.basic.MUser;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestM2MModifyTest extends BaseTestCase {
@Test
@@ -19,8 +20,8 @@ public class TestM2MModifyTest extends BaseTestCase {
MRole r1 = new MRole("r1");
// Save r1 and r2
Ebean.save(r0);
Ebean.save(r1);
DB.save(r0);
DB.save(r1);
// Create a new user
MUser u0 = new MUser("usr0");
@@ -28,32 +29,27 @@ public class TestM2MModifyTest extends BaseTestCase {
u0.addRole(r1);
// Save the user
Ebean.save(u0);
DB.save(u0);
List<MRole> roles = u0.getRoles();
Assert.assertTrue(roles.size() == 2);
assertThat(roles).hasSize(2);
u0 = Ebean.find(MUser.class, u0.getUserid());
u0 = DB.find(MUser.class, u0.getUserid());
roles = u0.getRoles();
int nrRoles = roles.size();
Assert.assertTrue(nrRoles == 2);
assertThat(roles).hasSize(2);
roles.clear();
roles.add(r0);
roles.add(r1);
roles.remove(r1);
Ebean.save(u0);
DB.save(u0);
u0 = Ebean.find(MUser.class, u0.getUserid());
u0 = DB.find(MUser.class, u0.getUserid());
roles = u0.getRoles();
nrRoles = roles.size();
Assert.assertTrue(nrRoles == 1);
assertThat(roles).hasSize(1);
}
}
@@ -0,0 +1,33 @@
package org.tests.model.orphanremoval;
import io.ebean.Model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
public class OmBeanListChild extends Model {
@Id
private Long id;
private final String name;
@ManyToOne
private OmBeanListParent parent;
@Version
private long version;
public OmBeanListChild(String name) {
this.name = name;
}
public Long getId() {
return id;
}
}
@@ -0,0 +1,40 @@
package org.tests.model.orphanremoval;
import io.ebean.Model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.util.List;
import static javax.persistence.CascadeType.ALL;
@Entity
public class OmBeanListParent extends Model {
@Id
private long id;
@Version
private long version;
@OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true)
private List<OmBeanListChild> children;
public long getId() {
return id;
}
public List<OmBeanListChild> getChildren() {
return children;
}
public void setChildren(List<OmBeanListChild> children) {
// So a BeanList is used and overwriting children will replace the table entries
this.children.clear();
this.children.addAll(children);
}
}
@@ -0,0 +1,37 @@
package org.tests.model.orphanremoval;
import org.junit.Test;
import java.util.List;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class TestOrphanRemovalOverwrite {
@Test
public void testOverwritingMapping() {
OmBeanListParent parent = new OmBeanListParent();
parent.save();
// Refreshing/querying sets the modifyListening flag on the association's BeanList
parent.refresh();
List<OmBeanListChild> childList = singletonList(new OmBeanListChild("child1"));
// Adding the children to the BeanList causes _ebean_getIdentity to be invoked before the children have been persisted and
// have Ids.
parent.setChildren(childList);
// Give the children Ids
parent.save();
// Refreshing here generates new objects for the associated children that are referred to by the parent.
parent.refresh();
assertNotNull("The children should now have Ids as they are persisted to the db.", childList.get(0).getId());
assertEquals("The children should have the same Id as the ones on the parent.",
childList.get(0).getId(), parent.getChildren().get(0).getId());
assertEquals("The children should therefore equal the children on the parent.", childList, parent.getChildren());
}
}