No effective change - format only

This commit is contained in:
rbygrave
2014-07-09 23:51:19 +12:00
parent 1e3035c558
commit 176197513d
9 changed files with 2270 additions and 2288 deletions
File diff suppressed because it is too large Load Diff
@@ -71,8 +71,7 @@ public final class DefaultOrmUpdate<T> implements SpiUpdate<T>, Serializable {
this.name = namedUpdate.getName();
this.notifyCache = namedUpdate.isNotifyCache();
// named updates are always converted to sql as part
// of the initialisation
// named updates are always converted to sql as part of the initialisation
this.updateStatement = namedUpdate.getSqlUpdateStatement();
this.type = deriveType(updateStatement);
}
@@ -223,7 +223,5 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
return cancelled;
}
}
}
@@ -2,21 +2,21 @@ package com.avaje.ebeaninternal.server.querydefn;
public class NaturalKeyBindParam {
private final String name;
private final Object value;
public NaturalKeyBindParam(String name, Object value) {
this.name = name;
this.value = value;
}
private final String name;
public String getName() {
return name;
}
private final Object value;
public NaturalKeyBindParam(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
public Object getValue() {
return value;
}
}
@@ -27,461 +27,456 @@ import com.avaje.ebeaninternal.server.query.SplitName;
* Holds the select() and join() details of a ORM query.
* </p>
* <p>
* It is worth noting that for autoFetch a "tuned fetch info" builds an instance
* of OrmQueryDetail. Tuning a query is a matter of replacing an instance of
* this class with one that has been tuned with select() and join() set.
* It is worth noting that for autoFetch a "tuned fetch info" builds an instance of OrmQueryDetail.
* Tuning a query is a matter of replacing an instance of this class with one that has been tuned
* with select() and join() set.
* </p>
*/
public class OrmQueryDetail implements Serializable {
private static final long serialVersionUID = -2510486880141461807L;
private static final long serialVersionUID = -2510486880141461807L;
/**
* Root level properties.
*/
private OrmQueryProperties baseProps = new OrmQueryProperties();
/**
* Root level properties.
*/
private OrmQueryProperties baseProps = new OrmQueryProperties();
/**
* Contains the fetch/lazy/query joins and their properties.
*/
private LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>(8);
/**
* Contains the fetch/lazy/query joins and their properties.
*/
private LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>(8);
private LinkedHashSet<String> includes = new LinkedHashSet<String>(8);
private LinkedHashSet<String> includes = new LinkedHashSet<String>(8);
/**
* Return a deep copy of the OrmQueryDetail.
*/
public OrmQueryDetail copy() {
OrmQueryDetail copy = new OrmQueryDetail();
copy.baseProps = baseProps.copy();
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
}
copy.includes = new LinkedHashSet<String>(includes);
return copy;
/**
* Return a deep copy of the OrmQueryDetail.
*/
public OrmQueryDetail copy() {
OrmQueryDetail copy = new OrmQueryDetail();
copy.baseProps = baseProps.copy();
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
}
copy.includes = new LinkedHashSet<String>(includes);
return copy;
}
/**
* Calculate the hash for the query plan.
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
if (baseProps == null) {
builder.add(false);
} else {
builder.add(true);
baseProps.queryPlanHash(request, builder);
}
/**
* Calculate the hash for the query plan.
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
if (baseProps == null) {
builder.add(false);
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
p.queryPlanHash(request, builder);
}
}
}
/**
* Return true if equal in terms of autofetch (select and joins).
*/
public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) {
return autofetchPlanHash() == otherDetail.autofetchPlanHash();
}
/**
* Calculate the hash for the query plan.
*/
private int autofetchPlanHash() {
int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
hc = hc * 31 + p.autofetchPlanHash();
}
}
return hc;
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (baseProps != null) {
sb.append("select ").append(baseProps);
}
if (fetchPaths != null) {
for (OrmQueryProperties join : fetchPaths.values()) {
sb.append(" fetch ").append(join);
}
}
return sb.toString();
}
public int hashCode() {
throw new RuntimeException("should not use");
}
/**
* set the properties to include on the base / root entity.
*/
public void select(String columns) {
baseProps = new OrmQueryProperties(null, columns);
}
public boolean containsProperty(String property) {
if (baseProps == null) {
return true;
} else {
return baseProps.isIncluded(property);
}
}
/**
* Set the base / root query properties.
*/
public void setBase(OrmQueryProperties baseProps) {
this.baseProps = baseProps;
}
public List<OrmQueryProperties> removeSecondaryQueries() {
return removeSecondaryQueries(false);
}
public List<OrmQueryProperties> removeSecondaryLazyQueries() {
return removeSecondaryQueries(true);
}
private List<OrmQueryProperties> removeSecondaryQueries(boolean lazyQuery) {
ArrayList<String> matchingPaths = new ArrayList<String>(2);
for (OrmQueryProperties chunk : fetchPaths.values()) {
boolean match = lazyQuery ? chunk.isLazyFetch() : chunk.isQueryFetch();
if (match) {
matchingPaths.add(chunk.getPath());
}
}
if (matchingPaths.size() == 0) {
return null;
}
// sort into depth order to remove
Collections.sort(matchingPaths);
// the list of secondary queries
ArrayList<OrmQueryProperties> props = new ArrayList<OrmQueryProperties>(2);
for (int i = 0; i < matchingPaths.size(); i++) {
String path = matchingPaths.get(i);
includes.remove(path);
OrmQueryProperties secQuery = fetchPaths.remove(path);
if (secQuery == null) {
// the path has already been removed by another
// secondary query
} else {
builder.add(true);
baseProps.queryPlanHash(request, builder);
}
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
p.queryPlanHash(request, builder);
props.add(secQuery);
// remove any child properties for this path
Iterator<OrmQueryProperties> pass2It = fetchPaths.values().iterator();
while (pass2It.hasNext()) {
OrmQueryProperties pass2Prop = pass2It.next();
if (secQuery.isChild(pass2Prop)) {
// remove join to secondary query from the main query
// and add to this secondary query
pass2It.remove();
includes.remove(pass2Prop.getPath());
secQuery.add(pass2Prop);
}
}
}
}
/**
* Return true if equal in terms of autofetch (select and joins).
*/
public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) {
return autofetchPlanHash() == otherDetail.autofetchPlanHash();
// Add the secondary queries as select properties
// to the parent chunk to ensure the foreign keys
// are included in the query
for (int i = 0; i < props.size(); i++) {
String path = props.get(i).getPath();
// split into parent and property
String[] split = SplitName.split(path);
// add property to parent chunk
OrmQueryProperties chunk = getChunk(split[0], true);
chunk.addSecondaryQueryJoin(split[1]);
}
/**
* Calculate the hash for the query plan.
*/
private int autofetchPlanHash() {
return props;
}
int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) {
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
hc = hc * 31 + p.autofetchPlanHash();
}
}
boolean tuned = false;
return hc;
}
OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false);
if (tunedRoot != null && tunedRoot.hasProperties()) {
tuned = true;
baseProps.setTunedProperties(tunedRoot);
public String toString() {
StringBuilder sb = new StringBuilder();
if (baseProps != null) {
sb.append("select ").append(baseProps);
}
if (fetchPaths != null) {
for (OrmQueryProperties join : fetchPaths.values()) {
sb.append(" fetch ").append(join);
}
}
return sb.toString();
}
public int hashCode() {
throw new RuntimeException("should not use");
}
/**
* set the properties to include on the base / root entity.
*/
public void select(String columns) {
baseProps = new OrmQueryProperties(null, columns);
}
public boolean containsProperty(String property) {
if (baseProps == null) {
return true;
for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) {
OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false);
if (chunk != null) {
// set the properties to select
chunk.setTunedProperties(tunedChunk);
} else {
return baseProps.isIncluded(property);
// add a missing join
putFetchPath(tunedChunk.copy());
}
}
}
return tuned;
}
/**
* Matches a join() method of the query.
*/
public void putFetchPath(OrmQueryProperties chunk) {
String path = chunk.getPath();
fetchPaths.put(path, chunk);
includes.add(path);
}
/**
* Remove all joins and properties.
* <p>
* Typically for the row count query.
* </p>
*/
public void clear() {
includes.clear();
fetchPaths.clear();
}
/**
* Set the fetch properties and configuration for a given path.
*
* @param path
* the property to join
* @param partialProps
* the properties on the join property to include
*/
public OrmQueryProperties addFetch(String path, String partialProps, FetchConfig fetchConfig) {
OrmQueryProperties chunk = getChunk(path, true);
chunk.setProperties(partialProps);
chunk.setFetchConfig(fetchConfig);
return chunk;
}
public void sortFetchPaths(BeanDescriptor<?> d) {
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>(fetchPaths.size());
for (OrmQueryProperties p : fetchPaths.values()) {
sortFetchPaths(d, p, sorted);
}
/**
* Set the base / root query properties.
*/
public void setBase(OrmQueryProperties baseProps) {
this.baseProps = baseProps;
}
fetchPaths = sorted;
}
public List<OrmQueryProperties> removeSecondaryQueries() {
return removeSecondaryQueries(false);
}
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p,
LinkedHashMap<String, OrmQueryProperties> sorted) {
public List<OrmQueryProperties> removeSecondaryLazyQueries() {
return removeSecondaryQueries(true);
}
private List<OrmQueryProperties> removeSecondaryQueries(boolean lazyQuery) {
ArrayList<String> matchingPaths = new ArrayList<String>(2);
for (OrmQueryProperties chunk : fetchPaths.values()) {
boolean match = lazyQuery ? chunk.isLazyFetch() : chunk.isQueryFetch();
if (match) {
matchingPaths.add(chunk.getPath());
}
String path = p.getPath();
if (!sorted.containsKey(path)) {
String parentPath = p.getParentPath();
if (parentPath == null || sorted.containsKey(parentPath)) {
// off root path or parent already ahead in fetch order
sorted.put(path, p);
} else {
OrmQueryProperties parentProp = fetchPaths.get(parentPath);
if (parentProp == null) {
ElPropertyValue el = d.getElGetValue(parentPath);
if (el == null) {
String msg = "Path [" + parentPath + "] not valid from " + d.getFullName();
throw new PersistenceException(msg);
}
// add a missing parent path just fetching the Id property
BeanPropertyAssoc<?> assocOne = (BeanPropertyAssoc<?>) el.getBeanProperty();
parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty());
}
if (matchingPaths.size() == 0) {
return null;
if (parentProp != null) {
sortFetchPaths(d, parentProp, sorted);
}
sorted.put(path, p);
}
}
}
// sort into depth order to remove
Collections.sort(matchingPaths);
/**
* Convert 'fetch joins' to 'many' properties over to 'query joins'.
*/
public void convertManyFetchJoinsToQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath,
boolean allowOne, int queryBatch) {
// the list of secondary queries
ArrayList<OrmQueryProperties> props = new ArrayList<OrmQueryProperties>(2);
ArrayList<OrmQueryProperties> manyChunks = new ArrayList<OrmQueryProperties>(3);
for (int i = 0; i < matchingPaths.size(); i++) {
String path = matchingPaths.get(i);
includes.remove(path);
OrmQueryProperties secQuery = fetchPaths.remove(path);
if (secQuery == null) {
// the path has already been removed by another
// secondary query
// the name of the many fetch property if there is one
String manyFetchProperty = null;
} else {
props.add(secQuery);
// flag that is set once the many fetch property is chosen
boolean fetchJoinFirstMany = allowOne;
// remove any child properties for this path
Iterator<OrmQueryProperties> pass2It = fetchPaths.values().iterator();
while (pass2It.hasNext()) {
OrmQueryProperties pass2Prop = pass2It.next();
if (secQuery.isChild(pass2Prop)) {
// remove join to secondary query from the main query
// and add to this secondary query
pass2It.remove();
includes.remove(pass2Prop.getPath());
secQuery.add(pass2Prop);
}
}
}
sortFetchPaths(beanDescriptor);
for (String fetchPath : fetchPaths.keySet()) {
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath);
if (elProp.containsManySince(manyFetchProperty)) {
// this is a join to a *ToMany
OrmQueryProperties chunk = fetchPaths.get(fetchPath);
if (chunk.isFetchJoin() && !isLazyLoadManyRoot(lazyLoadManyPath, chunk)
&& !hasParentSecJoin(lazyLoadManyPath, chunk)) {
// this is a 'fetch join' (included in main query)
if (fetchJoinFirstMany) {
// letting the first one remain a 'fetch join'
fetchJoinFirstMany = false;
manyFetchProperty = fetchPath;
} else {
// convert this one over to a 'query join'
manyChunks.add(chunk);
}
}
// Add the secondary queries as select properties
// to the parent chunk to ensure the foreign keys
// are included in the query
for (int i = 0; i < props.size(); i++) {
String path = props.get(i).getPath();
// split into parent and property
String[] split = SplitName.split(path);
// add property to parent chunk
OrmQueryProperties chunk = getChunk(split[0], true);
chunk.addSecondaryQueryJoin(split[1]);
}
return props;
}
}
public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) {
boolean tuned = false;
OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false);
if (tunedRoot != null && tunedRoot.hasProperties()) {
tuned = true;
baseProps.setTunedProperties(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());
}
}
}
return tuned;
for (int i = 0; i < manyChunks.size(); i++) {
// convert 'fetch joins' over to 'query joins'
manyChunks.get(i).setQueryFetch(queryBatch, true);
}
}
/**
* Matches a join() method of the query.
*/
public void putFetchPath(OrmQueryProperties chunk) {
String path = chunk.getPath();
fetchPaths.put(path, chunk);
includes.add(path);
/**
* Return true if this is actually the root level of a +query/+lazy loading query.
*/
private boolean isLazyLoadManyRoot(String lazyLoadManyPath, OrmQueryProperties chunk) {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(chunk.getPath())) {
return true;
}
return false;
}
/**
* Remove all joins and properties.
* <p>
* Typically for the row count query.
* </p>
*/
public void clear() {
includes.clear();
fetchPaths.clear();
}
/**
* Set the fetch properties and configuration for a given path.
*
* @param path
* the property to join
* @param partialProps
* the properties on the join property to include
*/
public OrmQueryProperties addFetch(String path, String partialProps, FetchConfig fetchConfig) {
OrmQueryProperties chunk = getChunk(path, true);
chunk.setProperties(partialProps);
chunk.setFetchConfig(fetchConfig);
return chunk;
}
public void sortFetchPaths(BeanDescriptor<?> d) {
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>(fetchPaths.size());
for (OrmQueryProperties p : fetchPaths.values()) {
sortFetchPaths(d, p, sorted);
}
fetchPaths = sorted;
}
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p,
LinkedHashMap<String, OrmQueryProperties> sorted) {
String path = p.getPath();
if (!sorted.containsKey(path)) {
String parentPath = p.getParentPath();
if (parentPath == null || sorted.containsKey(parentPath)) {
// off root path or parent already ahead in fetch order
sorted.put(path, p);
} else {
OrmQueryProperties parentProp = fetchPaths.get(parentPath);
if (parentProp == null) {
ElPropertyValue el = d.getElGetValue(parentPath);
if (el == null) {
String msg = "Path [" + parentPath + "] not valid from " + d.getFullName();
throw new PersistenceException(msg);
}
// add a missing parent path just fetching the Id property
BeanPropertyAssoc<?> assocOne = (BeanPropertyAssoc<?>) el.getBeanProperty();
parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty());
}
if (parentProp != null) {
sortFetchPaths(d, parentProp, sorted);
}
sorted.put(path, p);
}
}
}
/**
* Convert 'fetch joins' to 'many' properties over to 'query joins'.
*/
public void convertManyFetchJoinsToQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath,
boolean allowOne, int queryBatch) {
ArrayList<OrmQueryProperties> manyChunks = new ArrayList<OrmQueryProperties>(3);
// the name of the many fetch property if there is one
String manyFetchProperty = null;
// flag that is set once the many fetch property is chosen
boolean fetchJoinFirstMany = allowOne;
sortFetchPaths(beanDescriptor);
for (String fetchPath : fetchPaths.keySet()) {
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath);
if (elProp.containsManySince(manyFetchProperty)) {
// this is a join to a *ToMany
OrmQueryProperties chunk = fetchPaths.get(fetchPath);
if (chunk.isFetchJoin()
&& !isLazyLoadManyRoot(lazyLoadManyPath, chunk)
&& !hasParentSecJoin(lazyLoadManyPath, chunk)) {
// this is a 'fetch join' (included in main query)
if (fetchJoinFirstMany) {
// letting the first one remain a 'fetch join'
fetchJoinFirstMany = false;
manyFetchProperty = fetchPath;
} else {
// convert this one over to a 'query join'
manyChunks.add(chunk);
}
}
}
}
for (int i = 0; i < manyChunks.size(); i++) {
// convert 'fetch joins' over to 'query joins'
manyChunks.get(i).setQueryFetch(queryBatch, true);
}
}
/**
* Return true if this is actually the root level of a +query/+lazy loading
* query.
*/
private boolean isLazyLoadManyRoot(String lazyLoadManyPath, OrmQueryProperties chunk) {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(chunk.getPath())) {
return true;
}
/**
* If the chunk has a parent that is a query or lazy join. In this case it does not need to be
* converted.
*/
private boolean hasParentSecJoin(String lazyLoadManyPath, OrmQueryProperties chunk) {
OrmQueryProperties parent = getParent(chunk);
if (parent == null) {
return false;
} else {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(parent.getPath())) {
return false;
} else if (!parent.isFetchJoin()) {
return true;
} else {
return hasParentSecJoin(lazyLoadManyPath, parent);
}
}
}
/**
* Return the parent chunk.
*/
private OrmQueryProperties getParent(OrmQueryProperties chunk) {
String parentPath = chunk.getParentPath();
return parentPath == null ? null : fetchPaths.get(parentPath);
}
/**
* Set any default select clauses for the main bean and any joins that have not explicitly defined
* a select clause.
* <p>
* That is this will use FetchType.LAZY to exclude some properties by default.
* </p>
*/
public void setDefaultSelectClause(BeanDescriptor<?> desc) {
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
if (baseProps == null) {
baseProps = new OrmQueryProperties();
}
baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet());
}
/**
* If the chunk has a parent that is a query or lazy join. In this case it
* does not need to be converted.
*/
private boolean hasParentSecJoin(String lazyLoadManyPath, OrmQueryProperties chunk) {
OrmQueryProperties parent = getParent(chunk);
if (parent == null) {
return false;
} else {
if (lazyLoadManyPath != null && lazyLoadManyPath.equals(parent.getPath())) {
return false;
} else if (!parent.isFetchJoin()) {
return true;
} else {
return hasParentSecJoin(lazyLoadManyPath, parent);
}
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());
}
}
}
}
/**
* Return the parent chunk.
*/
private OrmQueryProperties getParent(OrmQueryProperties chunk) {
String parentPath = chunk.getParentPath();
return parentPath == null ? null : fetchPaths.get(parentPath);
public boolean hasSelectClause() {
return (baseProps != null && 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 true if there are no joins.
*/
public boolean isJoinsEmpty() {
return fetchPaths.isEmpty();
}
/**
* Add the explicit bean join.
* <p>
* This is also used to Exclude the matching property from the parent select (aka remove the
* foreign key) because it is now included in it's on node in the SqlTree.
* </p>
*/
public void includeBeanJoin(String parentPath, String propertyName) {
OrmQueryProperties parentChunk = getChunk(parentPath, true);
parentChunk.includeBeanJoin(propertyName);
}
public OrmQueryProperties getChunk(String path, boolean create) {
if (path == null) {
return baseProps;
}
OrmQueryProperties props = fetchPaths.get(path);
if (create && props == null) {
props = new OrmQueryProperties(path);
putFetchPath(props);
return props;
/**
* Set any default select clauses for the main bean and any joins that have
* not explicitly defined a select clause.
* <p>
* That is this will use FetchType.LAZY to exclude some properties by
* default.
* </p>
*/
public void setDefaultSelectClause(BeanDescriptor<?> desc) {
if (desc.hasDefaultSelectClause() && !hasSelectClause()) {
if (baseProps == null) {
baseProps = new OrmQueryProperties();
}
baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet());
}
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());
}
}
}
} else {
return props;
}
}
public boolean hasSelectClause() {
return (baseProps != null && baseProps.hasSelectClause());
}
/**
* Return true if the property is included.
*/
public boolean includes(String path) {
/**
* 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());
}
OrmQueryProperties chunk = fetchPaths.get(path);
/**
* Return true if there are no joins.
*/
public boolean isJoinsEmpty() {
return fetchPaths.isEmpty();
}
// may not have fetch properties if just +cache etc
return chunk != null && !chunk.isCache();
}
/**
* Add the explicit bean join.
* <p>
* This is also used to Exclude the matching property from the parent select
* (aka remove the foreign key) because it is now included in it's on node
* in the SqlTree.
* </p>
*/
public void includeBeanJoin(String parentPath, String propertyName) {
OrmQueryProperties parentChunk = getChunk(parentPath, true);
parentChunk.includeBeanJoin(propertyName);
}
public OrmQueryProperties getChunk(String path, boolean create) {
if (path == null) {
return baseProps;
}
OrmQueryProperties props = fetchPaths.get(path);
if (create && props == null) {
props = new OrmQueryProperties(path);
putFetchPath(props);
return props;
} else {
return props;
}
}
/**
* Return true if the property is included.
*/
public boolean includes(String path) {
OrmQueryProperties chunk = fetchPaths.get(path);
// may not have fetch properties if just +cache etc
return chunk != null && !chunk.isCache();
}
/**
* Return the property includes for this detail.
*/
public HashSet<String> getIncludes() {
return includes;
}
/**
* Return the property includes for this detail.
*/
public HashSet<String> getIncludes() {
return includes;
}
}
@@ -3,198 +3,196 @@ package com.avaje.ebeaninternal.server.querydefn;
import javax.persistence.PersistenceException;
/**
* Parses a Object relational query statement into a OrmQueryDetail and
* OrmQueryAttributes.
* Parses a Object relational query statement into a OrmQueryDetail and OrmQueryAttributes.
* <p>
* The reason they are split into detail and attributes is that the autoFetch
* feature is used to replace the OrmQueryDetail leaving the attributes
* unchanged.
* The reason they are split into detail and attributes is that the autoFetch feature is used to
* replace the OrmQueryDetail leaving the attributes unchanged.
* </p>
*/
public class OrmQueryDetailParser {
private final OrmQueryDetail detail = new OrmQueryDetail();
private final OrmQueryDetail detail = new OrmQueryDetail();
private int maxRows;
private int maxRows;
private int firstRow;
private int firstRow;
private String rawWhereClause;
private String rawWhereClause;
private String rawOrderBy;
private String rawOrderBy;
private final SimpleTextParser parser;
private final SimpleTextParser parser;
public OrmQueryDetailParser(String oql) {
this.parser = new SimpleTextParser(oql);
public OrmQueryDetailParser(String oql) {
this.parser = new SimpleTextParser(oql);
}
public void parse() throws PersistenceException {
parser.nextWord();
processInitial();
}
protected void assign(DefaultOrmQuery<?> query) {
query.setOrmQueryDetail(detail);
query.setFirstRow(firstRow);
query.setMaxRows(maxRows);
query.setRawWhereClause(rawWhereClause);
query.order(rawOrderBy);
}
private void processInitial() {
if (parser.isMatch("find")) {
OrmQueryProperties props = readFindFetch();
detail.setBase(props);
} else {
process();
}
while (!parser.isFinished()) {
process();
}
}
public void parse() throws PersistenceException {
private boolean isFetch() {
return parser.isMatch("fetch") || parser.isMatch("join");
}
private void process() {
if (isFetch()) {
OrmQueryProperties props = readFindFetch();
detail.putFetchPath(props);
} else if (parser.isMatch("where")) {
readWhere();
} else if (parser.isMatch("order", "by")) {
readOrderBy();
} else if (parser.isMatch("limit")) {
readLimit();
} else {
throw new PersistenceException("Query expected 'fetch', 'where','order by' or 'limit' keyword but got ["
+ parser.getWord() + "] \r " + parser.getOql());
}
}
private void readLimit() {
try {
String maxLimit = parser.nextWord();
maxRows = Integer.parseInt(maxLimit);
String offsetKeyword = parser.nextWord();
if (offsetKeyword != null) {
if (!parser.isMatch("offset")) {
throw new PersistenceException("expected offset keyword but got " + parser.getWord());
}
String firstRowLimit = parser.nextWord();
firstRow = Integer.parseInt(firstRowLimit);
parser.nextWord();
processInitial();
}
} catch (NumberFormatException e) {
String msg = "Expected an integer for maxRows or firstRows in limit offset clause";
throw new PersistenceException(msg, e);
}
}
protected void assign(DefaultOrmQuery<?> query) {
query.setOrmQueryDetail(detail);
query.setFirstRow(firstRow);
query.setMaxRows(maxRows);
query.setRawWhereClause(rawWhereClause);
query.order(rawOrderBy);
}
private void readOrderBy() {
// read the by
parser.nextWord();
private void processInitial() {
if (parser.isMatch("find")) {
OrmQueryProperties props = readFindFetch();
detail.setBase(props);
} else {
process();
}
while (!parser.isFinished()) {
process();
StringBuilder sb = new StringBuilder();
while (parser.nextWord() != null) {
if (parser.isMatch("limit")) {
break;
} else {
String w = parser.getWord();
if (!w.startsWith("(")) {
sb.append(" ");
}
sb.append(w);
}
}
rawOrderBy = sb.toString().trim();
if (!parser.isFinished()) {
readLimit();
}
}
private void readWhere() {
int nextMode = 0;
StringBuilder sb = new StringBuilder();
while ((parser.nextWord()) != null) {
if (parser.isMatch("order", "by")) {
nextMode = 1;
break;
} else if (parser.isMatch("limit")) {
nextMode = 2;
break;
} else {
sb.append(" ").append(parser.getWord());
}
}
String whereClause = sb.toString().trim();
if (whereClause.length() > 0) {
rawWhereClause = whereClause;
}
private boolean isFetch() {
return parser.isMatch("fetch") || parser.isMatch("join");
if (nextMode == 1) {
readOrderBy();
} else if (nextMode == 2) {
readLimit();
}
}
private void process() {
if (isFetch()) {
OrmQueryProperties props = readFindFetch();
detail.putFetchPath(props);
private OrmQueryProperties readFindFetch() {
} else if (parser.isMatch("where")) {
readWhere();
boolean readAlias = false;
} else if (parser.isMatch("order", "by")) {
readOrderBy();
} else if (parser.isMatch("limit")) {
readLimit();
} else {
throw new PersistenceException("Query expected 'fetch', 'where','order by' or 'limit' keyword but got ["
+ parser.getWord() + "] \r " + parser.getOql());
}
}
private void readLimit() {
try {
String maxLimit = parser.nextWord();
maxRows = Integer.parseInt(maxLimit);
String offsetKeyword = parser.nextWord();
if (offsetKeyword != null) {
if (!parser.isMatch("offset")) {
throw new PersistenceException("expected offset keyword but got " + parser.getWord());
}
String firstRowLimit = parser.nextWord();
firstRow = Integer.parseInt(firstRowLimit);
parser.nextWord();
}
} catch (NumberFormatException e) {
String msg = "Expected an integer for maxRows or firstRows in limit offset clause";
throw new PersistenceException(msg, e);
}
}
private void readOrderBy() {
// read the by
String props = null;
String path = parser.nextWord();
String token = null;
while ((token = parser.nextWord()) != null) {
if (!readAlias && parser.isMatch("as")) {
// next token is alias
parser.nextWord();
readAlias = true;
StringBuilder sb = new StringBuilder();
while (parser.nextWord() != null) {
if (parser.isMatch("limit")) {
break;
} else {
String w = parser.getWord();
if (!w.startsWith("(")) {
sb.append(" ");
}
sb.append(w);
}
}
rawOrderBy = sb.toString().trim();
} else if ('(' == token.charAt(0)) {
props = token;
parser.nextWord();
break;
if (!parser.isFinished()) {
readLimit();
}
} else if (isFindFetchEnd()) {
break;
} else if (!readAlias) {
readAlias = true;
} else {
throw new PersistenceException("Expected (props) or new 'fetch' 'where' but got " + token);
}
}
private void readWhere() {
int nextMode = 0;
StringBuilder sb = new StringBuilder();
while ((parser.nextWord()) != null) {
if (parser.isMatch("order", "by")) {
nextMode = 1;
break;
} else if (parser.isMatch("limit")) {
nextMode = 2;
break;
} else {
sb.append(" ").append(parser.getWord());
}
}
String whereClause = sb.toString().trim();
if (whereClause.length() > 0) {
rawWhereClause = whereClause;
}
if (nextMode == 1) {
readOrderBy();
} else if (nextMode == 2) {
readLimit();
}
if (props != null) {
props = props.substring(1, props.length() - 1);
}
return new OrmQueryProperties(path, props);
}
private OrmQueryProperties readFindFetch() {
boolean readAlias = false;
String props = null;
String path = parser.nextWord();
String token = null;
while ((token = parser.nextWord()) != null) {
if (!readAlias && parser.isMatch("as")) {
// next token is alias
parser.nextWord();
readAlias = true;
} else if ('(' == token.charAt(0)) {
props = token;
parser.nextWord();
break;
} else if (isFindFetchEnd()) {
break;
} else if (!readAlias) {
readAlias = true;
} else {
throw new PersistenceException("Expected (props) or new 'fetch' 'where' but got " + token);
}
}
if (props != null) {
props = props.substring(1, props.length() - 1);
}
return new OrmQueryProperties(path, props);
private boolean isFindFetchEnd() {
if (isFetch()) {
return true;
}
private boolean isFindFetchEnd() {
if (isFetch()) {
return true;
}
if (parser.isMatch("where")) {
return true;
}
if (parser.isMatch("order", "by")) {
return true;
}
return false;
if (parser.isMatch("where")) {
return true;
}
if (parser.isMatch("order", "by")) {
return true;
}
return false;
}
}
@@ -6,40 +6,40 @@ import com.avaje.ebeaninternal.api.SpiQuery;
public class OrmQueryLimitRequest implements SqlLimitRequest {
private final SpiQuery<?> ormQuery;
private final SpiQuery<?> ormQuery;
private final DatabasePlatform dbPlatform;
private final String sql;
private final String sqlOrderBy;
private final String sql;
private final String sqlOrderBy;
public OrmQueryLimitRequest(String sql, String sqlOrderBy, SpiQuery<?> ormQuery, DatabasePlatform dbPlatform) {
this.sql = sql;
this.sqlOrderBy = sqlOrderBy;
this.ormQuery = ormQuery;
this.dbPlatform = dbPlatform;
}
public String getDbOrderBy() {
return sqlOrderBy;
}
public String getDbSql() {
return sql;
}
public String getDbOrderBy() {
return sqlOrderBy;
}
public int getFirstRow() {
return ormQuery.getFirstRow();
}
public String getDbSql() {
return sql;
}
public int getMaxRows() {
return ormQuery.getMaxRows();
}
public int getFirstRow() {
return ormQuery.getFirstRow();
}
public boolean isDistinct() {
return ormQuery.isDistinctQuery();
}
public int getMaxRows() {
return ormQuery.getMaxRows();
}
public boolean isDistinct() {
return ormQuery.isDistinctQuery();
}
public SpiQuery<?> getOrmQuery() {
return ormQuery;
File diff suppressed because it is too large Load Diff
@@ -2,176 +2,176 @@ package com.avaje.ebeaninternal.server.querydefn;
public class SimpleTextParser {
private final String oql;
private final char[] chars;
private final int eof;
private final String oql;
private final char[] chars;
private final int eof;
private int pos;
private String word;
private String lowerWord;
private int pos;
private String word;
private String lowerWord;
private int openParenthesisCount;
private int openParenthesisCount;
public SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
}
public SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
}
public int getPos() {
public int getPos() {
return pos;
}
public String getOql() {
return oql;
}
return oql;
}
public String getWord() {
return word;
}
public String getWord() {
return word;
}
public String peekNextWord() {
int origPos = pos;
String nw = nextWordInternal();
pos = origPos;
return nw;
}
public String peekNextWord() {
int origPos = pos;
String nw = nextWordInternal();
pos = origPos;
return nw;
}
/**
* Match the current and the next word.
*/
public boolean isMatch(String lowerMatch, String nextWordMatch) {
/**
* Match the current and the next word.
*/
public boolean isMatch(String lowerMatch, String nextWordMatch) {
if (isMatch(lowerMatch)) {
String nw = peekNextWord();
if (nw != null) {
nw = nw.toLowerCase();
return nw.equals(nextWordMatch);
}
}
return false;
}
if (isMatch(lowerMatch)) {
String nw = peekNextWord();
if (nw != null) {
nw = nw.toLowerCase();
return nw.equals(nextWordMatch);
}
}
return false;
}
public boolean isFinished() {
return word == null;
}
public boolean isFinished() {
return word == null;
}
public int findWordLower(String lowerMatch, int afterPos) {
this.pos = afterPos;
return findWordLower(lowerMatch);
}
public int findWordLower(String lowerMatch, int afterPos) {
this.pos = afterPos;
return findWordLower(lowerMatch);
}
public int findWordLower(String lowerMatch) {
do {
if (nextWord() == null) {
return -1;
}
if (lowerMatch.equals(lowerWord)) {
return pos - lowerWord.length();
}
} while (true);
}
public int findWordLower(String lowerMatch) {
do {
if (nextWord() == null) {
return -1;
}
if (lowerMatch.equals(lowerWord)) {
return pos - lowerWord.length();
}
} while (true);
}
/**
* Match the current word.
*/
public boolean isMatch(String lowerMatch) {
return lowerMatch.equals(lowerWord);
}
/**
* Match the current word.
*/
public boolean isMatch(String lowerMatch) {
return lowerMatch.equals(lowerWord);
}
public String nextWord() {
word = nextWordInternal();
if (word != null) {
lowerWord = word.toLowerCase();
}
return word;
}
public String nextWord() {
word = nextWordInternal();
if (word != null) {
lowerWord = word.toLowerCase();
}
return word;
}
private String nextWordInternal() {
trimLeadingWhitespace();
if (pos >= eof) {
return null;
}
int start = pos;
if (chars[pos] == '(') {
moveToClose();
} else {
moveToEndOfWord();
}
return oql.substring(start, pos);
}
private String nextWordInternal() {
trimLeadingWhitespace();
if (pos >= eof) {
return null;
}
int start = pos;
if (chars[pos] == '(') {
moveToClose();
} else {
moveToEndOfWord();
}
return oql.substring(start, pos);
}
private void moveToClose() {
private void moveToClose() {
pos++;
openParenthesisCount = 0;
pos++;
openParenthesisCount = 0;
for (; pos < eof; pos++) {
char c = chars[pos];
if (c == '(') {
// count nested parenthesis
openParenthesisCount++;
for (; pos < eof; pos++) {
char c = chars[pos];
if (c == '(') {
// count nested parenthesis
openParenthesisCount++;
} else if (c == ')') {
if (openParenthesisCount > 0) {
// still in nested parenthesis
--openParenthesisCount;
} else {
// we have found the end
pos++;
return;
}
}
}
}
} else if (c == ')') {
if (openParenthesisCount > 0) {
// still in nested parenthesis
--openParenthesisCount;
} else {
// we have found the end
pos++;
return;
}
}
}
}
private void moveToEndOfWord() {
char c = chars[pos];
boolean isOperator = isOperator(c);
for (; pos < eof; pos++) {
c = chars[pos];
if (isWordTerminator(c, isOperator)) {
return;
}
}
}
private void moveToEndOfWord() {
char c = chars[pos];
boolean isOperator = isOperator(c);
for (; pos < eof; pos++) {
c = chars[pos];
if (isWordTerminator(c, isOperator)) {
return;
}
}
}
private boolean isWordTerminator(char c, boolean isOperator) {
if (Character.isWhitespace(c)) {
return true;
}
if (isOperator(c)) {
return !isOperator;
}
if (c == '('){
return true;
}
private boolean isWordTerminator(char c, boolean isOperator) {
if (Character.isWhitespace(c)) {
return true;
}
if (isOperator(c)) {
return !isOperator;
}
if (c == '(') {
return true;
}
return isOperator;
}
return isOperator;
}
private boolean isOperator(char c) {
switch (c) {
case '<':
return true;
case '>':
return true;
case '=':
return true;
case '!':
return true;
private boolean isOperator(char c) {
switch (c) {
case '<':
return true;
case '>':
return true;
case '=':
return true;
case '!':
return true;
default:
return false;
}
}
default:
return false;
}
}
private void trimLeadingWhitespace() {
for (; pos < eof; pos++) {
char c = chars[pos];
if (!Character.isWhitespace(c)) {
break;
}
}
}
private void trimLeadingWhitespace() {
for (; pos < eof; pos++) {
char c = chars[pos];
if (!Character.isWhitespace(c)) {
break;
}
}
}
}