mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#566 - Refactor internals - Initial tidy on OrmQueryDetail and OrmQueryDetailProperties
This commit is contained in:
@@ -92,7 +92,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Get the properties for a given path.
|
||||
*/
|
||||
public Set<String> get(String path) {
|
||||
public LinkedHashSet<String> get(String path) {
|
||||
Props props = pathMap.get(path);
|
||||
return props == null ? null : props.getProperties();
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Set the properties for a given path.
|
||||
*/
|
||||
public void put(String path, Set<String> properties) {
|
||||
public void put(String path, LinkedHashSet<String> properties) {
|
||||
pathMap.put(path, new Props(this, null, path, properties));
|
||||
}
|
||||
|
||||
@@ -160,9 +160,9 @@ public class PathProperties {
|
||||
private final String parentPath;
|
||||
private final String path;
|
||||
|
||||
private final Set<String> propSet;
|
||||
private final LinkedHashSet<String> propSet;
|
||||
|
||||
private Props(PathProperties owner, String parentPath, String path, Set<String> propSet) {
|
||||
private Props(PathProperties owner, String parentPath, String path, LinkedHashSet<String> propSet) {
|
||||
this.owner = owner;
|
||||
this.path = path;
|
||||
this.parentPath = parentPath;
|
||||
@@ -195,7 +195,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Return the properties for this property set.
|
||||
*/
|
||||
public Set<String> getProperties() {
|
||||
public LinkedHashSet<String> getProperties() {
|
||||
return propSet;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
@@ -33,6 +35,21 @@ public class HashQueryPlanBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the set with order being important.
|
||||
*/
|
||||
public HashQueryPlanBuilder addOrdered(Set<?> set) {
|
||||
if (set == null) {
|
||||
add(false);
|
||||
} else {
|
||||
add(true);
|
||||
for (Object o : set) {
|
||||
add(o);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
|
||||
@@ -1038,7 +1038,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return findId(query, t);
|
||||
}
|
||||
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(type);
|
||||
|
||||
@@ -73,9 +73,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@@ -347,7 +347,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
private final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
|
||||
private final String defaultSelectClause;
|
||||
private final Set<String> defaultSelectClauseSet;
|
||||
private final LinkedHashSet<String> defaultSelectClauseSet;
|
||||
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
@@ -856,7 +856,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
/**
|
||||
* Return the default select clause already parsed into an ordered Set.
|
||||
*/
|
||||
public Set<String> getDefaultSelectClauseSet() {
|
||||
public LinkedHashSet<String> getDefaultSelectClauseSet() {
|
||||
return defaultSelectClauseSet;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
@@ -763,7 +762,7 @@ public class DeployBeanDescriptor<T> {
|
||||
/**
|
||||
* Parse the include separating by comma or semicolon.
|
||||
*/
|
||||
public Set<String> parseDefaultSelectClause(String rawList) {
|
||||
public LinkedHashSet<String> parseDefaultSelectClause(String rawList) {
|
||||
|
||||
if (rawList == null) {
|
||||
return null;
|
||||
@@ -780,7 +779,7 @@ public class DeployBeanDescriptor<T> {
|
||||
set.add(temp);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Utility to help isSame methods.
|
||||
*/
|
||||
@@ -19,6 +22,26 @@ public class Same {
|
||||
return v1 == null ? v2 == null : v1.equals(v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if both collections are the same by value and order is taken into account.
|
||||
*/
|
||||
public static boolean sameByValue(Collection<?> v1, Collection<?> v2) {
|
||||
if (v1 == null) {
|
||||
return v2 == null;
|
||||
}
|
||||
if (v2 == null || v1.size() != v2.size()) {
|
||||
return false;
|
||||
}
|
||||
Iterator<?> thisIt = v1.iterator();
|
||||
Iterator<?> thatIt = v2.iterator();
|
||||
while (thisIt.hasNext() && thatIt.hasNext()) {
|
||||
if (!thisIt.next().equals(thatIt.next())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null safe check by sameByValue or sameByNull based on byValue.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Generates the SQL SELECT statements taking into account the physical
|
||||
@@ -354,8 +353,7 @@ public class CQueryBuilder {
|
||||
|
||||
// transfer PathProperties into OrmQueryDetail
|
||||
for (String path : pathProps.getPaths()) {
|
||||
Set<String> props = pathProps.get(path);
|
||||
detail.getChunk(path, true).setDefaultProperties(null, props);
|
||||
detail.getChunk(path, true).setDefaultProperties(null, pathProps.get(path));
|
||||
}
|
||||
|
||||
// check if @Id property included in RawSql
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.Serializable;
|
||||
@@ -18,7 +19,6 @@ 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.
|
||||
@@ -38,6 +38,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
/**
|
||||
* Root level properties.
|
||||
*/
|
||||
@NotNull
|
||||
private OrmQueryProperties baseProps = new OrmQueryProperties();
|
||||
|
||||
/**
|
||||
@@ -60,17 +61,17 @@ public class OrmQueryDetail implements Serializable {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int queryPlanHash() {
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
queryPlanHash(builder);
|
||||
return builder.getPlanHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the hash for the query plan.
|
||||
*/
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
if (baseProps == null) {
|
||||
builder.add(false);
|
||||
} else {
|
||||
builder.add(true);
|
||||
baseProps.queryPlanHash(builder);
|
||||
}
|
||||
|
||||
baseProps.queryPlanHash(builder);
|
||||
if (fetchPaths != null) {
|
||||
for (OrmQueryProperties p : fetchPaths.values()) {
|
||||
p.queryPlanHash(builder);
|
||||
@@ -79,11 +80,10 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if equal in terms of autoTune (select and fetch).
|
||||
* Return true if the details are the same for query plan purposes.
|
||||
*/
|
||||
public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) {
|
||||
|
||||
if (!isSame(baseProps, otherDetail.baseProps)) {
|
||||
public boolean isSameByPlan(OrmQueryDetail otherDetail) {
|
||||
if (!isSameByPlan(baseProps, otherDetail.baseProps)) {
|
||||
return false;
|
||||
}
|
||||
if (fetchPaths == null) {
|
||||
@@ -92,10 +92,16 @@ public class OrmQueryDetail implements Serializable {
|
||||
if (fetchPaths.size() != otherDetail.fetchPaths.size()) {
|
||||
return false;
|
||||
}
|
||||
Set<Map.Entry<String, OrmQueryProperties>> entries = fetchPaths.entrySet();
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : entries) {
|
||||
OrmQueryProperties chunk = otherDetail.getChunk(entry.getKey(), false);
|
||||
if (!isSame(entry.getValue(), chunk)) {
|
||||
// check with ordering being important
|
||||
Iterator<Map.Entry<String, OrmQueryProperties>> thisIt = fetchPaths.entrySet().iterator();
|
||||
Iterator<Map.Entry<String, OrmQueryProperties>> thatIt = otherDetail.fetchPaths.entrySet().iterator();
|
||||
while (thisIt.hasNext() && thatIt.hasNext()) {
|
||||
Map.Entry<String, OrmQueryProperties> thisEntry = thisIt.next();
|
||||
Map.Entry<String, OrmQueryProperties> thatEntry = thatIt.next();
|
||||
if (!thisEntry.getKey().equals(thatEntry.getKey())) {
|
||||
return false;
|
||||
}
|
||||
if (!thisEntry.getValue().isSameByPlan(thatEntry.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -103,21 +109,54 @@ public class OrmQueryDetail implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isSame(OrmQueryProperties p1, OrmQueryProperties p2) {
|
||||
if (p1 == null) {
|
||||
return p2 == null;
|
||||
/**
|
||||
* Return true if equal in terms of autoTune (select and fetch without property ordering).
|
||||
*/
|
||||
public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) {
|
||||
|
||||
if (!isSameByAutoTune(baseProps, otherDetail.baseProps)) {
|
||||
return false;
|
||||
}
|
||||
return p1.isSame(p2);
|
||||
if (fetchPaths == null) {
|
||||
return otherDetail.fetchPaths == null;
|
||||
}
|
||||
if (fetchPaths.size() != otherDetail.fetchPaths.size()) {
|
||||
return false;
|
||||
}
|
||||
// check without regard to ordering
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
|
||||
OrmQueryProperties chunk = otherDetail.getChunk(entry.getKey(), false);
|
||||
if (!isSameByAutoTune(entry.getValue(), chunk)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isSameByAutoTune(OrmQueryProperties p1, OrmQueryProperties p2) {
|
||||
return p1 == null ? p2 == null : p1.isSameByAutoTune(p2);
|
||||
}
|
||||
|
||||
private boolean isSameByPlan(OrmQueryProperties p1, OrmQueryProperties p2) {
|
||||
return p1 == null ? p2 == null : p1.isSameByPlan(p2);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the detail in string form.
|
||||
*/
|
||||
public String asString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (baseProps != null) {
|
||||
sb.append("select ").append(baseProps);
|
||||
if (!baseProps.isEmpty()) {
|
||||
baseProps.append("select ", sb);
|
||||
}
|
||||
if (fetchPaths != null) {
|
||||
for (OrmQueryProperties join : fetchPaths.values()) {
|
||||
sb.append(" fetch ").append(join);
|
||||
join.append(" fetch ", sb);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -135,7 +174,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
|
||||
public boolean containsProperty(String property) {
|
||||
return baseProps == null || baseProps.isIncluded(property);
|
||||
return baseProps.isIncluded(property);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,9 +438,6 @@ public class OrmQueryDetail implements Serializable {
|
||||
public void setDefaultSelectClause(BeanDescriptor<?> desc) {
|
||||
|
||||
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
|
||||
if (baseProps == null) {
|
||||
baseProps = new OrmQueryProperties();
|
||||
}
|
||||
baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet());
|
||||
}
|
||||
|
||||
@@ -417,14 +453,14 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
|
||||
public boolean hasSelectClause() {
|
||||
return (baseProps != null && baseProps.hasSelectClause());
|
||||
return (baseProps.hasSelectClause());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query detail has neither select properties specified or any joins defined.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return fetchPaths.isEmpty() && (baseProps == null || !baseProps.hasProperties());
|
||||
return fetchPaths.isEmpty() && (!baseProps.hasProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,8 @@ public class OrmQueryDetailParser {
|
||||
|
||||
public OrmQueryDetail parse() throws PersistenceException {
|
||||
|
||||
if (parser.isEmpty()) return detail;
|
||||
|
||||
parser.nextWord();
|
||||
processInitial();
|
||||
return detail;
|
||||
@@ -155,7 +157,7 @@ public class OrmQueryDetailParser {
|
||||
private void readSelect() {
|
||||
String path = null;
|
||||
String props = parser.nextWord();
|
||||
if (props.startsWith("(")) {
|
||||
if (props != null && props.startsWith("(")) {
|
||||
props = props.substring(1, props.length() - 1);
|
||||
OrmQueryProperties base = new OrmQueryProperties(path, props);
|
||||
detail.setBase(base);
|
||||
|
||||
@@ -10,13 +10,12 @@ import com.avaje.ebeaninternal.api.SpiExpressionFactory;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.expression.FilterExpressionList;
|
||||
import com.avaje.ebeaninternal.server.expression.Same;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -32,8 +31,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
private String parentPath;
|
||||
private String path;
|
||||
|
||||
private String properties;
|
||||
|
||||
private String rawProperties;
|
||||
private String trimmedProperties;
|
||||
|
||||
/**
|
||||
@@ -57,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 Set<String> included;
|
||||
private LinkedHashSet<String> included;
|
||||
|
||||
/**
|
||||
* Included bean joins.
|
||||
@@ -92,8 +90,10 @@ public class OrmQueryProperties implements Serializable {
|
||||
this.parentPath = SplitName.parent(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for root so path (and parentPath) are null.
|
||||
*/
|
||||
public OrmQueryProperties() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,25 +137,17 @@ public class OrmQueryProperties implements Serializable {
|
||||
* This can include the +query and +lazy type hints.
|
||||
* </p>
|
||||
*/
|
||||
public void setProperties(String properties) {
|
||||
this.properties = properties;
|
||||
this.trimmedProperties = properties;
|
||||
parseProperties();
|
||||
public void setProperties(String rawProperties) {
|
||||
|
||||
if (!isAllProperties()) {
|
||||
Set<String> parsed = parseIncluded(trimmedProperties);
|
||||
if (parsed.contains("*")) {
|
||||
this.included = null;
|
||||
} else {
|
||||
this.included = parsed;
|
||||
}
|
||||
} else {
|
||||
this.included = null;
|
||||
}
|
||||
}
|
||||
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
|
||||
|
||||
private boolean isAllProperties() {
|
||||
return (trimmedProperties == null) || (trimmedProperties.length() == 0) || "*".equals(trimmedProperties);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,8 +196,8 @@ public class OrmQueryProperties implements Serializable {
|
||||
/**
|
||||
* Set the properties from deployment default FetchTypes.
|
||||
*/
|
||||
public void setDefaultProperties(String properties, Set<String> included) {
|
||||
this.properties = properties;
|
||||
public void setDefaultProperties(String properties, LinkedHashSet<String> included) {
|
||||
this.rawProperties = properties;
|
||||
this.trimmedProperties = properties;
|
||||
this.included = included;
|
||||
}
|
||||
@@ -215,7 +207,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
*/
|
||||
public void setTunedProperties(OrmQueryProperties tunedProperties) {
|
||||
if (tunedProperties.hasProperties()) {
|
||||
this.properties = tunedProperties.properties;
|
||||
this.rawProperties = tunedProperties.rawProperties;
|
||||
this.trimmedProperties = tunedProperties.trimmedProperties;
|
||||
this.included = tunedProperties.included;
|
||||
this.queryFetchBatch = Math.max(queryFetchBatch, tunedProperties.queryFetchBatch);
|
||||
@@ -264,7 +256,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
OrmQueryProperties copy = new OrmQueryProperties();
|
||||
copy.parentPath = parentPath;
|
||||
copy.path = path;
|
||||
copy.properties = properties;
|
||||
copy.rawProperties = rawProperties;
|
||||
copy.trimmedProperties = trimmedProperties;
|
||||
copy.cache = cache;
|
||||
copy.readOnly = readOnly;
|
||||
@@ -273,7 +265,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
copy.lazyFetchBatch = lazyFetchBatch;
|
||||
copy.filterMany = filterMany;
|
||||
if (included != null) {
|
||||
copy.included = new HashSet<String>(included);
|
||||
copy.included = new LinkedHashSet<String>(included);
|
||||
}
|
||||
if (includedBeanJoin != null) {
|
||||
copy.includedBeanJoin = new HashSet<String>(includedBeanJoin);
|
||||
@@ -290,17 +282,28 @@ public class OrmQueryProperties implements Serializable {
|
||||
return included != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the properties and configuration are empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return rawProperties == null || rawProperties.isEmpty();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String s = "";
|
||||
StringBuilder sb = new StringBuilder(40);
|
||||
append("", sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String append(String prefix, StringBuilder sb) {
|
||||
sb.append(prefix);
|
||||
if (path != null) {
|
||||
s += path + " ";
|
||||
sb.append(path).append(" ");
|
||||
}
|
||||
if (properties != null) {
|
||||
s += "(" + properties + ") ";
|
||||
} else if (included != null) {
|
||||
s += "(" + included + ") ";
|
||||
if (!isEmpty()) {
|
||||
sb.append("(").append(rawProperties).append(") ");
|
||||
}
|
||||
return s;
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean isChild(OrmQueryProperties possibleChild) {
|
||||
@@ -318,35 +321,22 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query plan hash.
|
||||
* Return the raw properties.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(path);
|
||||
if (properties != null) {
|
||||
builder.add(properties);
|
||||
} else {
|
||||
builder.add(included);
|
||||
}
|
||||
builder.add(filterMany != null);
|
||||
if (filterMany != null) {
|
||||
filterMany.queryPlanHash(builder);
|
||||
}
|
||||
builder.add(lazyFetchBatch);
|
||||
builder.add(queryFetchBatch);
|
||||
builder.add(queryFetchAll);
|
||||
}
|
||||
|
||||
public String getProperties() {
|
||||
return properties;
|
||||
return rawProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this has properties.
|
||||
* Return true if this has properties. Returning false means this part of the
|
||||
* path is a partial object.
|
||||
*/
|
||||
public boolean hasProperties() {
|
||||
return properties != null || included != null;
|
||||
return included != null;
|
||||
}
|
||||
|
||||
public boolean allProperties() {
|
||||
return included == null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,10 +360,6 @@ public class OrmQueryProperties implements Serializable {
|
||||
includedBeanJoin.add(propertyName);
|
||||
}
|
||||
|
||||
public boolean allProperties() {
|
||||
return included == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This excludes the bean joined properties.
|
||||
* <p>
|
||||
@@ -386,7 +372,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
return included;
|
||||
}
|
||||
|
||||
LinkedHashSet<String> temp = new LinkedHashSet<String>(secondaryQueryJoins.size() + included.size());
|
||||
LinkedHashSet<String> temp = new LinkedHashSet<String>(2 * (secondaryQueryJoins.size() + included.size()));
|
||||
temp.addAll(included);
|
||||
temp.addAll(secondaryQueryJoins);
|
||||
return temp;
|
||||
@@ -400,30 +386,10 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the properties including the bean joins. This is the set that will be used by
|
||||
* EntityBeanIntercept to determine if a property needs to be lazy loaded.
|
||||
* Return the property set.
|
||||
*/
|
||||
public Set<String> getAllIncludedProperties() {
|
||||
|
||||
if (included == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (includedBeanJoin == null && secondaryQueryJoins == null) {
|
||||
return new LinkedHashSet<String>(included);
|
||||
}
|
||||
|
||||
LinkedHashSet<String> s = new LinkedHashSet<String>(2 * (included.size() + 5));
|
||||
if (included != null) {
|
||||
s.addAll(included);
|
||||
}
|
||||
if (includedBeanJoin != null) {
|
||||
s.addAll(includedBeanJoin);
|
||||
}
|
||||
if (secondaryQueryJoins != null) {
|
||||
s.addAll(secondaryQueryJoins);
|
||||
}
|
||||
return s;
|
||||
protected Set<String> getIncluded() {
|
||||
return included;
|
||||
}
|
||||
|
||||
public boolean isIncluded(String propName) {
|
||||
@@ -480,96 +446,47 @@ public class OrmQueryProperties implements Serializable {
|
||||
return path;
|
||||
}
|
||||
|
||||
private void parseProperties() {
|
||||
if (trimmedProperties == null) {
|
||||
return;
|
||||
}
|
||||
int pos = trimmedProperties.indexOf("+readonly");
|
||||
if (pos > -1) {
|
||||
trimmedProperties = StringHelper.replaceString(trimmedProperties, "+readonly", "");
|
||||
this.readOnly = true;
|
||||
}
|
||||
pos = trimmedProperties.indexOf("+cache");
|
||||
if (pos > -1) {
|
||||
trimmedProperties = StringHelper.replaceString(trimmedProperties, "+cache", "");
|
||||
this.cache = true;
|
||||
}
|
||||
pos = trimmedProperties.indexOf("+query");
|
||||
if (pos > -1) {
|
||||
queryFetchBatch = parseBatchHint(pos, "+query");
|
||||
}
|
||||
pos = trimmedProperties.indexOf("+lazy");
|
||||
if (pos > -1) {
|
||||
lazyFetchBatch = parseBatchHint(pos, "+lazy");
|
||||
}
|
||||
|
||||
trimmedProperties = trimmedProperties.trim();
|
||||
while (trimmedProperties.startsWith(",")) {
|
||||
trimmedProperties = trimmedProperties.substring(1).trim();
|
||||
}
|
||||
}
|
||||
|
||||
private int parseBatchHint(int pos, String option) {
|
||||
|
||||
int startPos = pos + option.length();
|
||||
|
||||
int endPos = findEndPos(startPos, trimmedProperties);
|
||||
if (endPos == -1) {
|
||||
trimmedProperties = StringHelper.replaceString(trimmedProperties, option, "");
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
String batchParam = trimmedProperties.substring(startPos + 1, endPos);
|
||||
|
||||
if (endPos + 1 >= trimmedProperties.length()) {
|
||||
trimmedProperties = trimmedProperties.substring(0, pos);
|
||||
} else {
|
||||
trimmedProperties = trimmedProperties.substring(0, pos) + trimmedProperties.substring(endPos + 1);
|
||||
}
|
||||
return Integer.parseInt(batchParam);
|
||||
}
|
||||
}
|
||||
|
||||
private int findEndPos(int pos, String props) {
|
||||
|
||||
if (pos < props.length()) {
|
||||
if (props.charAt(pos) == '(') {
|
||||
int endPara = props.indexOf(')', pos + 1);
|
||||
if (endPara == -1) {
|
||||
String m = "Error could not find ')' in " + props + " after position " + pos;
|
||||
throw new RuntimeException(m);
|
||||
}
|
||||
return endPara;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the include separating by comma or semicolon.
|
||||
* Return true if the properties are the same for autoTune purposes.
|
||||
*/
|
||||
private static Set<String> parseIncluded(String rawList) {
|
||||
|
||||
String[] res = rawList.split(",");
|
||||
|
||||
LinkedHashSet<String> set = new LinkedHashSet<String>(res.length + 3);
|
||||
|
||||
String temp;
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
temp = res[i].trim();
|
||||
if (temp.length() > 0) {
|
||||
set.add(temp);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
public boolean isSame(OrmQueryProperties p2) {
|
||||
public boolean isSameByAutoTune(OrmQueryProperties p2) {
|
||||
if (included == null) {
|
||||
return p2.included == null;
|
||||
}
|
||||
return included.equals(p2.included);
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties are the same for query plan purposes.
|
||||
*/
|
||||
public boolean isSameByPlan(OrmQueryProperties p2) {
|
||||
|
||||
if (!Same.sameByValue(secondaryQueryJoins, p2.secondaryQueryJoins)) return false;
|
||||
if (!Same.sameByValue(included, p2.included)) return false;
|
||||
if (!Same.sameByNull(filterMany, p2.filterMany)) return false;
|
||||
if (filterMany != null && !filterMany.isSameByPlan(p2.filterMany)) return false;
|
||||
|
||||
return lazyFetchBatch == p2.lazyFetchBatch
|
||||
&& queryFetchBatch == p2.queryFetchBatch
|
||||
&& queryFetchAll == p2.queryFetchAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query plan hash.
|
||||
*/
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(path);
|
||||
builder.addOrdered(included);
|
||||
builder.add(secondaryQueryJoins);
|
||||
|
||||
builder.add(filterMany != null);
|
||||
if (filterMany != null) {
|
||||
filterMany.queryPlanHash(builder);
|
||||
}
|
||||
builder.add(lazyFetchBatch);
|
||||
builder.add(queryFetchBatch);
|
||||
builder.add(queryFetchAll);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* Parses the path properties string.
|
||||
*/
|
||||
public class OrmQueryPropertiesParser {
|
||||
|
||||
private static Response EMPTY = new Response();
|
||||
|
||||
/**
|
||||
* Immutable response of the parsed properties and options.
|
||||
*/
|
||||
public static class Response {
|
||||
|
||||
final boolean readOnly;
|
||||
final boolean cache;
|
||||
final int queryFetchBatch;
|
||||
final int lazyFetchBatch;
|
||||
final String properties;
|
||||
final LinkedHashSet<String> included;
|
||||
|
||||
public Response(boolean readOnly, boolean cache, int queryFetchBatch, int lazyFetchBatch, String properties, LinkedHashSet<String> included) {
|
||||
this.readOnly = readOnly;
|
||||
this.cache = cache;
|
||||
this.queryFetchBatch = queryFetchBatch;
|
||||
this.lazyFetchBatch = lazyFetchBatch;
|
||||
this.properties = properties;
|
||||
this.included = included;
|
||||
}
|
||||
|
||||
public Response() {
|
||||
this.readOnly = false;
|
||||
this.cache = false;
|
||||
this.queryFetchBatch = -1;
|
||||
this.lazyFetchBatch = -1;
|
||||
this.properties = "";
|
||||
this.included = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the path properties string returning the parsed properties and options.
|
||||
* In general it is comma delimited with some special strings like +lazy(20).
|
||||
*/
|
||||
public static Response parse(String rawProperties) {
|
||||
return new OrmQueryPropertiesParser(rawProperties).parse();
|
||||
}
|
||||
|
||||
private String inputProperties;
|
||||
|
||||
private String outputProperties = "";
|
||||
private boolean allProperties;
|
||||
private boolean readOnly;
|
||||
private boolean cache;
|
||||
private int queryFetchBatch = -1;
|
||||
private int lazyFetchBatch = -1;
|
||||
|
||||
private OrmQueryPropertiesParser(String inputProperties) {
|
||||
this.inputProperties = inputProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the raw string properties input.
|
||||
*/
|
||||
private Response parse() {
|
||||
|
||||
if (inputProperties == null || inputProperties.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
int pos = inputProperties.indexOf("+readonly");
|
||||
if (pos > -1) {
|
||||
inputProperties = StringHelper.replaceString(inputProperties, "+readonly", "");
|
||||
readOnly = true;
|
||||
}
|
||||
pos = inputProperties.indexOf("+cache");
|
||||
if (pos > -1) {
|
||||
inputProperties = StringHelper.replaceString(inputProperties, "+cache", "");
|
||||
cache = true;
|
||||
}
|
||||
pos = inputProperties.indexOf("+query");
|
||||
if (pos > -1) {
|
||||
queryFetchBatch = parseBatchHint(pos, "+query");
|
||||
}
|
||||
pos = inputProperties.indexOf("+lazy");
|
||||
if (pos > -1) {
|
||||
lazyFetchBatch = parseBatchHint(pos, "+lazy");
|
||||
}
|
||||
|
||||
LinkedHashSet<String> included = parseIncluded();
|
||||
String properties = (allProperties) ? "*" : outputProperties;
|
||||
return new Response(readOnly, cache, queryFetchBatch, lazyFetchBatch, properties, included);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the include separating by comma or semicolon.
|
||||
*/
|
||||
private LinkedHashSet<String> parseIncluded() {
|
||||
|
||||
inputProperties = inputProperties.trim();
|
||||
if (inputProperties.isEmpty()) {
|
||||
// default properties
|
||||
return null;
|
||||
}
|
||||
if (inputProperties.equals("*")) {
|
||||
// explicit all properties
|
||||
allProperties = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] res = inputProperties.split(",");
|
||||
|
||||
StringBuilder sb = new StringBuilder(70);
|
||||
LinkedHashSet<String> propertySet = new LinkedHashSet<String>(res.length * 2);
|
||||
|
||||
int count = 0;
|
||||
String temp;
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
temp = res[i].trim();
|
||||
if (temp.length() > 0) {
|
||||
if (count > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(temp);
|
||||
propertySet.add(temp);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (propertySet.isEmpty()) {
|
||||
// default properties
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertySet.contains("*")) {
|
||||
// explicit all properties
|
||||
allProperties = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// partial properties
|
||||
outputProperties = sb.toString();
|
||||
return propertySet;
|
||||
}
|
||||
|
||||
private int parseBatchHint( int pos, String option) {
|
||||
|
||||
int startPos = pos + option.length();
|
||||
int endPos = findEndPos(startPos, inputProperties);
|
||||
if (endPos == -1) {
|
||||
inputProperties = StringHelper.replaceString(inputProperties, option, "");
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
String batchParam = inputProperties.substring(startPos + 1, endPos);
|
||||
|
||||
if (endPos + 1 >= inputProperties.length()) {
|
||||
inputProperties = inputProperties.substring(0, pos);
|
||||
} else {
|
||||
inputProperties = inputProperties.substring(0, pos) + inputProperties.substring(endPos + 1);
|
||||
}
|
||||
return Integer.parseInt(batchParam);
|
||||
}
|
||||
}
|
||||
|
||||
private int findEndPos(int pos, String props) {
|
||||
|
||||
if (pos < props.length()) {
|
||||
if (props.charAt(pos) == '(') {
|
||||
int endPara = props.indexOf(')', pos + 1);
|
||||
if (endPara == -1) {
|
||||
throw new RuntimeException("Error could not find ')' in " + props + " after position " + pos);
|
||||
}
|
||||
return endPara;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@ public class SimpleTextParser {
|
||||
this.eof = oql.length();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return oql.isEmpty();
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user