From f9c70d2a2b3c6aaa0b7f5fde2c268c174686090b Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Sun, 30 Apr 2017 13:07:35 +0200 Subject: [PATCH] Fix/select distinct cascaded fetch (#1015) Fix/select distinct cascaded fetch with findSingleAttributeList() on associated property path --- .../server/querydefn/DefaultOrmQuery.java | 2 +- .../server/querydefn/OrmQueryDetail.java | 20 +++++++++++++------ .../server/querydefn/OrmQueryDetailTest.java | 8 ++++---- .../query/other/TestQuerySingleAttribute.java | 16 +++++++++++++++ 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 30bf1468b..4e5d0f403 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -497,7 +497,7 @@ public class DefaultOrmQuery implements SpiQuery { * Limit the number of fetch joins to Many properties, mark as query joins as needed. */ private void markQueryJoins() { - detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch()); + detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type != Type.ATTRIBUTE); } private boolean isAllowOneManyFetch() { diff --git a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java index 24ef4814e..808cc0467 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -310,17 +310,21 @@ public class OrmQueryDetail implements Serializable { * Sort the fetch paths into depth order adding any missing parent paths if necessary. */ public void sortFetchPaths(BeanDescriptor d) { + sortFetchPaths(d, true); + } + + private void sortFetchPaths(BeanDescriptor d, boolean addIds) { if (!fetchPaths.isEmpty()) { LinkedHashMap sorted = new LinkedHashMap<>(); for (OrmQueryProperties p : fetchPaths.values()) { - sortFetchPaths(d, p, sorted); + sortFetchPaths(d, p, sorted, addIds); } fetchPaths = sorted; } } - private void sortFetchPaths(BeanDescriptor d, OrmQueryProperties p, LinkedHashMap sorted) { + private void sortFetchPaths(BeanDescriptor d, OrmQueryProperties p, LinkedHashMap sorted, boolean addId) { String path = p.getPath(); if (!sorted.containsKey(path)) { @@ -337,10 +341,14 @@ public class OrmQueryDetail implements Serializable { } // add a missing parent path just fetching the Id property BeanPropertyAssoc assocOne = (BeanPropertyAssoc) el.getBeanProperty(); - parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty()); + if (addId) { + parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty()); + } else { + parentProp = new OrmQueryProperties(parentPath, new LinkedHashSet<>()); + } } - sortFetchPaths(d, parentProp, sorted); + sortFetchPaths(d, parentProp, sorted, addId); sorted.put(path, p); } } @@ -349,7 +357,7 @@ public class OrmQueryDetail implements Serializable { /** * Mark 'fetch joins' to 'many' properties over to 'query joins' where needed. */ - void markQueryJoins(BeanDescriptor beanDescriptor, String lazyLoadManyPath, boolean allowOne) { + void markQueryJoins(BeanDescriptor beanDescriptor, String lazyLoadManyPath, boolean allowOne, boolean addIds) { // the name of the many fetch property if there is one String manyFetchProperty = null; @@ -357,7 +365,7 @@ public class OrmQueryDetail implements Serializable { // flag that is set once the many fetch property is chosen boolean fetchJoinFirstMany = allowOne; - sortFetchPaths(beanDescriptor); + sortFetchPaths(beanDescriptor, addIds); for (String fetchPath : fetchPaths.keySet()) { ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath); diff --git a/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryDetailTest.java b/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryDetailTest.java index ed9ed004f..5c3ef69b6 100644 --- a/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryDetailTest.java +++ b/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryDetailTest.java @@ -175,7 +175,7 @@ public class OrmQueryDetailTest extends BaseTestCase { OrmQueryDetail detail = new OrmQueryDetail(); detail.fetch("details", null, null); - detail.markQueryJoins(orderDesc(), null, true); + detail.markQueryJoins(orderDesc(), null, true, true); assertThat(detail.getChunk("details", false).isQueryFetch()).isFalse(); } @@ -186,7 +186,7 @@ public class OrmQueryDetailTest extends BaseTestCase { OrmQueryDetail detail = new OrmQueryDetail(); detail.fetch("details", null, null); - detail.markQueryJoins(orderDesc(), null, false); + detail.markQueryJoins(orderDesc(), null, false, true); assertThat(detail.getChunk("details", false).isQueryFetch()).isTrue(); } @@ -198,7 +198,7 @@ public class OrmQueryDetailTest extends BaseTestCase { detail.fetch("details", null, null); detail.fetch("customer.contacts", null, null); - detail.markQueryJoins(orderDesc(), null, true); + detail.markQueryJoins(orderDesc(), null, true, true); assertThat(detail.getChunk("details", false).isQueryFetch()).isFalse(); assertThat(detail.getChunk("customer.contacts", false).isQueryFetch()).isTrue(); @@ -211,7 +211,7 @@ public class OrmQueryDetailTest extends BaseTestCase { detail.fetch("details", null, null); detail.fetch("customer.contacts", null, null); - detail.markQueryJoins(orderDesc(), null, false); + detail.markQueryJoins(orderDesc(), null, false, true); assertThat(detail.getChunk("details", false).isQueryFetch()).isTrue(); assertThat(detail.getChunk("customer.contacts", false).isQueryFetch()).isTrue(); diff --git a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java index 7d2a72364..2fe88f48b 100644 --- a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java +++ b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java @@ -3,6 +3,8 @@ package org.tests.query.other; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; + +import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; import org.avaje.test.model.rawsql.inherit.ChildA; @@ -162,6 +164,20 @@ public class TestQuerySingleAttribute extends BaseTestCase { assertThat(cities).contains("Auckland").containsNull(); } + @Test + public void distinctWithCascadedFetch() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Contact.class) + .setDistinct(true) + .fetch("customer.billingAddress","city"); + + List cities = query.findSingleAttributeList(); + + assertThat(sqlOf(query)).contains("select distinct t2.city from contact t0 join o_customer t1 on t1.id = t0.customer_id left join o_address t2 on t2.id = t1.billing_address_id"); + assertThat(cities).contains("Auckland").containsNull(); + } @Test public void distinctSelectOnInheritedBean() {