diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractExpression.java index a3e8bba53..3909877d6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractExpression.java @@ -1,15 +1,15 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebean.util.SplitName; import io.ebeaninternal.api.ManyWhereJoins; +import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiExpressionValidation; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.el.ElPropertyDeploy; import io.ebeaninternal.server.el.ElPropertyValue; -import io.ebean.util.SplitName; -import io.ebeaninternal.api.NaturalKeyQueryData; /** * Base class for simple expressions. @@ -78,7 +78,7 @@ abstract class AbstractExpression implements SpiExpression { if (elProp != null) { if (elProp.containsFormulaWithJoin()) { // for findCount query select clause - manyWhereJoin.addFormulaWithJoin(elProp.getElPrefix(), elProp.getName()); + manyWhereJoin.addFormulaWithJoin(elProp.elPrefix(), elProp.name()); } if (elProp.containsMany()) { // for findCount we join to a many property diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 7dbecc60b..cb833253d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1,7 +1,30 @@ package io.ebeaninternal.server.querydefn; -import io.ebean.*; +import io.ebean.CacheMode; +import io.ebean.CountDistinctOrder; +import io.ebean.Database; +import io.ebean.DtoQuery; +import io.ebean.Expression; +import io.ebean.ExpressionFactory; +import io.ebean.ExpressionList; +import io.ebean.FetchConfig; +import io.ebean.FetchGroup; +import io.ebean.FetchPath; +import io.ebean.FutureIds; +import io.ebean.FutureList; +import io.ebean.FutureRowCount; +import io.ebean.OrderBy; import io.ebean.OrderBy.Property; +import io.ebean.PagedList; +import io.ebean.PersistenceContextScope; +import io.ebean.ProfileLocation; +import io.ebean.Query; +import io.ebean.QueryIterator; +import io.ebean.QueryType; +import io.ebean.RawSql; +import io.ebean.Transaction; +import io.ebean.UpdateQuery; +import io.ebean.Version; import io.ebean.bean.CallOrigin; import io.ebean.bean.ObjectGraphNode; import io.ebean.bean.ObjectGraphOrigin; @@ -9,10 +32,30 @@ import io.ebean.bean.PersistenceContext; import io.ebean.event.BeanQueryRequest; import io.ebean.event.readaudit.ReadEvent; import io.ebean.plugin.BeanType; -import io.ebeaninternal.api.*; +import io.ebeaninternal.api.BindParams; +import io.ebeaninternal.api.BindValuesKey; +import io.ebeaninternal.api.CQueryPlanKey; +import io.ebeaninternal.api.CacheIdLookup; +import io.ebeaninternal.api.CacheIdLookupMany; +import io.ebeaninternal.api.CacheIdLookupSingle; +import io.ebeaninternal.api.HashQuery; +import io.ebeaninternal.api.ManyWhereJoins; +import io.ebeaninternal.api.NaturalKeyQueryData; +import io.ebeaninternal.api.SpiEbeanServer; +import io.ebeaninternal.api.SpiExpression; +import io.ebeaninternal.api.SpiExpressionList; +import io.ebeaninternal.api.SpiExpressionValidation; +import io.ebeaninternal.api.SpiNamedParam; +import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuerySecondary; +import io.ebeaninternal.api.SpiTransaction; import io.ebeaninternal.server.autotune.ProfilingListener; import io.ebeaninternal.server.core.SpiOrmQueryRequest; -import io.ebeaninternal.server.deploy.*; +import io.ebeaninternal.server.deploy.BeanDescriptor; +import io.ebeaninternal.server.deploy.BeanNaturalKey; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import io.ebeaninternal.server.deploy.InheritInfo; +import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.el.ElPropertyDeploy; import io.ebeaninternal.server.expression.DefaultExpressionList; import io.ebeaninternal.server.expression.IdInExpression; @@ -24,7 +67,13 @@ import io.ebeaninternal.server.transaction.ExternalJdbcTransaction; import javax.persistence.PersistenceException; import java.sql.Connection; import java.sql.Timestamp; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -416,7 +465,7 @@ public final class DefaultOrmQuery extends AbstractQuery implements SpiQuery< for (Property orderProperty : orderBy.getProperties()) { ElPropertyDeploy elProp = beanDescriptor.elPropertyDeploy(orderProperty.getProperty()); if (elProp != null && elProp.containsFormulaWithJoin()) { - manyWhereJoins.addFormulaWithJoin(elProp.getElPrefix(), elProp.getName()); + manyWhereJoins.addFormulaWithJoin(elProp.elPrefix(), elProp.name()); } } } diff --git a/ebean-test/src/test/java/org/tests/query/joins/TestQueryJoinOnFormula.java b/ebean-test/src/test/java/org/tests/query/joins/TestQueryJoinOnFormula.java index 079b41d0f..513c8ec48 100644 --- a/ebean-test/src/test/java/org/tests/query/joins/TestQueryJoinOnFormula.java +++ b/ebean-test/src/test/java/org/tests/query/joins/TestQueryJoinOnFormula.java @@ -2,10 +2,12 @@ package org.tests.query.joins; import io.ebean.BaseTestCase; import io.ebean.DB; +import io.ebean.Query; import io.ebean.test.LoggedSql; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.tests.model.basic.Order; +import org.tests.model.basic.OrderShipment; import org.tests.model.basic.ResetBasicData; import org.tests.model.family.ChildPerson; import org.tests.model.family.ParentPerson; @@ -59,8 +61,8 @@ public class TestQueryJoinOnFormula extends BaseTestCase { LoggedSql.start(); int orders = DB.find(Order.class) - .where().eq("totalItems", 3) - .findCount(); + .where().eq("totalItems", 3) + .findCount(); assertThat(orders).isEqualTo(2); List sql = LoggedSql.stop(); @@ -69,6 +71,94 @@ public class TestQueryJoinOnFormula extends BaseTestCase { assertSql(sql.get(0)).contains("select count(*) from ( select t0.id from o_order t0 left join (select order_id,"); } + @Test + public void testOrderOnChainedFormulaProperty() { + // test that join to order.details is not included + + // Tests if SqlTreeBuilder.IncludesDistiller.createExtraJoin appends formulaJoinProperties + Query shipQuery = DB.find(OrderShipment.class) + .select("id") + .order().asc("order.totalAmount"); + + shipQuery.findList(); + assertThat(shipQuery.getGeneratedSql()).isEqualTo("select t0.id " + + "from or_order_ship t0 " + + "left join o_order t1 on t1.id = t0.order_id " + + "left join (select order_id, count(*) as total_items, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) z_bt1 on z_bt1.order_id = t1.id " + + "order by z_bt1.total_amount"); + } + + @Test + public void testWhereOnChainedFormulaProperty() { + // test that join to order.details is not included + + // Tests if SqlTreeBuilder.IncludesDistiller.createExtraJoin appends formulaJoinProperties + Query shipQuery = DB.find(OrderShipment.class) + .select("id") + .where().isNotNull("order.totalAmount").query(); + + shipQuery.findList(); + assertThat(shipQuery.getGeneratedSql()).isEqualTo("select t0.id " + + "from or_order_ship t0 " + + "left join o_order t1 on t1.id = t0.order_id " + + "left join (select order_id, count(*) as total_items, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) z_bt1 on z_bt1.order_id = t1.id " + + "where z_bt1.total_amount is not null"); + } + + @Test + public void testWhereOnChainedFormulaManyWhere() { + // test that join to order.details is not included + + // Tests if SqlTreeBuilder.IncludesDistiller.createExtraJoin appends formulaJoinProperties + Query shipQuery = DB.find(OrderShipment.class) + .select("id") + .where().isNotNull("order.shipments.order.totalAmount").query(); + + shipQuery.findList(); + assertThat(shipQuery.getGeneratedSql()).isEqualTo("select distinct t0.id " + + "from or_order_ship t0 " + + "join o_order u1 on u1.id = t0.order_id " + + "join or_order_ship u2 on u2.order_id = u1.id " + + "join o_order u3 on u3.id = u2.order_id " + + "left join (select order_id, count(*) as total_items, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) z_bu3 on z_bu3.order_id = u3.id " + + "where z_bu3.total_amount is not null"); + } + + @Test + public void testOrderOnChainedFormulaPropertyWithFetch() { + + // Tests if SqlTreeBuilder.buildSelectChain appends formulaJoinProperties + Query shipQuery = DB.find(OrderShipment.class) + .select("id") + .fetch("order", "totalAmount") + .order().asc("order.totalAmount"); + + shipQuery.findList(); + assertThat(shipQuery.getGeneratedSql()).isEqualTo("select t0.id, t1.id, z_bt1.total_amount " + + "from or_order_ship t0 " + + "left join o_order t1 on t1.id = t0.order_id " + + "left join (select order_id, count(*) as total_items, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) z_bt1 on z_bt1.order_id = t1.id " + + "order by z_bt1.total_amount"); + + } + + @Test + public void testWhereOnChainedFormulaPropertyWithFetch() { + // Tests if SqlTreeBuilder.buildSelectChain appends formulaJoinProperties + Query shipQuery = DB.find(OrderShipment.class) + .select("id") + .fetch("order", "totalAmount") + .where().isNotNull("order.totalAmount").query(); + + shipQuery.findList(); + assertThat(shipQuery.getGeneratedSql()).isEqualTo("select t0.id, t1.id, z_bt1.total_amount " + + "from or_order_ship t0 " + + "left join o_order t1 on t1.id = t0.order_id " + + "left join (select order_id, count(*) as total_items, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) z_bt1 on z_bt1.order_id = t1.id " + + "where z_bt1.total_amount is not null"); + + } + @Test public void test_OrderFindCount_multiFormula() { @@ -211,13 +301,15 @@ public class TestQueryJoinOnFormula extends BaseTestCase { LoggedSql.start(); DB.find(ChildPerson.class) - .where().eq("parent.totalAge", 3) - .findIds(); + .where().eq("parent.totalAge", 3) + .findIds(); List loggedSql = LoggedSql.stop(); assertEquals(1, loggedSql.size()); - assertThat(loggedSql.get(0)).contains("select t0.identifier from child_person t0 left join (select i2.parent_identifier"); - assertThat(loggedSql.get(0)).contains("where coalesce(f2.child_age, 0) = ?"); + assertThat(loggedSql.get(0)) + .contains("select t0.identifier from child_person t0") + .contains("left join (select i2.parent_identifier") + .contains("where coalesce(f2.child_age, 0) = ?"); } @Test @@ -231,7 +323,7 @@ public class TestQueryJoinOnFormula extends BaseTestCase { List loggedSql = LoggedSql.stop(); assertEquals(1, loggedSql.size()); - assertThat(loggedSql.get(0)).contains("select count(*) from ( select t0.identifier"); + assertThat(loggedSql.get(0)).contains("select count(*) from child_person t0 left join parent_person t1 on t1.identifier = t0.parent_identifier"); assertThat(loggedSql.get(0)).contains("where coalesce(f2.child_age, 0) = ?"); } }