From cd8c12a9a8f2419e01b5002aaa00053f9cba5458 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Wed, 9 Jan 2019 19:13:35 +1300 Subject: [PATCH] #1566 - ENH: Support query.setBaseTable() ... to query against dynamic table (ala manual table partitioning) --- src/main/java/io/ebean/Query.java | 9 ++++ .../java/io/ebeaninternal/api/SpiQuery.java | 5 ++ .../server/query/SqlTreeBuilder.java | 13 +++-- .../server/query/SqlTreeNodeRoot.java | 9 ++-- .../server/querydefn/DefaultOrmQuery.java | 17 +++++++ .../org/tests/query/TestQueryBaseTable.java | 51 +++++++++++++++++++ 6 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/tests/query/TestQueryBaseTable.java diff --git a/src/main/java/io/ebean/Query.java b/src/main/java/io/ebean/Query.java index 4f0fab7de..25e8d2730 100644 --- a/src/main/java/io/ebean/Query.java +++ b/src/main/java/io/ebean/Query.java @@ -1596,6 +1596,15 @@ public interface Query { */ Query alias(String alias); + /** + * Set the base table to use for this query. + *

+ * Typically this is used when a table has partitioning and we wish to specify a specific + * partition/table to query against. + *

+ */ + Query setBaseTable(String baseTable); + /** * Return the type of beans being queried. */ diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java index 881cae5e3..e83258486 100644 --- a/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -820,6 +820,11 @@ public interface SpiQuery extends Query, TxnProfileEventCodes { */ boolean isCancelled(); + /** + * Return the base table to use if user defined on the query. + */ + String getBaseTable(); + /** * Return root table alias set by {@link #alias(String)} command. */ diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 00ddc14c5..1c21b2e6f 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -312,7 +312,12 @@ public final class SqlTreeBuilder { // Optional many property for lazy loading query STreePropertyAssocMany lazyLoadMany = (query == null) ? null : query.getLazyLoadMany(); boolean withId = !rawNoId && !subQuery && (query == null || query.isWithId()); - return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany, temporalMode, disableLazyLoad, sqlDistinct); + + String baseTable = (query == null) ? null : query.getBaseTable(); + if (baseTable == null) { + baseTable = desc.getBaseTable(temporalMode); + } + return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany, temporalMode, disableLazyLoad, sqlDistinct, baseTable); } else if (prop instanceof STreePropertyAssocMany) { return new SqlTreeNodeManyRoot(prefix, (STreePropertyAssocMany) prop, props, myList, temporalMode, disableLazyLoad); @@ -497,9 +502,9 @@ public final class SqlTreeBuilder { for (STreePropertyAssocOne propertyAssocOne : desc.propsOne()) { //noinspection StatementWithEmptyBody if (queryProps != null - && queryProps.isIncludedBeanJoin(propertyAssocOne.getName()) - && propertyAssocOne.hasForeignKey() - && !propertyAssocOne.isFormula()) { + && queryProps.isIncludedBeanJoin(propertyAssocOne.getName()) + && propertyAssocOne.hasForeignKey() + && !propertyAssocOne.isFormula()) { // if it is a joined bean with FK constraint... then don't add the property // as it will have its own entire Node in the SqlTree } else { diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java index 2f25bf7e2..78ef22595 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java @@ -16,15 +16,18 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean { private final boolean sqlDistinct; + private final String baseTable; + /** * Specify for SqlSelect to include an Id property or not. */ - SqlTreeNodeRoot(STreeType desc, SqlTreeProperties props, List myList, boolean withId, - TableJoin includeJoin, STreePropertyAssocMany many, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad, boolean sqlDistinct) { + SqlTreeNodeRoot(STreeType desc, SqlTreeProperties props, List myList, boolean withId, TableJoin includeJoin, + STreePropertyAssocMany many, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad, boolean sqlDistinct, String baseTable) { super(desc, props, myList, withId, many, temporalMode, disableLazyLoad); this.includeJoin = includeJoin; this.sqlDistinct = sqlDistinct; + this.baseTable = baseTable; } @Override @@ -73,7 +76,7 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean { @Override public SqlJoinType appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) { - ctx.append(desc.getBaseTable(temporalMode)); + ctx.append(baseTable); ctx.append(" ").append(baseTableAlias); ctx.appendFromForUpdate(); diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 07608ff51..0a461b743 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -235,6 +235,8 @@ public class DefaultOrmQuery implements SpiQuery { */ private String rootTableAlias; + private String baseTable; + /** * The node of the bean or collection that fired lazy loading. Not null if profiling is on and * this query is for lazy loading. Used to hook back a lazy loading query to the "original" query @@ -764,6 +766,7 @@ public class DefaultOrmQuery implements SpiQuery { copy.profilingListener = profilingListener; copy.profileLocation = profileLocation; + copy.baseTable = baseTable; copy.rootTableAlias = rootTableAlias; copy.distinct = distinct; copy.timeout = timeout; @@ -1082,6 +1085,9 @@ public class DefaultOrmQuery implements SpiQuery { if (disableLazyLoading) { sb.append(",disLazy:"); } + if (baseTable != null) { + sb.append(",baseTable:").append(baseTable); + } if (rootTableAlias != null) { sb.append(",root:").append(rootTableAlias); } @@ -1858,6 +1864,17 @@ public class DefaultOrmQuery implements SpiQuery { } } + @Override + public Query setBaseTable(String baseTable) { + this.baseTable = baseTable; + return this; + } + + @Override + public String getBaseTable() { + return baseTable; + } + @Override public DefaultOrmQuery alias(String alias) { this.rootTableAlias = alias; diff --git a/src/test/java/org/tests/query/TestQueryBaseTable.java b/src/test/java/org/tests/query/TestQueryBaseTable.java new file mode 100644 index 000000000..1e5fc13c0 --- /dev/null +++ b/src/test/java/org/tests/query/TestQueryBaseTable.java @@ -0,0 +1,51 @@ +package org.tests.query; + +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import io.ebean.annotation.ForPlatform; +import io.ebean.annotation.Platform; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; +import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestQueryBaseTable extends BaseTestCase { + + @ForPlatform({Platform.H2, Platform.POSTGRES}) + @Test + public void test() { + + ResetBasicData.reset(); + + LoggedSqlCollector.start(); + + Customer one = Ebean.find(Customer.class) + .setBaseTable("O_CUSTOMER") + .where().startsWith("name", "Rob") + .findOne(); + + Customer two = Ebean.find(Customer.class) + .where().startsWith("name", "Rob") + .findOne(); + + Customer three = Ebean.find(Customer.class) + .setBaseTable("O_CUSTOMER") + .where().startsWith("name", "Fiona") + .findOne(); + + assertThat(one.getName()).isEqualTo(two.getName()); + assertThat(three.getName()).isEqualTo("Fiona"); + + List sql = LoggedSqlCollector.stop(); + + assertThat(sql).hasSize(3); + assertThat(sql.get(0)).contains("from O_CUSTOMER"); + assertThat(sql.get(1)).contains("from o_customer"); + assertThat(sql.get(2)).contains("from O_CUSTOMER"); + + } +}