diff --git a/src/main/java/io/ebeaninternal/api/LoadBeanContext.java b/src/main/java/io/ebeaninternal/api/LoadBeanContext.java index f8329a776..901c54d04 100644 --- a/src/main/java/io/ebeaninternal/api/LoadBeanContext.java +++ b/src/main/java/io/ebeaninternal/api/LoadBeanContext.java @@ -2,6 +2,7 @@ package io.ebeaninternal.api; import io.ebean.bean.BeanCollection; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; /** * Controls the loading of ManyToOne and OneToOne relationships. @@ -11,5 +12,5 @@ public interface LoadBeanContext extends LoadSecondaryQuery { /** * Register a BeanCollection into the load context. */ - void register(String manyProperty, BeanCollection collection); + void register(BeanPropertyAssocMany many, BeanCollection collection); } diff --git a/src/main/java/io/ebeaninternal/api/LoadContext.java b/src/main/java/io/ebeaninternal/api/LoadContext.java index 47d03f25f..4ba56604f 100644 --- a/src/main/java/io/ebeaninternal/api/LoadContext.java +++ b/src/main/java/io/ebeaninternal/api/LoadContext.java @@ -5,6 +5,7 @@ import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.ObjectGraphNode; import io.ebean.bean.PersistenceContext; import io.ebeaninternal.server.core.OrmQueryRequest; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.BeanPropertyAssocOne; /** @@ -56,6 +57,5 @@ public interface LoadContext { /** * Register a collection for lazy loading. */ - void register(String path, BeanCollection bc); - + void register(String path, BeanPropertyAssocMany many, BeanCollection bc); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index d6b1e4dc3..3039c3af0 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2269,7 +2269,7 @@ public class BeanDescriptor implements BeanType, STreeType { BeanCollection ref = many.createReferenceIfNull(bean); if (ref != null && !ref.isRegisteredWithLoadContext()) { String path = SplitName.add(prefix, many.getName()); - loadContext.register(path, ref); + loadContext.register(path, many, ref); } } } @@ -2311,7 +2311,7 @@ public class BeanDescriptor implements BeanType, STreeType { final BeanCollection collection = manyProp.createReference(ebi.getOwner()); ebi.setLoadedLazy(); if (loadBeanContext != null) { - loadBeanContext.register(manyProp.getName(), collection); + loadBeanContext.register(manyProp, collection); } return true; } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 39c043922..647055737 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -213,6 +213,11 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST } } + @Override + public BeanPropertyAssocMany asMany() { + return this; + } + @Override public boolean isManyToManyWithHistory() { return manyToMany && !excludedFromHistory && descriptor.isHistorySupport(); diff --git a/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java b/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java index 27a23808b..f5cf9fa06 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java +++ b/src/main/java/io/ebeaninternal/server/deploy/DbReadContext.java @@ -68,7 +68,7 @@ public interface DbReadContext { /** * Register a collection for lazy loading. */ - void register(String path, BeanCollection bc); + void register(BeanPropertyAssocMany many, BeanCollection bc); /** * Return the property that is associated with the many. There can only be diff --git a/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java index b1aa69f2a..e2c103bbb 100644 --- a/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -11,6 +11,7 @@ import io.ebeaninternal.api.LoadBeanRequest; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.querydefn.OrmQueryProperties; import java.util.ArrayList; @@ -37,9 +38,9 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext { } @Override - public void register(String manyProperty, BeanCollection collection) { - String path = fullPath + "." + manyProperty; - parent.register(path, collection); + public void register(BeanPropertyAssocMany many, BeanCollection collection) { + String path = fullPath + "." + many.getName(); + parent.register(path, many, collection); } /** diff --git a/src/main/java/io/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/io/ebeaninternal/server/loadcontext/DLoadContext.java index d06ada917..df2a9e8e8 100644 --- a/src/main/java/io/ebeaninternal/server/loadcontext/DLoadContext.java +++ b/src/main/java/io/ebeaninternal/server/loadcontext/DLoadContext.java @@ -283,8 +283,8 @@ public class DLoadContext implements LoadContext { } @Override - public void register(String path, BeanCollection bc) { - getManyContext(path).register(bc); + public void register(String path, BeanPropertyAssocMany many, BeanCollection bc) { + getManyContext(path, many).register(bc); } DLoadBeanContext getBeanContext(String path) { @@ -315,11 +315,12 @@ public class DLoadContext implements LoadContext { } } - DLoadManyContext getManyContext(String path) { - if (path == null) { - throw new RuntimeException("path is null?"); - } - return manyMap.computeIfAbsent(path, p -> createManyContext(p, defaultBatchSize, null)); + DLoadManyContext getManyContext(String path, BeanPropertyAssocMany many) { + return manyMap.computeIfAbsent(path, p -> createManyContext(p, many, defaultBatchSize)); + } + + private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany many, int batchSize) { + return new DLoadManyContext(this, many, path, batchSize, null); } private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) { diff --git a/src/main/java/io/ebeaninternal/server/query/CQuery.java b/src/main/java/io/ebeaninternal/server/query/CQuery.java index 0a62a2aa2..379ca4396 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -636,10 +636,9 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran } @Override - public void register(String path, BeanCollection bc) { - - path = getPath(path); - request.getGraphContext().register(path, bc); + public void register(BeanPropertyAssocMany many, BeanCollection bc) { + String path = getPath(many.getName()); + request.getGraphContext().register(path, many, bc); } /** diff --git a/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java index 49d4fb313..63e2b81c0 100644 --- a/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/query/STreePropertyAssocMany.java @@ -2,11 +2,17 @@ package io.ebeaninternal.server.query; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.TableJoin; public interface STreePropertyAssocMany extends STreePropertyAssoc { + /** + * Return as BeanPropertyAssocMany. + */ + BeanPropertyAssocMany asMany(); + /** * Append exported columns to the select. */ diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java index e0c87512f..d08837e1b 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -419,7 +419,7 @@ class SqlTreeNodeBean implements SqlTreeNode { ref.setDisableLazyLoad(true); } if (!ref.isRegisteredWithLoadContext()) { - ctx.register(many.getName(), ref); + ctx.register(many.asMany(), ref); } } } diff --git a/src/test/java/io/ebeaninternal/server/loadcontext/DLoadContextTest.java b/src/test/java/io/ebeaninternal/server/loadcontext/DLoadContextTest.java index bad3b37a2..9a79eb827 100644 --- a/src/test/java/io/ebeaninternal/server/loadcontext/DLoadContextTest.java +++ b/src/test/java/io/ebeaninternal/server/loadcontext/DLoadContextTest.java @@ -5,6 +5,7 @@ import io.ebean.FetchConfig; import io.ebean.Query; import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.core.OrmQueryRequestTestHelper; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import org.tests.model.basic.Order; import org.junit.Test; @@ -94,13 +95,14 @@ public class DLoadContextTest extends BaseTestCase { @Test public void construct_when_fetch_expect_100_100_batchSize() { + BeanPropertyAssocMany many = (BeanPropertyAssocMany)getBeanDescriptor(Order.class).getBeanProperty("details"); // the fetch is converted to a query join due to the maxRows OrmQueryRequest queryRequest = queryRequest(query().fetch("details").setMaxRows(100)); queryRequest.initTransIfRequired(); queryRequest.endTransIfRequired(); DLoadContext graphContext = (DLoadContext) queryRequest.getGraphContext(); - DLoadManyContext details = graphContext.getManyContext("details"); + DLoadManyContext details = graphContext.getManyContext("details", many); assertThat(details.firstBatchSize).isEqualTo(100); assertThat(details.secondaryBatchSize).isEqualTo(100); diff --git a/src/test/java/org/tests/o2m/lazy/OmlBar.java b/src/test/java/org/tests/o2m/lazy/OmlBar.java new file mode 100644 index 000000000..6e35c1e17 --- /dev/null +++ b/src/test/java/org/tests/o2m/lazy/OmlBar.java @@ -0,0 +1,37 @@ +package org.tests.o2m.lazy; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "oml_bar") +public class OmlBar { + + @Id + private Long id; + + @OneToMany(mappedBy = "bar", cascade = CascadeType.ALL) + private List fooList = new ArrayList(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getFooList() { + return fooList; + } + + public void setFooList(List fooList) { + this.fooList = fooList; + } + +} diff --git a/src/test/java/org/tests/o2m/lazy/OmlBaz.java b/src/test/java/org/tests/o2m/lazy/OmlBaz.java new file mode 100644 index 000000000..7d2e40a42 --- /dev/null +++ b/src/test/java/org/tests/o2m/lazy/OmlBaz.java @@ -0,0 +1,33 @@ +package org.tests.o2m.lazy; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "oml_baz") +public class OmlBaz { + @Id + private Long id; + + @ManyToOne(optional = false) + private OmlFoo foo; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public OmlFoo getFoo() { + return foo; + } + + public void setFoo(OmlFoo foo) { + this.foo = foo; + } + +} diff --git a/src/test/java/org/tests/o2m/lazy/OmlFoo.java b/src/test/java/org/tests/o2m/lazy/OmlFoo.java new file mode 100644 index 000000000..52d428d07 --- /dev/null +++ b/src/test/java/org/tests/o2m/lazy/OmlFoo.java @@ -0,0 +1,49 @@ +package org.tests.o2m.lazy; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "oml_foo") +public class OmlFoo { + + @Id + private Long id; + + @ManyToOne(optional = false) + private OmlBar bar; + + @OneToMany(mappedBy = "foo", cascade = CascadeType.ALL) + private List bazList = new ArrayList(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public OmlBar getBar() { + return bar; + } + + public void setBar(OmlBar bar) { + this.bar = bar; + } + + public List getBazList() { + return bazList; + } + + public void setBazList(List bazList) { + this.bazList = bazList; + } + +} diff --git a/src/test/java/org/tests/o2m/lazy/TestLazyManyViaPath.java b/src/test/java/org/tests/o2m/lazy/TestLazyManyViaPath.java new file mode 100644 index 000000000..8eee5d3e8 --- /dev/null +++ b/src/test/java/org/tests/o2m/lazy/TestLazyManyViaPath.java @@ -0,0 +1,56 @@ +package org.tests.o2m.lazy; + +import io.ebean.BaseTestCase; +import io.ebean.DB; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestLazyManyViaPath extends BaseTestCase { + + @Test + public void test() { + + OmlBar bar = new OmlBar(); + List fooList = new ArrayList<>(); + fooList.add(createNewFooWithBar(bar)); + + bar.setFooList(fooList); + + DB.save(bar); + + OmlFoo fooFromDb = DB + .find(OmlFoo.class) + .where() + .eq("id", bar.getFooList().get(0).getId()) + .findOne(); + + // This works + List foosList = fooFromDb.getBar().getFooList(); + assertThat(fooList.size()).isEqualTo(1); + + OmlBaz bazFromDb = DB + .find(OmlBaz.class) + .where() + .eq("id", bar.getFooList().get(0).getBazList().get(0).getId()) + .findOne(); + + // This does not work and gives the exception + List foosList1 = bazFromDb.getFoo().getBar().getFooList(); + assertThat(foosList1.size()).isEqualTo(1); + + } + + private static OmlFoo createNewFooWithBar(OmlBar bar) { + OmlFoo foo = new OmlFoo(); + foo.setBar(bar); + OmlBaz baz = new OmlBaz(); + baz.setFoo(foo); + foo.getBazList().add(baz); + return foo; + } + +}