From 5c6d6608ef11d1a72252e50597ae59f91d66d0e4 Mon Sep 17 00:00:00 2001
From: Robin Bygrave
Date: Wed, 17 Feb 2016 13:59:48 +1300
Subject: [PATCH] #566 - Refactor internals - Initial tidy on OrmQueryDetail
and OrmQueryDetailProperties - internals for fetch paths etc
---
.../autotune/service/ProfileOrigin.java | 2 +-
.../server/deploy/BeanDescriptor.java | 2 +-
.../server/query/CQueryBuilder.java | 2 +-
.../server/query/SqlTreeBuilder.java | 8 +-
.../server/querydefn/DefaultOrmQuery.java | 2 +-
.../server/querydefn/OrmQueryDetail.java | 63 +++---
.../querydefn/OrmQueryDetailParser.java | 5 +-
.../server/querydefn/OrmQueryProperties.java | 186 +++++++++---------
...faultServer_createOrmQueryRequestTest.java | 179 ++++++++++++++++-
.../server/querydefn/OrmQueryDetailTest.java | 49 ++++-
.../querydefn/OrmQueryPropertiesTest.java | 39 ++++
.../tests/query/TestQueryFindIterate.java | 5 +-
12 files changed, 397 insertions(+), 145 deletions(-)
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autotune/service/ProfileOrigin.java b/src/main/java/com/avaje/ebeaninternal/server/autotune/service/ProfileOrigin.java
index 626285149..c89a9db87 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autotune/service/ProfileOrigin.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autotune/service/ProfileOrigin.java
@@ -110,7 +110,7 @@ public class ProfileOrigin {
Collection pathProperties = pathProps.getPathProps();
for (Props props : pathProperties) {
if (!props.isEmpty()) {
- detail.addFetch(props.getPath(), props.getPropertiesAsString(), null);
+ detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
index fada56d65..2e14bd300 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -1252,7 +1252,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType {
OrmQueryDetail detail = query.getDetail();
for (int i = 0; i < propertiesMany.length; i++) {
- if (detail.includes(propertiesMany[i].getName())) {
+ if (detail.includesPath(propertiesMany[i].getName())) {
return propertiesMany[i];
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java
index 9f5bf3181..ba9acce78 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java
@@ -353,7 +353,7 @@ public class CQueryBuilder {
// transfer PathProperties into OrmQueryDetail
for (String path : pathProps.getPaths()) {
- detail.getChunk(path, true).setDefaultProperties(null, pathProps.get(path));
+ detail.fetch(path, pathProps.get(path));
}
// check if @Id property included in RawSql
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java
index a4de477ac..285f412a1 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java
@@ -140,7 +140,7 @@ public class SqlTreeBuilder {
boolean includeJoins = (alias == null) ? false : alias.isIncludeJoins();
return new SqlTree(summary.toString(), rootNode, selectSql, fromSql, inheritanceWhereSql, encryptedProps,
- manyProperty, queryDetail.getIncludes(), includeJoins);
+ manyProperty, queryDetail.getFetchPaths(), includeJoins);
}
private String buildSelectClause() {
@@ -183,7 +183,7 @@ public class SqlTreeBuilder {
rootNode = buildSelectChain(null, null, desc, null);
if (!rawSql) {
- alias.addJoin(queryDetail.getIncludes(), desc);
+ alias.addJoin(queryDetail.getFetchPaths(), desc);
alias.addJoin(predicates.getPredicateIncludes(), desc);
alias.addManyWhereJoins(manyWhereJoins.getPropertyNames());
@@ -471,7 +471,7 @@ public class SqlTreeBuilder {
return false;
}
- if (queryDetail.includes(propName)) {
+ if (queryDetail.includesPath(propName)) {
if (manyProperty != null) {
// only one many associated allowed to be included in fetch
@@ -498,7 +498,7 @@ public class SqlTreeBuilder {
*/
private boolean isIncludeBean(String prefix) {
- if (queryDetail.includes(prefix)) {
+ if (queryDetail.includesPath(prefix)) {
// explicitly included
summary.append(", ").append(prefix);
String[] splitNames = SplitName.split(prefix);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 2fb6afb81..7d05cec6f 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -1008,7 +1008,7 @@ public class DefaultOrmQuery implements SpiQuery {
}
public DefaultOrmQuery fetch(String property, String columns, FetchConfig config) {
- detail.addFetch(property, columns, config);
+ detail.fetch(property, columns, config);
return this;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java
index b9d643b67..4f6383c3b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java
@@ -13,12 +13,12 @@ import javax.persistence.PersistenceException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Represents the internal structure of an Object Relational query.
@@ -46,8 +46,6 @@ public class OrmQueryDetail implements Serializable {
*/
private LinkedHashMap fetchPaths = new LinkedHashMap(8);
- private LinkedHashSet includes = new LinkedHashSet(8);
-
/**
* Return a deep copy of the OrmQueryDetail.
*/
@@ -57,7 +55,6 @@ public class OrmQueryDetail implements Serializable {
for (Map.Entry entry : fetchPaths.entrySet()) {
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
}
- copy.includes = new LinkedHashSet(includes);
return copy;
}
@@ -170,7 +167,7 @@ public class OrmQueryDetail implements Serializable {
* set the properties to include on the base / root entity.
*/
public void select(String columns) {
- baseProps = new OrmQueryProperties(null, columns);
+ baseProps = new OrmQueryProperties(null, columns, null);
}
public boolean containsProperty(String property) {
@@ -215,7 +212,6 @@ public class OrmQueryDetail implements Serializable {
for (int i = 0; i < matchingPaths.size(); i++) {
String path = matchingPaths.get(i);
- includes.remove(path);
OrmQueryProperties secQuery = fetchPaths.remove(path);
if (secQuery != null) {
props.add(secQuery);
@@ -228,7 +224,6 @@ public class OrmQueryDetail implements Serializable {
// remove join to secondary query from the main query
// and add to this secondary query
pass2It.remove();
- includes.remove(pass2Prop.getPath());
secQuery.add(pass2Prop);
}
}
@@ -257,29 +252,24 @@ public class OrmQueryDetail implements Serializable {
OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false);
if (tunedRoot != null) {
tuned = true;
- baseProps.setTunedProperties(tunedRoot);
-
+ baseProps = tunedRoot;
for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) {
- OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false);
- if (chunk != null) {
- // set the properties to select
- chunk.setTunedProperties(tunedChunk);
- } else {
- // add a missing join
- putFetchPath(tunedChunk.copy());
- }
+ fetch(tunedChunk.copy());
}
}
return tuned;
}
/**
- * Matches a join() method of the query.
+ * Add or replace the fetch detail.
*/
- public void putFetchPath(OrmQueryProperties chunk) {
+ protected void fetch(OrmQueryProperties chunk) {
String path = chunk.getPath();
- fetchPaths.put(path, chunk);
- includes.add(path);
+ if (path == null) {
+ baseProps = chunk;
+ } else {
+ fetchPaths.put(path, chunk);
+ }
}
/**
@@ -289,7 +279,6 @@ public class OrmQueryDetail implements Serializable {
*
*/
public void clear() {
- includes.clear();
fetchPaths.clear();
}
@@ -299,11 +288,16 @@ public class OrmQueryDetail implements Serializable {
* @param path the property to join
* @param partialProps the properties on the join property to include
*/
- public void addFetch(String path, String partialProps, FetchConfig fetchConfig) {
+ public void fetch(String path, String partialProps, FetchConfig fetchConfig) {
- OrmQueryProperties chunk = getChunk(path, true);
- chunk.setProperties(partialProps);
- chunk.setFetchConfig(fetchConfig);
+ fetch(new OrmQueryProperties(path, partialProps, fetchConfig));
+ }
+
+ /**
+ * Add for raw sql etc when the properties are already parsed into a set.
+ */
+ public void fetch(String path, LinkedHashSet properties) {
+ fetch(new OrmQueryProperties(path, properties));
}
public void sortFetchPaths(BeanDescriptor> d) {
@@ -438,15 +432,14 @@ public class OrmQueryDetail implements Serializable {
public void setDefaultSelectClause(BeanDescriptor> desc) {
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
- baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet());
+ baseProps = new OrmQueryProperties(null, desc.getDefaultSelectClause());
}
for (OrmQueryProperties joinProps : fetchPaths.values()) {
if (!joinProps.hasSelectClause()) {
BeanDescriptor> assocDesc = desc.getBeanDescriptor(joinProps.getPath());
if (assocDesc.hasDefaultSelectClause()) {
- // use the default select clause
- joinProps.setDefaultProperties(assocDesc.getDefaultSelectClause(), assocDesc.getDefaultSelectClauseSet());
+ fetch(joinProps.getPath(), assocDesc.getDefaultSelectClause(), joinProps.getFetchConfig());
}
}
}
@@ -489,7 +482,7 @@ public class OrmQueryDetail implements Serializable {
OrmQueryProperties props = fetchPaths.get(path);
if (create && props == null) {
props = new OrmQueryProperties(path);
- putFetchPath(props);
+ fetch(props);
return props;
} else {
@@ -498,9 +491,9 @@ public class OrmQueryDetail implements Serializable {
}
/**
- * Return true if the property is included.
+ * Return true if the fetch path is included.
*/
- public boolean includes(String path) {
+ public boolean includesPath(String path) {
OrmQueryProperties chunk = fetchPaths.get(path);
@@ -509,9 +502,9 @@ public class OrmQueryDetail implements Serializable {
}
/**
- * Return the property includes for this detail.
+ * Return the fetch paths for this detail.
*/
- public HashSet getIncludes() {
- return includes;
+ public Set getFetchPaths() {
+ return fetchPaths.keySet();
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java
index a54d56da7..7b2109288 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java
@@ -64,8 +64,7 @@ public class OrmQueryDetailParser {
private void process() {
if (isFetch()) {
- OrmQueryProperties props = readFindFetch();
- detail.putFetchPath(props);
+ detail.fetch(readFindFetch());
} else if (parser.isMatch("where")) {
readWhere();
@@ -157,7 +156,7 @@ public class OrmQueryDetailParser {
private void readSelect() {
String path = null;
String props = parser.nextWord();
- if (props != null && props.startsWith("(")) {
+ if (props.startsWith("(")) {
props = props.substring(1, props.length() - 1);
OrmQueryProperties base = new OrmQueryProperties(path, props);
detail.setBase(base);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
index 2d2b12c09..625537773 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -28,11 +28,11 @@ public class OrmQueryProperties implements Serializable {
private static final long serialVersionUID = -8785582703966455658L;
- private String parentPath;
- private String path;
+ private final String parentPath;
+ private final String path;
- private String rawProperties;
- private String trimmedProperties;
+ private final String rawProperties;
+ private final String trimmedProperties;
/**
* NB: -1 means no +query, 0 means use the default batch size.
@@ -55,7 +55,7 @@ public class OrmQueryProperties implements Serializable {
* Note this SHOULD be a LinkedHashSet to preserve order of the properties. This is to make using
* SqlSelect easier with predictable property/column ordering.
*/
- private LinkedHashSet included;
+ private final LinkedHashSet included;
/**
* Included bean joins.
@@ -83,25 +83,110 @@ public class OrmQueryProperties implements Serializable {
private SpiExpressionList filterMany;
/**
- * Construct with a given path (null == root path).
+ * Construct for root so path (and parentPath) are null.
+ */
+ public OrmQueryProperties() {
+ this((String)null);
+ }
+
+ /**
+ * Construct with a given path.
*/
public OrmQueryProperties(String path) {
this.path = path;
this.parentPath = SplitName.parent(path);
+ this.rawProperties = null;
+ this.trimmedProperties = null;
+ this.included = null;
+ }
+
+ public OrmQueryProperties(String path, String rawProperties) {
+ this(path, rawProperties, null);
+ }
+
+ public OrmQueryProperties(String path, String rawProperties, FetchConfig fetchConfig) {
+
+ OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
+
+ this.path = path;
+ this.parentPath = SplitName.parent(path);
+ this.rawProperties = rawProperties;
+ this.trimmedProperties = response.properties;
+ this.included = response.included;
+ this.lazyFetchBatch = response.lazyFetchBatch;
+ this.queryFetchBatch = response.queryFetchBatch;
+ this.cache = response.cache;
+ this.readOnly = response.readOnly;
+
+ if (fetchConfig != null) {
+ this.fetchConfig = fetchConfig;
+ lazyFetchBatch = fetchConfig.getLazyBatchSize();
+ queryFetchBatch = fetchConfig.getQueryBatchSize();
+ queryFetchAll = fetchConfig.isQueryAll();
+ }
+ }
+
+ public OrmQueryProperties(String path, LinkedHashSet parsedProperties) {
+ if (parsedProperties == null) {
+ throw new IllegalArgumentException("parsedProperties is null");
+ }
+
+ this.path = path;
+ this.parentPath = SplitName.parent(path);
+ // for rawSql parsedProperties can be empty (when only fetching Id property)
+ this.included = parsedProperties;
+ this.rawProperties = join(parsedProperties);
+ this.trimmedProperties = rawProperties;
+ this.lazyFetchBatch = -1;
+ this.queryFetchBatch = -1;
+ this.cache = false;
+ this.readOnly = false;
+ this.queryFetchAll = false;
}
/**
- * Construct for root so path (and parentPath) are null.
+ * Join the set of properties into a comma delimited string.
*/
- public OrmQueryProperties() {
+ private String join(LinkedHashSet parsedProperties) {
+ StringBuilder sb = new StringBuilder(50);
+ boolean first = true;
+ for (String property : parsedProperties) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(property);
+ }
+ return sb.toString();
}
/**
- * Used by query language parser.
+ * Copy constructor.
*/
- public OrmQueryProperties(String path, String properties) {
- this(path);
- setProperties(properties);
+ private OrmQueryProperties(OrmQueryProperties source) {
+
+ this.parentPath = source.parentPath;
+ this.path = source.path;
+ this.rawProperties = source.rawProperties;
+ this.trimmedProperties = source.trimmedProperties;
+ this.cache = source.cache;
+ this.readOnly = source.readOnly;
+ this.queryFetchAll = source.queryFetchAll;
+ this.queryFetchBatch = source.queryFetchBatch;
+ this.lazyFetchBatch = source.lazyFetchBatch;
+ this.filterMany = source.filterMany;
+ this.included = (source.included == null) ? null : new LinkedHashSet(source.included);
+ if (includedBeanJoin != null) {
+ this.includedBeanJoin = new HashSet(source.includedBeanJoin);
+ }
+ }
+
+ /**
+ * Creates a copy of the OrmQueryProperties.
+ */
+ public OrmQueryProperties copy() {
+ return new OrmQueryProperties(this);
}
/**
@@ -115,41 +200,10 @@ public class OrmQueryProperties implements Serializable {
orderBy.add(orderProp);
}
- /**
- * Set the Fetch configuration options for this path.
- */
- public void setFetchConfig(FetchConfig fetchConfig) {
- if (fetchConfig != null) {
- this.fetchConfig = fetchConfig;
- lazyFetchBatch = fetchConfig.getLazyBatchSize();
- queryFetchBatch = fetchConfig.getQueryBatchSize();
- queryFetchAll = fetchConfig.isQueryAll();
- }
- }
-
public FetchConfig getFetchConfig() {
return fetchConfig;
}
- /**
- * Set the comma delimited properties to fetch for this path.
- *
- * This can include the +query and +lazy type hints.
- *
- */
- public void setProperties(String rawProperties) {
-
- OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
-
- this.rawProperties = rawProperties;
- this.trimmedProperties = response.properties;
- this.included = response.included;
- this.lazyFetchBatch = response.lazyFetchBatch;
- this.queryFetchBatch = response.queryFetchBatch;
- this.cache = response.cache;
- this.readOnly = response.readOnly;
- }
-
/**
* Return the expressions used to filter on this path. This should be a many path to use this
* method.
@@ -193,28 +247,6 @@ public class OrmQueryProperties implements Serializable {
this.filterMany = filterMany;
}
- /**
- * Set the properties from deployment default FetchTypes.
- */
- public void setDefaultProperties(String properties, LinkedHashSet included) {
- this.rawProperties = properties;
- this.trimmedProperties = properties;
- this.included = included;
- }
-
- /**
- * Set the properties from a matching AutoTune tuned properties.
- */
- public void setTunedProperties(OrmQueryProperties tunedProperties) {
- if (tunedProperties.hasProperties()) {
- this.rawProperties = tunedProperties.rawProperties;
- this.trimmedProperties = tunedProperties.trimmedProperties;
- this.included = tunedProperties.included;
- this.queryFetchBatch = Math.max(queryFetchBatch, tunedProperties.queryFetchBatch);
- this.lazyFetchBatch = Math.max(lazyFetchBatch, tunedProperties.lazyFetchBatch);
- }
- }
-
/**
* Define the select and joins for this query.
*/
@@ -249,30 +281,6 @@ public class OrmQueryProperties implements Serializable {
}
}
- /**
- * Creates a copy of the OrmQueryProperties.
- */
- public OrmQueryProperties copy() {
- OrmQueryProperties copy = new OrmQueryProperties();
- copy.parentPath = parentPath;
- copy.path = path;
- copy.rawProperties = rawProperties;
- copy.trimmedProperties = trimmedProperties;
- copy.cache = cache;
- copy.readOnly = readOnly;
- copy.queryFetchAll = queryFetchAll;
- copy.queryFetchBatch = queryFetchBatch;
- copy.lazyFetchBatch = lazyFetchBatch;
- copy.filterMany = filterMany;
- if (included != null) {
- copy.included = new LinkedHashSet(included);
- }
- if (includedBeanJoin != null) {
- copy.includedBeanJoin = new HashSet(includedBeanJoin);
- }
- return copy;
- }
-
public boolean hasSelectClause() {
if ("*".equals(trimmedProperties)) {
// explicitly selected all properties
diff --git a/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java b/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java
index fb98c62d8..7d7b0caf2 100644
--- a/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java
+++ b/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_createOrmQueryRequestTest.java
@@ -11,18 +11,18 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.tests.model.basic.Order;
import org.junit.Test;
-import static org.assertj.core.api.StrictAssertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
- static DefaultServer defaultServer = (DefaultServer)Ebean.getDefaultServer();
+ static DefaultServer defaultServer = (DefaultServer) Ebean.getDefaultServer();
Query query() {
return defaultServer.find(Order.class);
}
OrmQueryRequest queryRequest(Query query) {
- return (OrmQueryRequest)defaultServer.createQueryRequest(SpiQuery.Type.LIST, query, null);
+ return (OrmQueryRequest) defaultServer.createQueryRequest(SpiQuery.Type.LIST, query, null);
}
OrmQueryDetail detail(Query query) {
@@ -87,26 +87,26 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
// with the fetch of customer the foreign key must be added to the root query
assertDifferent(detail(query().select("id,name")),
- detail(query().select("id,name").fetch("customer",new FetchConfig().query())));
+ detail(query().select("id,name").fetch("customer", new FetchConfig().query())));
}
@Test
public void when_additional_fetch_V2_then_different() {
- assertDifferent(detail(query().select("id,name")), detail(query().select("id,name").fetch("customer","id")));
+ assertDifferent(detail(query().select("id,name")), detail(query().select("id,name").fetch("customer", "id")));
}
@Test
public void when_fetchConfig_then_differentPlan() throws Exception {
- DefaultOrmQuery query1 = (DefaultOrmQuery)Ebean.find(Order.class)
+ DefaultOrmQuery query1 = (DefaultOrmQuery) Ebean.find(Order.class)
.select("status, shipDate")
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
.fetch("details.product", "sku, name");
- DefaultOrmQuery query2 = (DefaultOrmQuery)Ebean.find(Order.class)
+ DefaultOrmQuery query2 = (DefaultOrmQuery) Ebean.find(Order.class)
.select("status, shipDate")
.fetch("details", "orderQty, unitPrice")
.fetch("details.product", "sku, name");
@@ -114,6 +114,171 @@ public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
assertDifferent(detail(query1), detail(query2));
}
+ @Test
+ public void testJoinOrder_when_fetchJoins_expect_detailJoinsPreserveOrder() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("customer", "name")
+ .fetch("details");
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer", "details");
+ }
+
+ @Test
+ public void testJoinOrder_when_fetchJoinsAndWhere_expect_fetchJoinsOnlyInFetchPaths() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("details")
+ .where().eq("customer.name", "rob").query();
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("details");
+ }
+
+ @Test
+ public void testJoinOrder_when_queryFetch_expect_getFetchPaths_doesNotIncludeQueryJoin() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("customer", "name")
+ .fetch("details", new FetchConfig().query());
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void testJoinOrder_when_lazyFetch_expect_getFetchPaths_doesNotIncludeQueryJoin() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("customer", "name")
+ .fetch("details", new FetchConfig().lazy());
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void testJoinOrder_when_lazyFetchAndHasChildren_expect_getFetchPaths_doesNotIncludeJoinOrChild() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("customer", "name")
+ .fetch("details", new FetchConfig().lazy())
+ .fetch("details.product");
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void testJoinOrder_when_fetchMany_expect_getFetchPaths_containsAllInOrder() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("details")
+ .fetch("details.product")
+ .fetch("customer", "name");
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("details", "details.product", "customer");
+ }
+
+ @Test
+ public void testJoinOrder_when_queryJoin_expect_getFetchPaths_excludesQueryJoinAndChildren() {
+
+ Query query = Ebean.find(Order.class)
+ .select("status, orderDate")
+ .fetch("details", new FetchConfig().query())
+ .fetch("details.product")
+ .fetch("customer", "name");
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void test_removeJoinToMany_when_multipleManyPaths() {
+
+ Query query = Ebean.find(Order.class)
+ .fetch("details")
+ .fetch("details.product")
+ .fetch("customer")
+ .fetch("customer.contacts"); // second many path
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("details", "details.product", "customer");
+ }
+
+ @Test
+ public void test_removeAllJoinToMany_when_firstRow() {
+
+ Query query = Ebean.find(Order.class)
+ .setFirstRow(1)
+ .fetch("details") // many path
+ .fetch("details.product")
+ .fetch("customer")
+ .fetch("customer.contacts"); // many path
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void test_removeAllJoinToMany_when_maxRows() {
+
+ Query query = Ebean.find(Order.class)
+ .setMaxRows(1)
+ .fetch("details") // many path
+ .fetch("details.product")
+ .fetch("customer")
+ .fetch("customer.contacts"); // many path
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void test_removeJoinToMany_when_filterMany() {
+
+ Query query = Ebean.find(Order.class)
+ .fetch("details")
+ .fetch("details.product")
+ .fetch("customer")
+ .fetch("customer.contacts")
+ .filterMany("details").eq("orderQuantity", 10)
+ .query();
+
+ OrmQueryRequest queryRequest = queryRequest(query);
+ OrmQueryDetail detail = queryRequest.getQuery().getDetail();
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer", "customer.contacts");
+ }
+
private void assertSame(OrmQueryDetail detail1, OrmQueryDetail detail2) {
assertThat(detail1.isSameByPlan(detail2)).isTrue();
assertThat(detail1.queryPlanHash()).isEqualTo(detail2.queryPlanHash());
diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java
index b12516431..31c3dac5a 100644
--- a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java
+++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java
@@ -3,7 +3,9 @@ package com.avaje.ebeaninternal.server.querydefn;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
public class OrmQueryDetailTest {
@@ -68,4 +70,49 @@ public class OrmQueryDetailTest {
assertNull(root.getPath());
assertThat(root.getIncluded()).containsExactly("name");
}
+
+ @Test
+ public void getFetchPaths_when_noFetches_then_expect_empty() {
+
+ assertThat(new OrmQueryDetail().getFetchPaths()).isEmpty();
+
+ OrmQueryDetail detail = new OrmQueryDetail();
+ detail.select("foo");
+
+ assertThat(detail.getFetchPaths()).isEmpty();
+ }
+
+ @Test
+ public void getFetchPaths_when_oneFetch() {
+
+ OrmQueryDetail detail = new OrmQueryDetail();
+ detail.select("foo");
+ detail.fetch("customer", null, null);
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer");
+ }
+
+ @Test
+ public void getFetchPaths_when_multipleFetch_expect_preserveOrder() {
+
+ OrmQueryDetail detail = new OrmQueryDetail();
+ detail.select("foo");
+ detail.fetch("customer", null, null);
+ detail.fetch("details", null, null);
+
+ assertThat(detail.getFetchPaths()).containsExactly("customer", "details");
+ }
+
+ @Test
+ public void getFetchPaths_when_multipleFetch_expect_preserveOrder_v2() {
+
+ OrmQueryDetail detail = new OrmQueryDetail();
+ detail.select("foo");
+ detail.fetch("details", null, null);
+ detail.fetch("customer", null, null);
+ detail.fetch("details.product", null, null);
+
+ assertThat(detail.getFetchPaths()).containsExactly("details", "customer", "details.product");
+ }
+
}
\ No newline at end of file
diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java
index b1cc03494..8e50cc033 100644
--- a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java
+++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java
@@ -2,6 +2,8 @@ package com.avaje.ebeaninternal.server.querydefn;
import org.junit.Test;
+import java.util.LinkedHashSet;
+
import static org.assertj.core.api.Assertions.assertThat;
public class OrmQueryPropertiesTest {
@@ -12,6 +14,43 @@ public class OrmQueryPropertiesTest {
return sb.toString();
}
+ @Test(expected = IllegalArgumentException.class)
+ public void construct_with_propertySet_when_null() {
+ new OrmQueryProperties(null, (LinkedHashSet)null);
+ }
+
+ @Test
+ public void construct_with_propertySet_when_empty() {
+
+ OrmQueryProperties p1 = new OrmQueryProperties(null, new LinkedHashSet());
+ assertThat(p1.getProperties()).isEqualTo("");
+ assertThat(p1.allProperties()).isFalse();
+ }
+
+ @Test
+ public void construct_with_propertySet_when_one() {
+
+ LinkedHashSet set = new LinkedHashSet();
+ set.add("name");
+ OrmQueryProperties p1 = new OrmQueryProperties(null, set);
+
+ assertThat(p1.getProperties()).isEqualTo("name");
+ assertThat(p1.allProperties()).isFalse();
+ }
+
+ @Test
+ public void construct_with_propertySet_when_some() {
+
+ LinkedHashSet set = new LinkedHashSet();
+ set.add("id");
+ set.add("name");
+ set.add("startDate");
+ OrmQueryProperties p1 = new OrmQueryProperties(null, set);
+
+ assertThat(p1.getProperties()).isEqualTo("id,name,startDate");
+ assertThat(p1.allProperties()).isFalse();
+ }
+
@Test
public void append_when_empty() {
diff --git a/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java b/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java
index b4ce33d75..9f60c8c51 100644
--- a/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java
+++ b/src/test/java/com/avaje/tests/query/TestQueryFindIterate.java
@@ -13,6 +13,7 @@ import com.avaje.tests.model.basic.ResetBasicData;
import java.util.List;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -150,8 +151,8 @@ public class TestQueryFindIterate extends BaseTestCase {
List loggedSql = LoggedSqlCollector.stop();
assertEquals("Got SQL: "+loggedSql, 2, loggedSql.size());
- assertTrue(loggedSql.get(0).contains("select t0.id c0, t0.status c1, t0.order_date c2, t1.id c3, t1.name c4, t2.id c5, t2.order_qty c6, t2.ship_qty"));
- assertTrue(loggedSql.get(1).contains("select t0.order_id c0, t0.id c1, t0.ship_time c2, t0.cretime c3, t0.updtime c4, t0.version c5, t0.order_id c6 from or_order_ship"));
+ assertThat(loggedSql.get(0)).contains("select t0.id c0, t0.status c1, t0.order_date c2, t1.id c3, t1.name c4, t2.id c5, t2.order_qty c6, t2.ship_qty");
+ assertThat(loggedSql.get(1)).contains("select t0.order_id c0, t0.id c1, t0.ship_time c2, t0.cretime c3, t0.updtime c4, t0.version c5, t0.order_id c6 from or_order_ship");
}
@Test(expected=PersistenceException.class)