From b111fcfeccd35f383280d3ccf55140be13e6281b Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 26 Jul 2019 12:13:57 +0200 Subject: [PATCH 1/2] refresh does not work on oneToMany if property was not yet accessed (cherry picked from commit 446103e6e06d7583f8b2bb847907d7fc77a8be9f) --- .../java/io/ebean/EbeanServer_refresh.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/test/java/io/ebean/EbeanServer_refresh.java b/src/test/java/io/ebean/EbeanServer_refresh.java index 0c3e676a8..f9243082d 100644 --- a/src/test/java/io/ebean/EbeanServer_refresh.java +++ b/src/test/java/io/ebean/EbeanServer_refresh.java @@ -3,12 +3,14 @@ package io.ebean; import org.junit.Test; import org.tests.model.basic.EBasic; import org.tests.model.basic.Order; +import org.tests.model.basic.OrderDetail; import org.tests.model.basic.ResetBasicData; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; public class EbeanServer_refresh { @@ -77,4 +79,77 @@ public class EbeanServer_refresh { Ebean.refresh(order); } + + + @Test + public void refresh_on_details_new() { + + ResetBasicData.reset(); + + Order order = Ebean.find(Order.class, 1); + + DB.refresh(order); // call refresh BEFORE first access on "getDetail"; + + assertThat(order.getDetails()).hasSize(3); + + OrderDetail detail = new OrderDetail(); + detail.setOrder(order); + DB.save(detail); + + try { + + assertThat(order.getDetails()).hasSize(3); + + DB.refresh(order); + + assertThat(order.getDetails()).hasSize(4); + + } finally { + DB.delete(detail); // restore old state + } + + DB.refresh(order); + + assertThat(order.getDetails()).hasSize(3); + } + + + + @Test + public void refresh_on_details_changed() { + + ResetBasicData.reset(); + + Order order = Ebean.find(Order.class, 1); + + DB.refresh(order); // call refresh BEFORE first access on "getDetail" + // this changes the loader of the details-bean collection from DefaultServer to DLoadManyContext$LoadBuffer + // if this refresh is commented out, the test will pass + + assertThat(order.getDetails().get(0).getOrderQty()).isEqualTo(5); + + // search the detail in the DB and change qty to 42 + OrderDetail detail = DB.find(OrderDetail.class, order.getDetails().get(0).getId()); + assertThat(order.getDetails().get(0)).isEqualTo(detail).isNotSameAs(detail); + detail.setOrderQty(42); + DB.save(detail); + + try { + assertThat(order.getDetails().get(0).getOrderQty()).isEqualTo(5); + + DB.refresh(order); + + assertThat(order.getDetails().get(0).getOrderQty()).isEqualTo(42); + + } finally { + // restore old value + detail.setOrderQty(5); + DB.save(detail); + } + + DB.refresh(order); + + assertThat(order.getDetails().get(0).getOrderQty()).isEqualTo(5); + + } } From a780dc133ec1f45c728712abeb4788b044f671b5 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Mon, 29 Jul 2019 22:46:39 +1200 Subject: [PATCH 2/2] #1776 Fix for - refresh does not work on oneToMany if property was not yet accessed --- .../server/deploy/BeanPropertyAssocMany.java | 7 ++- .../server/query/STreePropertyAssocMany.java | 5 ++ .../server/query/SqlTreeNodeBean.java | 52 +++++++++---------- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 3fbfb49f7..969777472 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -613,8 +613,12 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST } @Override - public BeanCollection createReferenceIfNull(EntityBean parentBean) { + public BeanCollection createReference(EntityBean localBean, boolean forceNewReference) { + return forceNewReference ? createReference(localBean) : createReferenceIfNull(localBean); + } + @Override + public BeanCollection createReferenceIfNull(EntityBean parentBean) { Object v = getValue(parentBean); if (v instanceof BeanCollection) { BeanCollection bc = (BeanCollection) v; @@ -627,7 +631,6 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST } public BeanCollection createReference(EntityBean parentBean) { - BeanCollection ref = help.createReference(parentBean); setValue(parentBean, ref); return ref; diff --git a/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java index 9b6791e2f..49d4fb313 100644 --- a/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java @@ -22,6 +22,11 @@ public interface STreePropertyAssocMany extends STreePropertyAssoc { */ BeanCollection createReferenceIfNull(EntityBean localBean); + /** + * Return a reference collection forcing a new reference on REFRESH query. + */ + BeanCollection createReference(EntityBean localBean, boolean forceNewReference); + /** * Return true if the property has a join table. */ diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java index f03d60637..fa60e1485 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -376,7 +376,7 @@ class SqlTreeNodeBean implements SqlTreeNode { if (!lazyLoadMany && localBean != null) { ctx.setCurrentPrefix(prefix, pathMap); if (readId && !temporalVersions) { - createListProxies(localDesc, ctx, localBean, disableLazyLoad); + createListProxies(); } if (temporalMode == SpiQuery.TemporalMode.DRAFT) { localDesc.setDraft(localBean); @@ -413,6 +413,30 @@ class SqlTreeNodeBean implements SqlTreeNode { } } + /** + * Create lazy loading proxies for the Many's except for the one that is + * included in the actual query. + */ + private void createListProxies() { + STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); + boolean forceNewReference = queryMode == Mode.REFRESH_BEAN; + // load the List/Set/Map proxy objects (deferred fetching of lists) + for (STreePropertyAssocMany many : localDesc.propsMany()) { + if (many != fetchedMany) { + // create a proxy for the many (deferred fetching) + BeanCollection ref = many.createReference(localBean, forceNewReference); + if (ref != null) { + if (disableLazyLoad) { + ref.setDisableLazyLoad(true); + } + if (!ref.isRegisteredWithLoadContext()) { + ctx.register(many.getName(), ref); + } + } + } + } + } + void setBeanToParent() { if (parentBean != null) { // set this back to the parentBean @@ -463,32 +487,6 @@ class SqlTreeNodeBean implements SqlTreeNode { return load.complete(); } - /** - * Create lazy loading proxies for the Many's except for the one that is - * included in the actual query. - */ - private void createListProxies(STreeType localDesc, DbReadContext ctx, EntityBean localBean, boolean disableLazyLoad) { - - STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); - - // load the List/Set/Map proxy objects (deferred fetching of lists) - for (STreePropertyAssocMany many : localDesc.propsMany()) { - - if (fetchedMany == null || !fetchedMany.equals(many)) { - // create a proxy for the many (deferred fetching) - BeanCollection ref = many.createReferenceIfNull(localBean); - if (ref != null) { - if (disableLazyLoad) { - ref.setDisableLazyLoad(true); - } - if (!ref.isRegisteredWithLoadContext()) { - ctx.register(many.getName(), ref); - } - } - } - } - } - @Override public void appendGroupBy(DbSqlContext ctx, boolean subQuery) {