mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
No effective change - format only
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
|
||||
+159
-161
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user