diff --git a/src/main/java/io/ebean/FetchGroup.java b/src/main/java/io/ebean/FetchGroup.java index 2d45ee4f1..95ea70412 100644 --- a/src/main/java/io/ebean/FetchGroup.java +++ b/src/main/java/io/ebean/FetchGroup.java @@ -1,5 +1,7 @@ package io.ebean; +import io.ebean.service.SpiFetchGroupQuery; + import javax.annotation.Nonnull; /** @@ -111,4 +113,11 @@ public interface FetchGroup { return XServiceProvider.fetchGroupOf(cls); } + /** + * Return a query to be used by query beans for constructing FetchGroup. + */ + static SpiFetchGroupQuery queryFor(Class beanType) { + return XServiceProvider.fetchGroupQueryFor(beanType); + } + } diff --git a/src/main/java/io/ebean/XServiceProvider.java b/src/main/java/io/ebean/XServiceProvider.java index 81976df38..bc6c36d71 100644 --- a/src/main/java/io/ebean/XServiceProvider.java +++ b/src/main/java/io/ebean/XServiceProvider.java @@ -1,5 +1,6 @@ package io.ebean; +import io.ebean.service.SpiFetchGroupQuery; import io.ebean.service.SpiFetchGroupService; import io.ebean.service.SpiProfileLocationFactory; import io.ebean.service.SpiRawSqlService; @@ -71,4 +72,11 @@ class XServiceProvider { static FetchGroupBuilder fetchGroupOf(Class cls) { return fetchGroupService.of(cls); } + + /** + * Return the FetchGroup Query for building fetch groups via query beans. + */ + static SpiFetchGroupQuery fetchGroupQueryFor(Class cls) { + return fetchGroupService.queryFor(cls); + } } diff --git a/src/main/java/io/ebean/service/SpiFetchGroupQuery.java b/src/main/java/io/ebean/service/SpiFetchGroupQuery.java new file mode 100644 index 000000000..c620d247a --- /dev/null +++ b/src/main/java/io/ebean/service/SpiFetchGroupQuery.java @@ -0,0 +1,15 @@ +package io.ebean.service; + +import io.ebean.FetchGroup; +import io.ebean.Query; + +/** + * Extension of Query to build FetchGroup via query beans. + */ +public interface SpiFetchGroupQuery extends Query { + + /** + * Build the fetch group with select and fetch clauses. + */ + FetchGroup buildFetchGroup(); +} diff --git a/src/main/java/io/ebean/service/SpiFetchGroupService.java b/src/main/java/io/ebean/service/SpiFetchGroupService.java index b486d4ae1..e47ac5d63 100644 --- a/src/main/java/io/ebean/service/SpiFetchGroupService.java +++ b/src/main/java/io/ebean/service/SpiFetchGroupService.java @@ -23,4 +23,10 @@ public interface SpiFetchGroupService { * @return The FetchGroupBuilder to add additional select and fetch clauses */ FetchGroupBuilder of(Class beanType); + + /** + * Return a new FetchGroupQuery for building FetchGroup via query beans. + */ + SpiFetchGroupQuery queryFor(Class beanType); + } diff --git a/src/main/java/io/ebeaninternal/server/query/DFetchGroupService.java b/src/main/java/io/ebeaninternal/server/query/DFetchGroupService.java index 3dc53a83a..1a31ef613 100644 --- a/src/main/java/io/ebeaninternal/server/query/DFetchGroupService.java +++ b/src/main/java/io/ebeaninternal/server/query/DFetchGroupService.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.query; import io.ebean.FetchGroup; import io.ebean.FetchGroupBuilder; +import io.ebean.service.SpiFetchGroupQuery; import io.ebean.service.SpiFetchGroupService; import io.ebeaninternal.server.querydefn.OrmQueryDetail; @@ -20,6 +21,11 @@ public final class DFetchGroupService implements SpiFetchGroupService { return new DFetchGroupBuilder<>(); } + @Override + public SpiFetchGroupQuery queryFor(Class beanType) { + return new DefaultFetchGroupQuery<>(); + } + private OrmQueryDetail detail(String select) { OrmQueryDetail detail = new OrmQueryDetail(); detail.select(select); diff --git a/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java b/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java new file mode 100644 index 000000000..ff0632949 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java @@ -0,0 +1,580 @@ +package io.ebeaninternal.server.query; + +import io.ebean.CacheMode; +import io.ebean.CountDistinctOrder; +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.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.service.SpiFetchGroupQuery; +import io.ebeaninternal.server.querydefn.OrmQueryDetail; +import io.ebeaninternal.server.querydefn.SpiFetchGroup; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.Timestamp; +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; + +/** + * Implementation of FetchGroup query for use to create FetchGroup via query beans. + */ +class DefaultFetchGroupQuery implements SpiFetchGroupQuery { + + private static final FetchConfig FETCH_QUERY = new FetchConfig().query(); + + private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy(); + + private OrmQueryDetail detail = new OrmQueryDetail(); + + @Override + public FetchGroup buildFetchGroup() { + return new DFetchGroup<>(detail); + } + + @Override + public Query select(String columns) { + detail.select(columns); + return this; + } + + @Override + public Query select(FetchGroup fetchGroup) { + this.detail = ((SpiFetchGroup) fetchGroup).detail(); + return this; + } + + @Override + public Query fetch(String property) { + return fetch(property, null, null); + } + + @Override + public Query fetchQuery(String property) { + return fetch(property, null, FETCH_QUERY); + } + + @Override + public Query fetchLazy(String property) { + return fetch(property, null, FETCH_LAZY); + } + + @Override + public Query fetch(String property, FetchConfig joinConfig) { + return fetch(property, null, joinConfig); + } + + @Override + public Query fetch(String property, String columns) { + return fetch(property, columns, null); + } + + @Override + public Query fetchQuery(String property, String columns) { + return fetch(property, columns, FETCH_QUERY); + } + + @Override + public Query fetchLazy(String property, String columns) { + return fetch(property, columns, FETCH_LAZY); + } + + @Override + public Query fetch(String property, String columns, FetchConfig config) { + detail.fetch(property, columns, config); + return this; + } + + @Override + public Query setProfileId(int profileId) { + return this; + } + + @Override + public Query setProfileLocation(ProfileLocation profileLocation) { + return this; + } + + @Override + public Query setLabel(String label) { + return this; + } + + // Everything else deemed invalid + + @Override + public Query copy() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setRawSql(RawSql rawSql) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query asOf(Timestamp asOf) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query asDraft() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public DtoQuery asDto(Class dtoClass) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public UpdateQuery asUpdate() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public void cancel() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setPersistenceContextScope(PersistenceContextScope scope) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setDocIndexName(String indexName) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public ExpressionFactory getExpressionFactory() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public boolean isAutoTuned() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setAutoTune(boolean autoTune) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setAllowLoadErrors() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setLazyLoadBatchSize(int lazyLoadBatchSize) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setIncludeSoftDeletes() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setDisableReadAuditing() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query apply(FetchPath fetchPath) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query usingTransaction(Transaction transaction) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query usingConnection(Connection connection) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public List findIds() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public QueryIterator findIterate() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public void findEach(Consumer consumer) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public void findEachWhile(Predicate consumer) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public List findList() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public Set findSet() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public Map findMap() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public List findSingleAttributeList() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public A findSingleAttribute() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public boolean isCountDistinct() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public boolean exists() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nullable + @Override + public T findOne() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public Optional findOneOrEmpty() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public List> findVersions() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public List> findVersionsBetween(Timestamp start, Timestamp end) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int delete() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int delete(Transaction transaction) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int update() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int update(Transaction transaction) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int findCount() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public FutureRowCount findFutureCount() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public FutureIds findFutureIds() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public FutureList findFutureList() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Nonnull + @Override + public PagedList findPagedList() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setParameter(String name, Object value) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setParameter(int position, Object value) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setId(Object id) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Object getId() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query where(Expression expression) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public ExpressionList where() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public ExpressionList text() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public ExpressionList filterMany(String propertyName) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public ExpressionList having() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query having(Expression addExpressionToHaving) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query orderBy(String orderByClause) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query order(String orderByClause) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public OrderBy order() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public OrderBy orderBy() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setOrder(OrderBy orderBy) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setOrderBy(OrderBy orderBy) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setDistinct(boolean isDistinct) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setCountDistinct(CountDistinctOrder orderBy) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int getFirstRow() { + return 0; + } + + @Override + public Query setFirstRow(int firstRow) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public int getMaxRows() { + return 0; + } + + @Override + public Query setMaxRows(int maxRows) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setMapKey(String mapKey) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setBeanCacheMode(CacheMode beanCacheMode) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setUseQueryCache(CacheMode queryCacheMode) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setUseDocStore(boolean useDocStore) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setReadOnly(boolean readOnly) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setLoadBeanCache(boolean loadBeanCache) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setTimeout(int secs) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setBufferFetchSizeHint(int fetchSize) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public String getGeneratedSql() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query forUpdate() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query forUpdateNoWait() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query forUpdateSkipLocked() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public boolean isForUpdate() { + return false; + } + + @Override + public ForUpdate getForUpdateMode() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query alias(String alias) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setBaseTable(String baseTable) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Class getBeanType() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setInheritType(Class type) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Class getInheritType() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public QueryType getQueryType() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query setDisableLazyLoading(boolean disableLazyLoading) { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Set validate() { + throw new RuntimeException("Not allowed on fetch group query "); + } + + @Override + public Query orderById(boolean orderById) { + throw new RuntimeException("Not allowed on fetch group query "); + } +} diff --git a/src/test/java/io/ebean/FetchGroupTest.java b/src/test/java/io/ebean/FetchGroupTest.java index 148cc7230..8d0fd9c53 100644 --- a/src/test/java/io/ebean/FetchGroupTest.java +++ b/src/test/java/io/ebean/FetchGroupTest.java @@ -1,5 +1,6 @@ package io.ebean; +import io.ebean.service.SpiFetchGroupQuery; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import org.tests.model.basic.Address; @@ -112,4 +113,26 @@ public class FetchGroupTest extends BaseTestCase { assertThat(sql.get(0)).contains("select t0.id, t0.name, t0.version, t1.id, t1.line_1, t1.line_2, t1.city, t2.code, t2.name from o_customer t0 left join o_address t1 on t1.id = t0.billing_address_id left join o_country t2 on t2.code = t1.country_code "); } + @Test + public void fetchGroupQuery() { + + // in practice this query is used by query beans for type safe FetchGroup construction + final SpiFetchGroupQuery query = FetchGroup.queryFor(Customer.class); + query.select("name"); + + final FetchGroup fetchGroup = query.buildFetchGroup(); + + LoggedSqlCollector.start(); + + Customer.find + .query() + .where() + .ilike("name", "rob") + .select(fetchGroup) + .findList(); + + List sql = LoggedSqlCollector.stop(); + assertThat(sql).hasSize(1); + assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name from o_customer t0 where"); + } }