#2730 - Secondary @OneToMany fetch query is not happening if the same Entity type is fetched

This commit is contained in:
Rob Bygrave
2022-07-06 20:19:41 +12:00
parent 43dac07d5b
commit 88b6a60feb
7 changed files with 280 additions and 16 deletions
@@ -6,7 +6,6 @@ import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebean.core.type.DataReader;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.query.STreePropertyAssocMany;
import java.util.Map;
@@ -70,11 +69,6 @@ public interface DbReadContext {
*/
void register(BeanPropertyAssocMany<?> many, BeanCollection<?> bc);
/**
* Return the property that is associated with the many. There can only be
* one. This can be null.
*/
STreePropertyAssocMany getManyProperty();
/**
* Set back the bean that has just been loaded with its id.
@@ -605,14 +605,6 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
return logWhereSql;
}
/**
* Return the property that is associated with the many. There can only be one
* per SqlSelect. This can be null.
*/
@Override
public STreePropertyAssocMany getManyProperty() {
return manyProperty;
}
public String getBindLog() {
return bindLog;
@@ -36,6 +36,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
private final SpiQuery.TemporalMode temporalMode;
private final boolean temporalVersions;
final IdBinder lazyLoadParentIdBinder;
private final STreePropertyAssocMany loadingChildProperty;
SqlTreeLoadBean(SqlTreeNodeBean node) {
this.lazyLoadParent = node.lazyLoadParent;
@@ -55,6 +56,16 @@ class SqlTreeLoadBean implements SqlTreeLoad {
this.properties = node.properties;
this.pathMap = node.pathMap;
this.children = node.createLoadChildren();
this.loadingChildProperty = loadingChildProperty();
}
private STreePropertyAssocMany loadingChildProperty() {
for (SqlTreeLoad child : children) {
if (child instanceof SqlTreeLoadManyRoot) {
return ((SqlTreeLoadManyRoot) child).manyProp();
}
}
return null;
}
boolean isRoot() {
@@ -280,10 +291,9 @@ class SqlTreeLoadBean implements SqlTreeLoad {
* included in the actual query.
*/
private void createListProxies() {
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != fetchedMany) {
if (many != loadingChildProperty) {
if (readOnlyNoIntercept) {
many.createEmptyReference(localBean);
} else {
@@ -14,6 +14,10 @@ final class SqlTreeLoadManyRoot extends SqlTreeLoadBean {
this.manyProp = node.manyProp;
}
STreePropertyAssocMany manyProp() {
return manyProp;
}
@Override
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
// pass in null for parentBean because added to a collection rather than set to the parentBean
@@ -0,0 +1,51 @@
package org.tests.o2m.recurse;
import javax.persistence.*;
import java.util.List;
@Entity
public class RMItem {
@Id
private long itemId;
@ManyToOne
@JoinColumn(name = "item_group_id")
private RMItem itemGroup;
private String name;
@OneToMany(mappedBy = "itemGroup")
private List<RMItem> subItems;
public RMItem() {
}
public RMItem(String name) {
this.name = name;
}
public Long getItemId() {
return itemId;
}
public void setItemId(Long itemId) {
this.itemId = itemId;
}
public RMItem getItemGroup() {
return itemGroup;
}
public void setItemGroup(RMItem itemGroup) {
this.itemGroup = itemGroup;
}
public List<RMItem> getSubItems() {
return subItems;
}
public void setSubItems(List<RMItem> subItems) {
this.subItems = subItems;
}
}
@@ -0,0 +1,80 @@
package org.tests.o2m.recurse;
import io.ebean.Model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
public class RMItemHolder extends Model {
@Id
long id;
String name;
String notes;
@ManyToOne
//@JoinColumn(name = "item_a_id")
private RMItem itemA;
@ManyToOne
//@JoinColumn(name = "item_b_id")
private RMItem itemB;
@Version
long version;
public RMItemHolder(String name) {
this.name = name;
}
public RMItemHolder() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public RMItem getItemA() {
return itemA;
}
public void setItemA(RMItem itemA) {
this.itemA = itemA;
}
public RMItem getItemB() {
return itemB;
}
public void setItemB(RMItem itemB) {
this.itemB = itemB;
}
}
@@ -0,0 +1,133 @@
package org.tests.o2m.recurse;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.FetchConfig;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class TestFetchOneToManySameTypeTwoPaths {
@Test
void testSubItemListFetch_ofQuery() {
Database server = DB.getDefault();
RMItem itemA = new RMItem("a");
server.save(itemA);
RMItem itemB = new RMItem("b");
server.save(itemB);
for (int i=0; i<2; i++) {
RMItem subItem = new RMItem();
subItem.setItemGroup(itemA);
server.save(subItem);
}
for (int i=0; i<3; i++) {
RMItem subItem = new RMItem();
subItem.setItemGroup(itemB);
server.save(subItem);
}
RMItemHolder customer = new RMItemHolder();
customer.setItemA(itemA);
customer.setItemB(itemB);
server.save(customer);
// This is OK
{
RMItemHolder requestedCustomer = server.find(RMItemHolder.class)
.setDisableLazyLoading(true)
.fetch("itemA.subItems", FetchConfig.ofQuery())
.fetch("itemB.subItems", FetchConfig.ofQuery())
.where()
.eq("id", customer.getId())
.findOne();
assertEquals(2, requestedCustomer.getItemA().getSubItems().size());
assertEquals(3, requestedCustomer.getItemB().getSubItems().size());
}
}
@Test
void testSubItemListFetch_itemAFirst() {
Database server = DB.getDefault();
RMItem itemA = new RMItem("aa");
server.save(itemA);
RMItem itemB = new RMItem("bb");
server.save(itemB);
for (int i=0; i<2; i++) {
RMItem subItem = new RMItem();
subItem.setItemGroup(itemA);
server.save(subItem);
}
for (int i=0; i<3; i++) {
RMItem subItem = new RMItem();
subItem.setItemGroup(itemB);
server.save(subItem);
}
RMItemHolder customer = new RMItemHolder();
customer.setItemA(itemA);
customer.setItemB(itemB);
server.save(customer);
// This fails because requestedCustomer.getItemB().getSubItems() is not loaded
{
RMItemHolder requestedCustomer = server.find(RMItemHolder.class)
.setDisableLazyLoading(true)
.fetch("itemA.subItems")
.fetch("itemB.subItems")
.where()
.eq("id", customer.getId())
.findOne();
assertEquals(2, requestedCustomer.getItemA().getSubItems().size());
assertEquals(3, requestedCustomer.getItemB().getSubItems().size());
}
}
@Test
void testSubItemListFetch_itemBFirst() {
Database server = DB.getDefault();
RMItem itemA = new RMItem("a");
server.save(itemA);
RMItem itemB = new RMItem("b");
server.save(itemB);
for (int i=0; i<2; i++) {
RMItem subItem = new RMItem();
subItem.setItemGroup(itemA);
server.save(subItem);
}
for (int i=0; i<5; i++) {
RMItem subItem = new RMItem();
subItem.setItemGroup(itemB);
server.save(subItem);
}
RMItemHolder customer = new RMItemHolder();
customer.setItemA(itemA);
customer.setItemB(itemB);
server.save(customer);
// This fails because requestedCustomer.getItemA().getSubItems() is not loaded
{
RMItemHolder requestedCustomer = server.find(RMItemHolder.class)
.setDisableLazyLoading(true)
.fetch("itemB.subItems")
.fetch("itemA.subItems")
.where()
.eq("id", customer.getId())
.findOne();
assertEquals(2, requestedCustomer.getItemA().getSubItems().size());
assertEquals(5, requestedCustomer.getItemB().getSubItems().size());
System.out.println("here");
}
}
}