diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 2390c9de1..648cfb296 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -30,7 +30,7 @@ import java.util.Set; /** * Factory for SqlTree. */ -public class SqlTreeBuilder { +public final class SqlTreeBuilder { private static final Logger logger = LoggerFactory.getLogger(SqlTreeBuilder.class); @@ -323,7 +323,7 @@ public class SqlTreeBuilder { } else { // do not read Id on child beans (e.g. when used with fetch()) - boolean withId = (query == null || !query.isSingleAttribute()); + boolean withId = isNotSingleAttribute(); return new SqlTreeNodeBean(prefix, prop, props, myList, withId, temporalMode, disableLazyLoad); } } @@ -440,7 +440,7 @@ public class SqlTreeBuilder { p = desc.findBeanProperty("id"); selectProps.add(p); - } else if (p.isId() && (query == null || !query.isSingleAttribute())) { + } else if (p.isId() && isNotSingleAttribute()) { // do not bother to include id for normal queries as the // id is always added (except for subQueries) @@ -716,4 +716,11 @@ public class SqlTreeBuilder { } } + + /** + * Return true if the query is not a single attribute query. + */ + private boolean isNotSingleAttribute() { + return query == null || !query.isSingleAttribute(); + } } diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index fe9e99fed..4ca5e0d29 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -513,7 +513,12 @@ public class DefaultOrmQuery implements SpiQuery { @Override public void setDefaultSelectClause() { - detail.setDefaultSelectClause(beanDescriptor); + if (type != Type.ATTRIBUTE) { + detail.setDefaultSelectClause(beanDescriptor); + } else if (!detail.hasSelectClause()) { + // explicit empty select when single attribute query on non-root fetch path + detail.setEmptyBase(); + } } @Override @@ -1221,10 +1226,6 @@ public class DefaultOrmQuery implements SpiQuery { @Override @SuppressWarnings("unchecked") public List findSingleAttributeList() { - if (!detail.hasSelectClause()) { - // (no explicit select set - clear all properties) - detail.setBase(new OrmQueryProperties(null, new LinkedHashSet<>())); - } return (List) server.findSingleAttributeList(this, null); } diff --git a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java index b77e81689..24ef4814e 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -174,6 +174,13 @@ public class OrmQueryDetail implements Serializable { return baseProps.isIncluded(property); } + /** + * Set the base query properties to be empty. + */ + public void setEmptyBase() { + this.baseProps = new OrmQueryProperties(null, new LinkedHashSet<>()); + } + /** * Set the base / root query properties. */ diff --git a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java index b4e1af080..7d2a72364 100644 --- a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java +++ b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java @@ -129,7 +129,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { query2.findList(); assertThat(sqlOf(query2, 1)).contains("select t0.id, t0.name from o_customer t0"); } - + @Test public void distinctOnIdProperty(){ Query query = Ebean.find(Customer.class) @@ -250,12 +250,24 @@ public class TestQuerySingleAttribute extends BaseTestCase { query.findSingleAttributeList(); assertThat(sqlOf(query)).contains("select t1.more from rawinherit_uncle t0 join rawinherit_parent t1 on t1.id = t0.parent_id and t1.type in ('A','B')"); + } + @Test + public void findSingleFetchManyToOneInheritedBean_viaEbeanServer() { + + ResetBasicData.reset(); + + Query query = Ebean.find(EUncle.class) + .fetch("parent","more") + .setMaxRows(100); + + Ebean.getDefaultServer().findSingleAttributeList(query, null); + + assertThat(sqlOf(query)).contains("select t1.more from rawinherit_uncle t0 join rawinherit_parent t1 on t1.id = t0.parent_id and t1.type in ('A','B')"); } @Test @Ignore //don't know if ebean can handle this on many to many, as this means that the cartesian product is generated - public void distinctFetchManyToManyInheritedBean() { ResetBasicData.reset(); @@ -267,7 +279,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { query.findSingleAttributeList(); - assertThat(sqlOf(query)).contains("select distinct t0.more from rawinherit_data t0 " + assertThat(sqlOf(query)).contains("select distinct t0.more from rawinherit_data t0 " + "join rawinherit_parent_rawinherit_data t1 on t0.id = t1.rawinherit_data_id " + "join parent t2 on t1.rawinherit_parent_id = t2.id"); }