From 98a80295fef6d91b258c5dc20489854062d66093 Mon Sep 17 00:00:00 2001 From: Ryszard Trojnacki Date: Tue, 10 Sep 2019 20:07:33 +0200 Subject: [PATCH 1/4] Added code to allow for null items in @DbArray lists. --- .../server/type/ScalarTypeArrayList.java | 3 +- .../tests/model/array/TestDbArray_basic.java | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java index 8a728c0ec..f3d6ea4c7 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java @@ -104,7 +104,8 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase implements Sc protected List fromArray(Object[] array1) { List list = new ArrayList(array1.length); for (Object element : array1) { - list.add(converter.toElement(element)); + if(element == null ) list.add(null); + else list.add(converter.toElement(element)); } return new ModifyAwareList(list); } diff --git a/src/test/java/org/tests/model/array/TestDbArray_basic.java b/src/test/java/org/tests/model/array/TestDbArray_basic.java index f13bf3516..0566d6307 100644 --- a/src/test/java/org/tests/model/array/TestDbArray_basic.java +++ b/src/test/java/org/tests/model/array/TestDbArray_basic.java @@ -18,6 +18,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; public class TestDbArray_basic extends BaseTestCase { @@ -190,4 +191,56 @@ public class TestDbArray_basic extends BaseTestCase { Ebean.saveAll(all); Ebean.deleteAll(all); } + + @Test + public void nullItems() { + EArrayBean bean = new EArrayBean(); + bean.setName("null items"); + + List doubles = new ArrayList<>(); + doubles.add(1.3); + doubles.add(null); + doubles.add(2.4); + + bean.getPhoneNumbers().add("111222333"); + bean.getPhoneNumbers().add(null); + bean.getPhoneNumbers().add("333222111"); + + bean.getOtherIds().add(15L); + bean.getOtherIds().add(null); + bean.getOtherIds().add(30L); + bean.getOtherIds().add(null); + + bean.getUids().add(UUID.randomUUID()); + bean.getUids().add(null); + bean.getUids().add(UUID.randomUUID()); + + bean.setDoubs(doubles); + + bean.setStatuses(new ArrayList<>()); + bean.getStatuses().add(EArrayBean.Status.ONE); + bean.getStatuses().add(null); + bean.getStatuses().add(EArrayBean.Status.THREE); + + bean.getVcEnums().add(VarcharEnum.ONE); + bean.getVcEnums().add(null); + bean.getVcEnums().add(VarcharEnum.TWO); + + bean.getIntEnums().add(null); + bean.getIntEnums().add(IntEnum.ZERO); + bean.getIntEnums().add(null); + bean.getIntEnums().add(IntEnum.TWO); + + Ebean.save(bean); + + found = Ebean.find(EArrayBean.class, bean.getId()); + assertThat(found.getPhoneNumbers()).containsExactly("111222333", null, "333222111"); + assertThat(found.getOtherIds()).containsExactly(15L, null, 30L, null); + assertNull(found.getUids().get(1)); + assertThat(found.getDoubs()).containsExactly(1.3, null, 2.4); + assertThat(found.getStatuses()).containsExactly(EArrayBean.Status.ONE, null, EArrayBean.Status.THREE); + assertThat(found.getVcEnums()).containsExactly(VarcharEnum.ONE, null, VarcharEnum.TWO); + assertThat(found.getIntEnums()).containsExactly(null, IntEnum.ZERO, null, IntEnum.TWO); + Ebean.delete(bean); + } } From 13429daddc0d09dc50d14ad0d93a4afc49869e1d Mon Sep 17 00:00:00 2001 From: Ryszard Trojnacki Date: Tue, 10 Sep 2019 20:43:31 +0200 Subject: [PATCH 2/4] Test case for changing OneToMany collection by editing target bean. --- .../tests/cache/TestCacheCollectionIds.java | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/tests/cache/TestCacheCollectionIds.java b/src/test/java/org/tests/cache/TestCacheCollectionIds.java index bb09dd70e..a57908242 100644 --- a/src/test/java/org/tests/cache/TestCacheCollectionIds.java +++ b/src/test/java/org/tests/cache/TestCacheCollectionIds.java @@ -12,13 +12,7 @@ import io.ebeaninternal.server.cache.CachedManyIds; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import org.tests.model.basic.Contact; -import org.tests.model.basic.Country; -import org.tests.model.basic.Customer; -import org.tests.model.basic.OCachedBean; -import org.tests.model.basic.Order; -import org.tests.model.basic.OrderDetail; -import org.tests.model.basic.ResetBasicData; +import org.tests.model.basic.*; import java.util.ArrayList; import java.util.List; @@ -146,6 +140,46 @@ public class TestCacheCollectionIds extends BaseTestCase { assertTrue(cachedManyIds.getIdList().contains("AU")); } + @Test + public void testChangingCollectionByEditingConnectedBean() { + ResetBasicData.reset(); + + OCachedBean cachedBean = new OCachedBean(); + cachedBean.setName("hello"); + + Ebean.save(cachedBean); + awaitL2Cache(); + + OCachedBean dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(0, dummyToLoad.getChildren().size()); + + OCachedBeanChild child1=new OCachedBeanChild(); + child1.setCachedBean(cachedBean); + Ebean.insert(child1); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(1, dummyToLoad.getChildren().size()); + + OCachedBeanChild child2=new OCachedBeanChild(); + child2.setCachedBean(cachedBean); + Ebean.insert(child2); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(2, dummyToLoad.getChildren().size()); + + Ebean.delete(child2); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(1, dummyToLoad.getChildren().size()); + + Ebean.delete(child1); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(0, dummyToLoad.getChildren().size()); + + Ebean.delete(cachedBean); + } + /** * When updating a ManyToMany relations also the collection cache must be updated. From 1416647d04357f72bab4c1c331158f529fa69c58 Mon Sep 17 00:00:00 2001 From: Ryszard Trojnacki Date: Wed, 11 Sep 2019 14:05:31 +0200 Subject: [PATCH 3/4] Test case for changing OneToMany collection in a cached bean, by editing target bean that is not cached (annotated with @Cache). --- .../tests/cache/TestCacheCollectionIds.java | 44 ++++++++++++++++++- .../org/tests/model/basic/OBeanChild.java | 38 ++++++++++++++++ .../org/tests/model/basic/OCachedBean.java | 11 +++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/tests/model/basic/OBeanChild.java diff --git a/src/test/java/org/tests/cache/TestCacheCollectionIds.java b/src/test/java/org/tests/cache/TestCacheCollectionIds.java index a57908242..c57811889 100644 --- a/src/test/java/org/tests/cache/TestCacheCollectionIds.java +++ b/src/test/java/org/tests/cache/TestCacheCollectionIds.java @@ -141,11 +141,11 @@ public class TestCacheCollectionIds extends BaseTestCase { } @Test - public void testChangingCollectionByEditingConnectedBean() { + public void testChangingCollectionByEditingConnectedCachedBean() { ResetBasicData.reset(); OCachedBean cachedBean = new OCachedBean(); - cachedBean.setName("hello"); + cachedBean.setName("hello1"); Ebean.save(cachedBean); awaitL2Cache(); @@ -180,6 +180,46 @@ public class TestCacheCollectionIds extends BaseTestCase { Ebean.delete(cachedBean); } + @Test + public void testChangingCollectionByEditingConnectedNotCachedBean() { + ResetBasicData.reset(); + + OCachedBean cachedBean = new OCachedBean(); + cachedBean.setName("hello2"); + + Ebean.save(cachedBean); + awaitL2Cache(); + + OCachedBean dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(0, dummyToLoad.getNotCachedChildren().size()); + + OBeanChild child1=new OBeanChild(); + child1.setCachedBean(cachedBean); + Ebean.insert(child1); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(1, dummyToLoad.getNotCachedChildren().size()); + + OBeanChild child2=new OBeanChild(); + child2.setCachedBean(cachedBean); + Ebean.insert(child2); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(2, dummyToLoad.getNotCachedChildren().size()); + + Ebean.delete(child2); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(1, dummyToLoad.getNotCachedChildren().size()); + + Ebean.delete(child1); + awaitL2Cache(); + dummyToLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + assertEquals(0, dummyToLoad.getNotCachedChildren().size()); + + Ebean.delete(cachedBean); + } + /** * When updating a ManyToMany relations also the collection cache must be updated. diff --git a/src/test/java/org/tests/model/basic/OBeanChild.java b/src/test/java/org/tests/model/basic/OBeanChild.java new file mode 100644 index 000000000..a4d8d65ee --- /dev/null +++ b/src/test/java/org/tests/model/basic/OBeanChild.java @@ -0,0 +1,38 @@ +package org.tests.model.basic; + +import io.ebean.annotation.Cache; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * Not cached bean (copy of {@link OCachedBeanChild}} without {@link Cache} annotation + * for testing caching implementation for relations {@link Cache} and not {@link Cache}. + */ +@Entity +@Table(name = "o_bean_child") +public class OBeanChild { + @Id + Long id; + + @ManyToOne + OCachedBean cachedBean; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public OCachedBean getCachedBean() { + return cachedBean; + } + + public void setCachedBean(OCachedBean cachedBean) { + this.cachedBean = cachedBean; + } +} diff --git a/src/test/java/org/tests/model/basic/OCachedBean.java b/src/test/java/org/tests/model/basic/OCachedBean.java index 8e8d2a976..7879b5251 100644 --- a/src/test/java/org/tests/model/basic/OCachedBean.java +++ b/src/test/java/org/tests/model/basic/OCachedBean.java @@ -30,6 +30,9 @@ public class OCachedBean { @OneToMany(mappedBy = "cachedBean", cascade = CascadeType.ALL) List children = new ArrayList<>(); + @OneToMany(mappedBy = "cachedBean", cascade = CascadeType.ALL) + List notCachedChildren = new ArrayList<>(); + public Long getId() { return id; } @@ -61,4 +64,12 @@ public class OCachedBean { public void setChildren(List children) { this.children = children; } + + public List getNotCachedChildren() { + return notCachedChildren; + } + + public void setNotCachedChildren(List notCachedChildren) { + this.notCachedChildren = notCachedChildren; + } } From d2ece76ca8a1d19fe358928eaeed6d9d7ad8e6d2 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Thu, 12 Sep 2019 14:55:37 +1200 Subject: [PATCH 4/4] #1816 Fix for OneToMany relation to non-cached target bean. --- .../java/io/ebeaninternal/api/LoadManyRequest.java | 2 +- .../io/ebeaninternal/server/cache/CacheChangeSet.java | 2 +- .../ebeaninternal/server/core/DefaultBeanLoader.java | 2 +- .../server/deploy/BeanPropertyAssocMany.java | 10 +++++++++- .../server/loadcontext/DLoadManyContext.java | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/ebeaninternal/api/LoadManyRequest.java b/src/main/java/io/ebeaninternal/api/LoadManyRequest.java index 0e234536f..043f51e58 100644 --- a/src/main/java/io/ebeaninternal/api/LoadManyRequest.java +++ b/src/main/java/io/ebeaninternal/api/LoadManyRequest.java @@ -169,7 +169,7 @@ public class LoadManyRequest extends LoadRequest { Object parentId = desc.getId(ownerBean); logger.debug("BeanCollection after lazy load was empty. type:" + ownerBean.getClass().getName() + " id:" + parentId + " owner:" + ownerBean); } - } else if (isLoadCache()) { + } else if (isLoadCache() && many.isUseCache()) { Object parentId = desc.getId(bc.getOwnerBean()); desc.cacheManyPropPut(many, bc, parentId); } diff --git a/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java b/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java index b25ba9ce5..20cd7329c 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java +++ b/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java @@ -177,7 +177,7 @@ public class CacheChangeSet { final ManyKey key; - final List removes = new ArrayList<>(); + final Set removes = new HashSet<>(); final Map puts = new LinkedHashMap<>(); diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java index fc8d42027..faef445a6 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java @@ -82,7 +82,7 @@ class DefaultBeanLoader { parentDesc.contextPut(pc, parentId, parentBean); } - boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching(); + boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching() && many.isUseCache(); if (useManyIdCache) { Boolean readOnly = null; if (ebi.isReadOnly()) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 969777472..f884bb900 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -226,6 +226,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST desc.registerTable(targetDescriptor.getBaseTable(), this); } } + /** * Return the underlying collection of beans. */ @@ -345,7 +346,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST /** * Add the loaded current bean to its associated parent. - * + *

* Helper method used by Elastic integration when loading with a persistence context. */ @Override @@ -967,6 +968,13 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST elementDescriptor.jsonWriteElement(ctx, element); } + /** + * Only cache Many Ids if the target bean is also cached. + */ + public boolean isUseCache() { + return targetDescriptor.isBeanCaching(); + } + /** * A Many (element collection) property in bean cache to held as JSON. */ diff --git a/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java index a1cdb0fb6..d58bcdcf0 100644 --- a/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java +++ b/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java @@ -209,7 +209,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext { public void loadMany(BeanCollection bc, boolean onlyIds) { synchronized (this) { - boolean useCache = context.hitCache && !onlyIds; + boolean useCache = !onlyIds && context.hitCache && context.property.isUseCache(); if (useCache) { EntityBean ownerBean = bc.getOwnerBean(); BeanDescriptor parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass());