mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76033385dc | ||
|
|
b61cd30e29 | ||
|
|
18edaf877b | ||
|
|
f055a0d0cb | ||
|
|
a22574b0c9 | ||
|
|
3130f751f5 | ||
|
|
c83606c1da | ||
|
|
439858b259 | ||
|
|
3d3948aa8b | ||
|
|
27045baf44 | ||
|
|
5c6d6608ef | ||
|
|
2fcaab44c6 | ||
|
|
69bf1758bc | ||
|
|
af554410fd | ||
|
|
ae7827c483 | ||
|
|
4df2d4020e | ||
|
|
1aeffd31e0 | ||
|
|
5c59066ff8 | ||
|
|
a2bf8a0e46 | ||
|
|
3ef77ea410 | ||
|
|
fe2c2e5331 | ||
|
|
3d91a4f927 | ||
|
|
165d4aca92 | ||
|
|
076f8a03b9 | ||
|
|
ca944eb756 | ||
|
|
769e4bb488 | ||
|
|
0d456b2ad6 | ||
|
|
39e913cbe2 | ||
|
|
ae38672e02 | ||
|
|
8a8b36ec1e | ||
|
|
62aca1e77b | ||
|
|
f0b80068b6 | ||
|
|
5721fe3807 | ||
|
|
afffb88a29 | ||
|
|
42b112c3d7 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.17.2</version>
|
||||
<version>6.18.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
|
||||
@@ -71,7 +71,7 @@ class DRawSqlParser {
|
||||
|
||||
preFrom = trimSelectKeyword(preFrom);
|
||||
|
||||
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
||||
return new Sql(sql, preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -249,4 +249,22 @@ public class FetchConfig implements Serializable {
|
||||
return queryAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FetchConfig that = (FetchConfig) o;
|
||||
if (lazyBatchSize != that.lazyBatchSize) return false;
|
||||
if (queryBatchSize != that.queryBatchSize) return false;
|
||||
return queryAll == that.queryAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = lazyBatchSize;
|
||||
result = 92821 * result + queryBatchSize;
|
||||
result = 92821 * result + (queryAll ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,14 @@ public final class RawSql implements Serializable {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key;
|
||||
*/
|
||||
public Key getKey() {
|
||||
boolean parsed = sql != null && sql.parsed;
|
||||
String unParsedSql = (sql == null) ? "" : sql.unparsedSql;
|
||||
return new Key(parsed, unParsedSql, columnMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resultSet if this is a ResultSet based RawSql.
|
||||
@@ -221,16 +229,6 @@ public final class RawSql implements Serializable {
|
||||
return columnMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash for this query.
|
||||
*/
|
||||
public int queryHash() {
|
||||
if (resultSet != null) {
|
||||
return 31 * columnMapping.queryHash();
|
||||
}
|
||||
return 31 * sql.queryHash() + columnMapping.queryHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the sql part of the query. For parsed RawSql the sql is broken
|
||||
* up so that Ebean can insert extra WHERE and HAVING expressions into the
|
||||
@@ -260,13 +258,10 @@ public final class RawSql implements Serializable {
|
||||
|
||||
private final boolean distinct;
|
||||
|
||||
private final int queryHashCode;
|
||||
|
||||
/**
|
||||
* Construct for unparsed SQL.
|
||||
*/
|
||||
protected Sql(String unparsedSql) {
|
||||
this.queryHashCode = unparsedSql.hashCode();
|
||||
this.parsed = false;
|
||||
this.unparsedSql = unparsedSql;
|
||||
this.preFrom = null;
|
||||
@@ -282,12 +277,11 @@ public final class RawSql implements Serializable {
|
||||
/**
|
||||
* Construct for parsed SQL.
|
||||
*/
|
||||
protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr,
|
||||
protected Sql(String unparsedSql, String preFrom, String preWhere, boolean andWhereExpr,
|
||||
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
|
||||
|
||||
this.queryHashCode = queryHashCode;
|
||||
this.unparsedSql = unparsedSql;
|
||||
this.parsed = true;
|
||||
this.unparsedSql = null;
|
||||
this.preFrom = preFrom;
|
||||
this.preHaving = preHaving;
|
||||
this.preWhere = preWhere;
|
||||
@@ -298,13 +292,6 @@ public final class RawSql implements Serializable {
|
||||
this.distinct = distinct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for this query.
|
||||
*/
|
||||
public int queryHash() {
|
||||
return queryHashCode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (!parsed) {
|
||||
return "unparsed[" + unparsedSql + "]";
|
||||
@@ -406,13 +393,10 @@ public final class RawSql implements Serializable {
|
||||
|
||||
private final boolean immutable;
|
||||
|
||||
private final int queryHashCode;
|
||||
|
||||
/**
|
||||
* Construct from parsed sql where the columns have been identified.
|
||||
*/
|
||||
protected ColumnMapping(List<Column> columns) {
|
||||
this.queryHashCode = 0;
|
||||
this.immutable = false;
|
||||
this.parsed = true;
|
||||
this.propertyMap = null;
|
||||
@@ -428,7 +412,6 @@ public final class RawSql implements Serializable {
|
||||
* Construct for unparsed sql.
|
||||
*/
|
||||
protected ColumnMapping() {
|
||||
this.queryHashCode = 0;
|
||||
this.immutable = false;
|
||||
this.parsed = false;
|
||||
this.propertyMap = null;
|
||||
@@ -443,17 +426,13 @@ public final class RawSql implements Serializable {
|
||||
this.immutable = false;
|
||||
this.parsed = false;
|
||||
this.propertyMap = null;
|
||||
//this.propertyColumnMap = null;
|
||||
this.dbColumnMap = new LinkedHashMap<String, Column>();
|
||||
|
||||
int hc = 31;
|
||||
|
||||
int pos = 0;
|
||||
for (String prop : propertyNames) {
|
||||
hc = 31 * hc + prop.hashCode();
|
||||
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
|
||||
}
|
||||
propertyColumnMap = dbColumnMap;
|
||||
this.queryHashCode = hc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,20 +443,28 @@ public final class RawSql implements Serializable {
|
||||
this.parsed = parsed;
|
||||
this.dbColumnMap = dbColumnMap;
|
||||
|
||||
int hc = ColumnMapping.class.getName().hashCode();
|
||||
|
||||
HashMap<String, Column> pcMap = new HashMap<String, Column>();
|
||||
HashMap<String, String> pMap = new HashMap<String, String>();
|
||||
|
||||
for (Column c : dbColumnMap.values()) {
|
||||
pMap.put(c.getPropertyName(), c.getDbColumn());
|
||||
pcMap.put(c.getPropertyName(), c);
|
||||
hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode());
|
||||
hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode());
|
||||
}
|
||||
this.propertyMap = Collections.unmodifiableMap(pMap);
|
||||
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
|
||||
this.queryHashCode = hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ColumnMapping that = (ColumnMapping) o;
|
||||
return dbColumnMap.equals(that.dbColumnMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return dbColumnMap.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,16 +508,6 @@ public final class RawSql implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query hash for this column mapping.
|
||||
*/
|
||||
public int queryHash() {
|
||||
if (queryHashCode == 0) {
|
||||
throw new RuntimeException("Bug: queryHashCode == 0");
|
||||
}
|
||||
return queryHashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Columns where supplied by parsing the sql select
|
||||
* clause.
|
||||
@@ -648,6 +625,27 @@ public final class RawSql implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Column that = (Column) o;
|
||||
if (indexPos != that.indexPos) return false;
|
||||
if (!dbColumn.equals(that.dbColumn)) return false;
|
||||
if (dbAlias != null ? !dbAlias.equals(that.dbAlias) : that.dbAlias != null) return false;
|
||||
return propertyName != null ? propertyName.equals(that.propertyName) : that.propertyName == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = indexPos;
|
||||
result = 31 * result + dbColumn.hashCode();
|
||||
result = 31 * result + (dbAlias != null ? dbAlias.hashCode() : 0);
|
||||
result = 31 * result + (propertyName != null ? propertyName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return dbColumn + "->" + propertyName;
|
||||
}
|
||||
@@ -699,4 +697,39 @@ public final class RawSql implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A key for the RawSql object using for the query plan.
|
||||
*/
|
||||
public static final class Key {
|
||||
|
||||
private final boolean parsed;
|
||||
private final ColumnMapping columnMapping;
|
||||
private final String unParsedSql;
|
||||
|
||||
Key(boolean parsed, String unParsedSql, ColumnMapping columnMapping) {
|
||||
this.parsed = parsed;
|
||||
this.unParsedSql = unParsedSql;
|
||||
this.columnMapping = columnMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key that = (Key) o;
|
||||
return parsed == that.parsed
|
||||
&& columnMapping.equals(that.columnMapping)
|
||||
&& unParsedSql.equals(that.unParsedSql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (parsed ? 1 : 0);
|
||||
result = 31 * result + columnMapping.hashCode();
|
||||
result = 31 * result + unParsedSql.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
void internalAdd(Object bean);
|
||||
|
||||
/**
|
||||
* Add the bean with a check to see if it is already contained.
|
||||
*/
|
||||
void internalAddWithCheck(Object bean);
|
||||
|
||||
/**
|
||||
* Return the number of elements in the List Set or Map.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -8,17 +12,13 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* List capable of lazy loading.
|
||||
*/
|
||||
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
/**
|
||||
* The underlying List implementation.
|
||||
*/
|
||||
@@ -74,6 +74,13 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (list == null || !list.contains(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkEmptyLazyLoad() {
|
||||
if (list == null) {
|
||||
list = new ArrayList<E>();
|
||||
@@ -99,11 +106,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (list == null) {
|
||||
@@ -134,7 +141,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
public Collection<E> getActualDetails() {
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return list;
|
||||
|
||||
@@ -66,7 +66,18 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
map.put((K) key, (E) bean);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void internalPutWithCheck(Object key, Object bean) {
|
||||
if (map == null || !map.containsKey(key)) {
|
||||
internalPut(key, bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
|
||||
public void internalAdd(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Set capable of lazy loading.
|
||||
*/
|
||||
@@ -57,6 +57,13 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (set == null || !set.contains(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (set == null) {
|
||||
@@ -107,11 +114,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (set == null) {
|
||||
|
||||
@@ -387,6 +387,8 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean expressionEqualsWithNullAsNoop;
|
||||
|
||||
private String jodaLocalTimeMode;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -1640,6 +1642,20 @@ public class ServerConfig {
|
||||
this.disableClasspathSearch = disableClasspathSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode to use for Joda LocalTime support 'normal' or 'utc'.
|
||||
*/
|
||||
public String getJodaLocalTimeMode() {
|
||||
return jodaLocalTimeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode to use for Joda LocalTime support 'normal' or 'utc'.
|
||||
*/
|
||||
public void setJodaLocalTimeMode(String jodaLocalTimeMode) {
|
||||
this.jodaLocalTimeMode = jodaLocalTimeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically add classes (typically entities) that this server should
|
||||
* use.
|
||||
@@ -2286,6 +2302,7 @@ public class ServerConfig {
|
||||
dbUuid = DbUuid.BINARY;
|
||||
}
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
|
||||
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
|
||||
+10
-9
@@ -5,7 +5,7 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
|
||||
|
||||
/**
|
||||
* Converts a logical column definition into platform specific one.
|
||||
*
|
||||
* <p>
|
||||
* This translates standard sql types into platform specific ones.
|
||||
*/
|
||||
public class PlatformTypeConverter {
|
||||
@@ -44,20 +44,21 @@ public class PlatformTypeConverter {
|
||||
return columnDefinition;
|
||||
}
|
||||
|
||||
String type = columnDefinition.substring(0,open);
|
||||
String suffix = close + 1 < columnDefinition.length() ? columnDefinition.substring(close + 1) : "";
|
||||
String type = columnDefinition.substring(0, open);
|
||||
try {
|
||||
DbType dbType = platformTypes.lookup(type);
|
||||
int comma = columnDefinition.indexOf(',',open);
|
||||
int comma = columnDefinition.indexOf(',', open);
|
||||
if (comma > -1) {
|
||||
// scale and precision - decimal(10,4)
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open+1, comma));
|
||||
int precision = Integer.parseInt(columnDefinition.substring(comma+1, close));
|
||||
return dbType.renderType(scale,precision);
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open + 1, comma));
|
||||
int precision = Integer.parseInt(columnDefinition.substring(comma + 1, close));
|
||||
return dbType.renderType(scale, precision) + suffix;
|
||||
|
||||
} else {
|
||||
// scale - varchar(10)
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open+1, close));
|
||||
return dbType.renderType(scale,0);
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open + 1, close));
|
||||
return dbType.renderType(scale, 0) + suffix;
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
@@ -73,7 +74,7 @@ public class PlatformTypeConverter {
|
||||
|
||||
try {
|
||||
DbType dbType = platformTypes.lookup(columnDefinition);
|
||||
return dbType.renderType(0,0);
|
||||
return dbType.renderType(0, 0);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
// assume already platform specific, leave as is
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Key used for caching query plans for ORM and RawSql queries.
|
||||
*/
|
||||
public interface CQueryPlanKey {
|
||||
|
||||
/**
|
||||
* Used by read audit such that we can log read audit entries without the full sql
|
||||
* (which would make the read audit logs verbose).
|
||||
*/
|
||||
String getPartialKey();
|
||||
|
||||
}
|
||||
@@ -5,32 +5,18 @@ package com.avaje.ebeaninternal.api;
|
||||
*/
|
||||
public class HashQuery {
|
||||
|
||||
private final HashQueryPlan planHash;
|
||||
private final CQueryPlanKey planHash;
|
||||
|
||||
private final int bindHash;
|
||||
|
||||
/**
|
||||
* Create the HashQuery.
|
||||
*/
|
||||
public HashQuery(HashQueryPlan planHash, int bindHash) {
|
||||
public HashQuery(CQueryPlanKey planHash, int bindHash) {
|
||||
this.planHash = planHash;
|
||||
this.bindHash = bindHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan hash.
|
||||
*/
|
||||
public HashQueryPlan getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind values hash.
|
||||
*/
|
||||
public int getBindHash() {
|
||||
return bindHash;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * planHash.hashCode();
|
||||
hc = 31 * hc + bindHash;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
@@ -9,21 +11,19 @@ public class HashQueryPlanBuilder {
|
||||
|
||||
private int bindCount;
|
||||
|
||||
private String rawSql;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 31;
|
||||
this.planHash = 92821;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
return planHash+":"+bindCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 31 + cls.getName().hashCode();
|
||||
planHash = planHash * 92821 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,22 @@ public class HashQueryPlanBuilder {
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
|
||||
planHash = planHash * 92821 + (object == null ? 0 : object.hashCode());
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -39,7 +54,7 @@ public class HashQueryPlanBuilder {
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 31 + (hashValue);
|
||||
planHash = planHash * 92821 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -47,7 +62,7 @@ public class HashQueryPlanBuilder {
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 31 + (booleanValue ? 31 : 0);
|
||||
planHash = planHash * 92821 + (booleanValue ? 92821 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -58,19 +73,24 @@ public class HashQueryPlanBuilder {
|
||||
bindCount += extraBindCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add raw sql to the hash.
|
||||
*/
|
||||
public void addRawSql(String rawSql) {
|
||||
this.rawSql = rawSql;
|
||||
public void bindIfNotNull(Object someValue) {
|
||||
if (someValue != null) {
|
||||
bindCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public HashQueryPlan build() {
|
||||
return new HashQueryPlan(rawSql, planHash, bindCount);
|
||||
public String build() {
|
||||
return planHash+"_"+bindCount;
|
||||
}
|
||||
|
||||
|
||||
public int getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
public int getBindCount() {
|
||||
return bindCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controls the loading of reference objects for a query instance.
|
||||
@@ -22,17 +25,7 @@ public interface LoadContext {
|
||||
*/
|
||||
void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
|
||||
|
||||
/**
|
||||
* Register any secondary queries (+query or +lazy) with their
|
||||
* appropriate LoadBeanContext or LoadManyContext.
|
||||
* <p>
|
||||
* This is so the LoadBeanContext or LoadManyContext use the
|
||||
* defined query for +query and +lazy execution.
|
||||
* </p>
|
||||
*/
|
||||
void registerSecondaryQueries(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Return the node for a given path which is used by AutoTune profiling.
|
||||
*/
|
||||
ObjectGraphNode getObjectGraphNode(String path);
|
||||
|
||||
@@ -13,11 +13,8 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import com.avaje.ebean.dbmigration.DdlGenerator;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
|
||||
import java.util.List;
|
||||
@@ -130,22 +127,11 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
void remoteTransactionEvent(RemoteTransactionEvent event);
|
||||
|
||||
/**
|
||||
* Create a query request object.
|
||||
*/
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> q,
|
||||
Transaction t);
|
||||
|
||||
/**
|
||||
* Compile a query.
|
||||
*/
|
||||
<T> CQuery<T> compileQuery(Query<T> query, Transaction t);
|
||||
|
||||
/**
|
||||
* Return the queryEngine for this server.
|
||||
*/
|
||||
CQueryEngine getQueryEngine();
|
||||
|
||||
/**
|
||||
* Execute the findId's query but without copying the query.
|
||||
* <p>
|
||||
|
||||
@@ -17,32 +17,36 @@ public interface SpiExpression extends Expression {
|
||||
* </p>
|
||||
*/
|
||||
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Prepare the expression. For example, compile sub-query expressions etc.
|
||||
*/
|
||||
void prepareExpression(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash value used to identify a query for AutoTune tuning.
|
||||
* <p>
|
||||
* That is, if the hash changes then the query will be considered different
|
||||
* from an AutoTune perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
void queryAutoTuneHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Calculate a hash value for the expression.
|
||||
* This includes the expression type and property but should exclude
|
||||
* the bind values.
|
||||
* <p>
|
||||
* This is used where queries are the same except for the bind values, in which
|
||||
* case the query execution plan can be reused.
|
||||
* </p>
|
||||
*/
|
||||
void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
void queryPlanHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Return the hash value for the values that will be bound.
|
||||
*/
|
||||
int queryBindHash();
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the expression is the same without taking into account bind values.
|
||||
*/
|
||||
boolean isSameByPlan(SpiExpression other);
|
||||
|
||||
/**
|
||||
* Return true if the expression is the same with respect to bind values.
|
||||
*/
|
||||
boolean isSameByBind(SpiExpression other);
|
||||
|
||||
/**
|
||||
* Add some sql to the query.
|
||||
* <p>
|
||||
@@ -61,7 +65,7 @@ public interface SpiExpression extends Expression {
|
||||
/**
|
||||
* Add the parameter values to be set against query. For each ? place holder
|
||||
* there should be a corresponding value that is added to the bindList.
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* the associated request.
|
||||
*/
|
||||
@@ -71,4 +75,10 @@ public interface SpiExpression extends Expression {
|
||||
* Validate all the properties/paths associated with this expression.
|
||||
*/
|
||||
void validate(SpiExpressionValidation validation);
|
||||
|
||||
/**
|
||||
* Return a copy of the expression for use in the query plan key.
|
||||
*/
|
||||
SpiExpression copyForPlanKey();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.ExpressionFactory;
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Internal extension of ExpressionList.
|
||||
*/
|
||||
public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
|
||||
|
||||
/**
|
||||
* Return the underlying list of expressions.
|
||||
@@ -23,52 +19,9 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
SpiExpressionList<?> trimPath(int prefixTrim);
|
||||
|
||||
/**
|
||||
* Restore the ExpressionFactory after deserialisation.
|
||||
*/
|
||||
void setExpressionFactory(ExpressionFactory expr);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
|
||||
/**
|
||||
* Return true if this list is empty.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Concatenate the expression sql into a String.
|
||||
* <p>
|
||||
* The list of expressions are evaluated in order building a sql statement
|
||||
* with bind parameters.
|
||||
* </p>
|
||||
*/
|
||||
String buildSql(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Combine the expression bind values into a list.
|
||||
* <p>
|
||||
* Expressions are evaluated in order and all the resulting bind values are
|
||||
* returned as a List.
|
||||
* </p>
|
||||
*
|
||||
* @return the list of all the bind values in order.
|
||||
*/
|
||||
ArrayList<Object> buildBindValues(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Validate all the properties/paths used in this expression list.
|
||||
*/
|
||||
void validate(SpiExpressionValidation validation);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
|
||||
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request object used for gathering expression sql and bind values.
|
||||
*/
|
||||
@@ -54,7 +54,7 @@ public interface SpiExpressionRequest {
|
||||
/**
|
||||
* Return the ordered list of bind values for all expressions in this request.
|
||||
*/
|
||||
ArrayList<Object> getBindValues();
|
||||
List<Object> getBindValues();
|
||||
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
|
||||
@@ -289,11 +289,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
void setBeanDescriptor(BeanDescriptor<?> desc);
|
||||
|
||||
/**
|
||||
* Initialise/determine the joins required to support 'many' where clause predicates.
|
||||
*/
|
||||
boolean initManyWhereJoins();
|
||||
|
||||
/**
|
||||
* Return the joins required to support predicates on the many properties.
|
||||
*/
|
||||
@@ -319,31 +314,15 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
void setFilterMany(String prop, ExpressionList<?> filterMany);
|
||||
|
||||
/**
|
||||
* Remove the query joins from query detail.
|
||||
* <p>
|
||||
* These are registered with the Load Context.
|
||||
* </p>
|
||||
*/
|
||||
List<OrmQueryProperties> removeQueryJoins();
|
||||
|
||||
/**
|
||||
* Remove the lazy joins from query detail.
|
||||
* <p>
|
||||
* These are registered with the Load Context.
|
||||
* </p>
|
||||
*/
|
||||
List<OrmQueryProperties> removeLazyJoins();
|
||||
|
||||
/**
|
||||
* Set the path of the many when +query/+lazy loading query is executed.
|
||||
*/
|
||||
void setLazyLoadManyPath(String lazyLoadManyPath);
|
||||
|
||||
/**
|
||||
* Convert any many joins fetch joins to query joins.
|
||||
* Convert joins as necessary to query joins etc.
|
||||
*/
|
||||
void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch);
|
||||
SpiQuerySecondary convertJoins();
|
||||
|
||||
/**
|
||||
* Return the TransactionContext.
|
||||
@@ -467,27 +446,13 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Calculate a hash used by AutoTune to identify when a query has changed
|
||||
* (and hence potentially needs a new tuned query plan to be developed).
|
||||
* Prepare the query which prepares sub-query expressions and calculates
|
||||
* and returns the query plan key.
|
||||
* <p>
|
||||
* Excludes bind values and occurs prior to AutoTune potentially
|
||||
* tuning/modifying the query.
|
||||
* The query plan excludes actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
HashQueryPlan queryAutoTuneHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Identifies queries that are the same bar the bind variables.
|
||||
* <p>
|
||||
* This is used AFTER AutoTune has potentially tuned the query. This is
|
||||
* used to identify and reused query plans (the final SQL string and
|
||||
* associated SqlTree object).
|
||||
* </p>
|
||||
* <p>
|
||||
* Excludes the actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
HashQueryPlan queryPlanHash(BeanQueryRequest<?> request);
|
||||
CQueryPlanKey prepare(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The secondary query paths for 'query joins' and 'lazy loading'.
|
||||
*/
|
||||
public interface SpiQuerySecondary {
|
||||
|
||||
/**
|
||||
* Return a list of path/properties that are query join loaded.
|
||||
*/
|
||||
List<OrmQueryProperties> getQueryJoins();
|
||||
|
||||
/**
|
||||
* Return the list of path/properties that are lazy loaded.
|
||||
*/
|
||||
List<OrmQueryProperties> getLazyJoins();
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class ProfileOrigin {
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.addFetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.DdlGenerator;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
@@ -32,7 +31,6 @@ import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -463,19 +461,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return cqueryEngine.buildQuery(orm);
|
||||
}
|
||||
|
||||
public CQueryEngine getQueryEngine() {
|
||||
return cqueryEngine;
|
||||
}
|
||||
|
||||
public ServerCacheManager getServerCacheManager() {
|
||||
return serverCacheManager;
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
|
||||
|
||||
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName, t);
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName) {
|
||||
|
||||
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
|
||||
@@ -1048,7 +1037,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);
|
||||
@@ -1059,7 +1048,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return createQueryRequest(desc, spiQuery, t);
|
||||
}
|
||||
|
||||
public <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> query, Transaction t) {
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (desc.isAutoTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
@@ -1067,50 +1056,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
query.setDefaultSelectClause();
|
||||
}
|
||||
|
||||
if (query.selectAllForLazyLoadProperty()) {
|
||||
// we need to select all properties to ensure the lazy load property
|
||||
// was included (was not included by default or via autoTune).
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using selectAllForLazyLoadProperty");
|
||||
}
|
||||
}
|
||||
query.selectAllForLazyLoadProperty();
|
||||
|
||||
// if determine cost and no origin for AutoTune
|
||||
if (query.getParentNode() == null) {
|
||||
query.setOrigin(createCallStack());
|
||||
}
|
||||
|
||||
// determine extra joins required to support where clause
|
||||
// predicates on *ToMany properties
|
||||
if (query.initManyWhereJoins()) {
|
||||
// we need a sql distinct now
|
||||
query.setSqlDistinct(true);
|
||||
}
|
||||
|
||||
boolean allowOneManyFetch = true;
|
||||
if (Mode.LAZYLOAD_MANY.equals(query.getMode())) {
|
||||
allowOneManyFetch = false;
|
||||
|
||||
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) {
|
||||
// convert ALL fetch joins to Many's to be query joins
|
||||
// so that limit offset type SQL clauses work
|
||||
allowOneManyFetch = false;
|
||||
}
|
||||
|
||||
query.convertManyFetchJoinsToQueryJoins(allowOneManyFetch, queryBatchSize);
|
||||
|
||||
SpiTransaction serverTrans = (SpiTransaction) t;
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, desc, serverTrans);
|
||||
|
||||
BeanQueryAdapter queryAdapter = desc.getQueryAdapter();
|
||||
if (queryAdapter != null) {
|
||||
// adaption of the query probably based on the
|
||||
// current user
|
||||
queryAdapter.preQuery(request);
|
||||
}
|
||||
|
||||
// the query hash after any tuning
|
||||
request.calculateQueryPlanHash();
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, desc, (SpiTransaction) t);
|
||||
request.prepareQuery();
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.BeanIdList;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.api.SpiQuerySecondary;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -32,6 +31,13 @@ import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Wraps the objects involved in executing a Query.
|
||||
*/
|
||||
@@ -55,7 +61,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private HashQueryPlan queryPlanHash;
|
||||
private CQueryPlanKey queryPlanKey;
|
||||
|
||||
private SpiQuerySecondary secondaryQueries;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
@@ -126,10 +134,24 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query plan hash AFTER any potential AutoTune tuning.
|
||||
* Run BeanQueryAdapter preQuery() if needed.
|
||||
*/
|
||||
public void calculateQueryPlanHash() {
|
||||
this.queryPlanHash = query.queryPlanHash(this);
|
||||
private void adapterPreQuery() {
|
||||
BeanQueryAdapter queryAdapter = beanDescriptor.getQueryAdapter();
|
||||
if (queryAdapter != null) {
|
||||
queryAdapter.preQuery(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the query and calculate the query plan key.
|
||||
*/
|
||||
public void prepareQuery() {
|
||||
|
||||
adapterPreQuery();
|
||||
|
||||
this.secondaryQueries = query.convertJoins();
|
||||
this.queryPlanKey = query.prepare(this);
|
||||
}
|
||||
|
||||
public boolean isRawSql() {
|
||||
@@ -190,8 +212,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
// initialise the persistenceContext and loadContext
|
||||
this.persistenceContext = getPersistenceContext(query, transaction);
|
||||
this.loadContext = new DLoadContext(this);
|
||||
this.loadContext.registerSecondaryQueries(query);
|
||||
this.loadContext = new DLoadContext(this, secondaryQueries);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,7 +236,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
// determine the scope (from the query and then server)
|
||||
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
|
||||
return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
|
||||
return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,7 +326,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<?> findSet() {
|
||||
return (Set<T>)queryEngine.findMany(this);
|
||||
return (Set<T>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,10 +345,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return (Map<?, ?>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
public SpiQuery.Type getQueryType() {
|
||||
return query.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bean specific finder if one has been set.
|
||||
*/
|
||||
@@ -355,7 +372,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* query plan for this query exists.
|
||||
*/
|
||||
public CQueryPlan getQueryPlan() {
|
||||
return beanDescriptor.getQueryPlan(queryPlanHash);
|
||||
return beanDescriptor.getQueryPlan(queryPlanKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,15 +383,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* with just the bind variables changing.
|
||||
* </p>
|
||||
*/
|
||||
public HashQueryPlan getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
public CQueryPlanKey getQueryPlanKey() {
|
||||
return queryPlanKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the QueryPlan into the cache.
|
||||
*/
|
||||
public void putQueryPlan(CQueryPlan queryPlan) {
|
||||
beanDescriptor.putQueryPlan(queryPlanHash, queryPlan);
|
||||
beanDescriptor.putQueryPlan(queryPlanKey, queryPlan);
|
||||
}
|
||||
|
||||
public boolean isUseBeanCache() {
|
||||
@@ -401,7 +418,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
for (T bean : actualDetails) {
|
||||
ids.add(beanDescriptor.getIdForJson(bean));
|
||||
}
|
||||
beanDescriptor.readAuditMany(queryPlanHash.getPartialKey(), "l2-query-cache", ids);
|
||||
beanDescriptor.readAuditMany(queryPlanKey.getPartialKey(), "l2-query-cache", ids);
|
||||
}
|
||||
|
||||
return cached;
|
||||
|
||||
@@ -42,7 +42,7 @@ public interface BeanCollectionHelp<T> {
|
||||
/**
|
||||
* Add a bean to the List Set or Map.
|
||||
*/
|
||||
void add(BeanCollection<?> collection, EntityBean bean);
|
||||
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
|
||||
|
||||
/**
|
||||
* Create a lazy loading proxy for a List Set or Map.
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.avaje.ebean.event.readaudit.ReadEvent;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -87,7 +87,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
|
||||
private final ConcurrentHashMap<Integer, SpiUpdatePlan> updatePlanCache = new ConcurrentHashMap<Integer, SpiUpdatePlan>();
|
||||
|
||||
private final ConcurrentHashMap<HashQueryPlan, CQueryPlan> queryPlanCache = new ConcurrentHashMap<HashQueryPlan, CQueryPlan>();
|
||||
private final ConcurrentHashMap<CQueryPlanKey, CQueryPlan> queryPlanCache = new ConcurrentHashMap<CQueryPlanKey, CQueryPlan>();
|
||||
|
||||
private final ConcurrentHashMap<String, ElPropertyValue> elCache = new ConcurrentHashMap<String, ElPropertyValue>();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1163,11 +1163,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public CQueryPlan getQueryPlan(HashQueryPlan key) {
|
||||
public CQueryPlan getQueryPlan(CQueryPlanKey key) {
|
||||
return queryPlanCache.get(key);
|
||||
}
|
||||
|
||||
public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) {
|
||||
public void putQueryPlan(CQueryPlanKey key, CQueryPlan plan) {
|
||||
queryPlanCache.put(key, plan);
|
||||
}
|
||||
|
||||
@@ -1252,7 +1252,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
|
||||
OrmQueryDetail detail = query.getDetail();
|
||||
for (int i = 0; i < propertiesMany.length; i++) {
|
||||
if (detail.includes(propertiesMany[i].getName())) {
|
||||
if (detail.includesPath(propertiesMany[i].getName())) {
|
||||
return propertiesMany[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,12 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
collection.internalAdd(bean);
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -108,13 +108,14 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
|
||||
if (bean == null) {
|
||||
((BeanMap<?, ?>) collection).internalPutNull();
|
||||
} else {
|
||||
Object keyValue = beanProperty.getValueIntercept(bean);
|
||||
((BeanMap<?, ?>) collection).internalPut(keyValue, bean);
|
||||
BeanMap<?, ?> map = ((BeanMap<?, ?>) collection);
|
||||
map.internalPutWithCheck(keyValue, bean);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,13 +182,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* Add the bean to the appropriate collection on the parent bean.
|
||||
*/
|
||||
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) {
|
||||
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean, boolean withCheck) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>) super.getValue(parentBean);
|
||||
if (bc == null) {
|
||||
bc = help.createEmpty(parentBean);
|
||||
setValue(parentBean, bc);
|
||||
}
|
||||
help.add(bc, detailBean);
|
||||
help.add(bc, detailBean, withCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,7 +416,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
help.add(collection, bean);
|
||||
help.add(collection, bean, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,8 +61,12 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
collection.internalAdd(bean);
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
@@ -9,7 +10,7 @@ import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import com.avaje.ebeaninternal.server.expression.IdInExpression;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
|
||||
public class IntersectionRow {
|
||||
|
||||
@@ -98,7 +99,7 @@ public class IntersectionRow {
|
||||
sb.append(er.getSql());
|
||||
sb.append(" ) ");
|
||||
|
||||
ArrayList<Object> bindValues = er.getBindValues();
|
||||
List<Object> bindValues = er.getBindValues();
|
||||
for (int i = 0; i < bindValues.size(); i++) {
|
||||
bindParams.setParameter(++count, bindValues.get(i));
|
||||
}
|
||||
|
||||
@@ -63,6 +63,32 @@ public final class TableJoin {
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return queryHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TableJoin that = (TableJoin) o;
|
||||
|
||||
if (!table.equals(that.table)) return false;
|
||||
if (type != that.type) return false;
|
||||
if (columns.length != that.columns.length) return false;
|
||||
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (!columns[i].equals(that.columns[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a hash value for adding to a query plan.
|
||||
*/
|
||||
|
||||
@@ -8,76 +8,97 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
*/
|
||||
public class TableJoinColumn {
|
||||
|
||||
/**
|
||||
* The local database column name.
|
||||
*/
|
||||
private final String localDbColumn;
|
||||
/**
|
||||
* The local database column name.
|
||||
*/
|
||||
private final String localDbColumn;
|
||||
|
||||
/**
|
||||
* The foreign database column name.
|
||||
*/
|
||||
private final String foreignDbColumn;
|
||||
/**
|
||||
* The foreign database column name.
|
||||
*/
|
||||
private final String foreignDbColumn;
|
||||
|
||||
private final boolean insertable;
|
||||
|
||||
private final boolean updateable;
|
||||
private final boolean insertable;
|
||||
|
||||
/**
|
||||
* Hash for including in a query plan
|
||||
*/
|
||||
private final int queryHash;
|
||||
private final boolean updateable;
|
||||
|
||||
/**
|
||||
* Create the pair.
|
||||
*/
|
||||
public TableJoinColumn(DeployTableJoinColumn deploy) {
|
||||
this.localDbColumn = InternString.intern(deploy.getLocalDbColumn());
|
||||
this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn());
|
||||
this.insertable = deploy.isInsertable();
|
||||
this.updateable = deploy.isUpdateable();
|
||||
this.queryHash = hashOf(localDbColumn) * 31 + hashOf(foreignDbColumn);
|
||||
}
|
||||
/**
|
||||
* Hash for including in a query plan
|
||||
*/
|
||||
private final int queryHash;
|
||||
|
||||
private int hashOf(String value) {
|
||||
return (value == null) ? 0 : value.hashCode();
|
||||
}
|
||||
/**
|
||||
* Create the pair.
|
||||
*/
|
||||
public TableJoinColumn(DeployTableJoinColumn deploy) {
|
||||
this.localDbColumn = InternString.intern(deploy.getLocalDbColumn());
|
||||
this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn());
|
||||
this.insertable = deploy.isInsertable();
|
||||
this.updateable = deploy.isUpdateable();
|
||||
this.queryHash = hash();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return localDbColumn+" = "+foreignDbColumn;
|
||||
}
|
||||
int hash() {
|
||||
int result = localDbColumn != null ? localDbColumn.hashCode() : 0;
|
||||
result = 31 * result + (foreignDbColumn != null ? foreignDbColumn.hashCode() : 0);
|
||||
result = 31 * result + (insertable ? 1 : 0);
|
||||
result = 31 * result + (updateable ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for including in a query plan.
|
||||
*/
|
||||
public int queryHash() {
|
||||
return queryHash;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return queryHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the foreign database column name.
|
||||
*/
|
||||
public String getForeignDbColumn() {
|
||||
return foreignDbColumn;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
/**
|
||||
* Return the local database column name.
|
||||
*/
|
||||
public String getLocalDbColumn() {
|
||||
return localDbColumn;
|
||||
}
|
||||
TableJoinColumn that = (TableJoinColumn) o;
|
||||
if (insertable != that.insertable) return false;
|
||||
if (updateable != that.updateable) return false;
|
||||
if (!localDbColumn.equals(that.localDbColumn)) return false;
|
||||
return foreignDbColumn.equals(that.foreignDbColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be insertable.
|
||||
*/
|
||||
public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
public String toString() {
|
||||
return localDbColumn + " = " + foreignDbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be updateable.
|
||||
*/
|
||||
public boolean isUpdateable() {
|
||||
return updateable;
|
||||
}
|
||||
/**
|
||||
* Return a hash for including in a query plan.
|
||||
*/
|
||||
public int queryHash() {
|
||||
return queryHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the foreign database column name.
|
||||
*/
|
||||
public String getForeignDbColumn() {
|
||||
return foreignDbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the local database column name.
|
||||
*/
|
||||
public String getLocalDbColumn() {
|
||||
return localDbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be insertable.
|
||||
*/
|
||||
public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be updateable.
|
||||
*/
|
||||
public boolean isUpdateable() {
|
||||
return updateable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -21,19 +22,20 @@ public abstract class AbstractExpression implements SpiExpression {
|
||||
this.propName = propName;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propName;
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
if (propertyName != null) {
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName);
|
||||
if (propName != null) {
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propName);
|
||||
if (elProp != null) {
|
||||
if (elProp.containsFormulaWithJoin()) {
|
||||
// for findRowCount query select clause
|
||||
manyWhereJoin.addFormulaWithJoin(propertyName);
|
||||
manyWhereJoin.addFormulaWithJoin(propName);
|
||||
}
|
||||
if (elProp.containsMany()) {
|
||||
// for findRowCount we join to a many property
|
||||
@@ -44,13 +46,17 @@ public abstract class AbstractExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(SpiExpressionValidation validation) {
|
||||
validation.validate(getPropertyName());
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected ElPropertyValue getElProp(SpiExpressionRequest request) {
|
||||
@Override
|
||||
public void validate(SpiExpressionValidation validation) {
|
||||
validation.validate(propName);
|
||||
}
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
return request.getBeanDescriptor().getElGetValue(propertyName);
|
||||
protected final ElPropertyValue getElProp(SpiExpressionRequest request) {
|
||||
|
||||
return request.getBeanDescriptor().getElGetValue(propName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -12,7 +8,11 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
|
||||
class AllEqualsExpression implements SpiExpression {
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
class AllEqualsExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = -8691773558205937025L;
|
||||
|
||||
@@ -26,6 +26,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
return propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
if (propMap != null) {
|
||||
for (String propertyName : propMap.keySet()) {
|
||||
@@ -44,6 +45,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
if (propMap.isEmpty()) {
|
||||
@@ -57,6 +59,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (propMap.isEmpty()) {
|
||||
@@ -92,7 +95,8 @@ class AllEqualsExpression implements SpiExpression {
|
||||
* The null check is required due to the "is null" sql being generated.
|
||||
* </p>
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(AllEqualsExpression.class);
|
||||
|
||||
@@ -100,14 +104,11 @@ class AllEqualsExpression implements SpiExpression {
|
||||
Object value = entry.getValue();
|
||||
String propName = entry.getKey();
|
||||
builder.add(propName).add(value == null ? 0 : 1);
|
||||
builder.bind(value == null ? 0 : 1);
|
||||
builder.bindIfNotNull(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
|
||||
int hc = 31;
|
||||
@@ -117,4 +118,48 @@ class AllEqualsExpression implements SpiExpression {
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof AllEqualsExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AllEqualsExpression that = (AllEqualsExpression) other;
|
||||
return isSameByValue(that, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
if (!(other instanceof AllEqualsExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AllEqualsExpression that = (AllEqualsExpression) other;
|
||||
return isSameByValue(that, true);
|
||||
}
|
||||
|
||||
private boolean isSameByValue(AllEqualsExpression that, boolean byValue) {
|
||||
|
||||
if (propMap.size() != that.propMap.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Iterator<Entry<String, Object>> thisIt = propMap.entrySet().iterator();
|
||||
Iterator<Entry<String, Object>> thatIt = that.propMap.entrySet().iterator();
|
||||
|
||||
while (thisIt.hasNext() && thatIt.hasNext()) {
|
||||
Entry<String, Object> thisNext = thisIt.next();
|
||||
Entry<String, Object> thatNext = thatIt.next();
|
||||
|
||||
if (!thisNext.getKey().equals(thatNext.getKey())) {
|
||||
return false;
|
||||
}
|
||||
if (!Same.sameBy(byValue, thisNext.getValue(), thatNext.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
|
||||
class BetweenExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = 2078918165221454910L;
|
||||
@@ -21,28 +20,45 @@ class BetweenExpression extends AbstractExpression {
|
||||
this.valueHigh = valHigh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
request.addBindValue(valueLow);
|
||||
request.addBindValue(valueHigh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append(getPropertyName()).append(BETWEEN).append(" ? and ? ");
|
||||
request.append(propName).append(BETWEEN).append(" ? and ? ");
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenExpression.class).add(propName);
|
||||
builder.bind(2);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = valueLow.hashCode();
|
||||
hc = hc * 31 + valueHigh.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof BetweenExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BetweenExpression that = (BetweenExpression) other;
|
||||
return this.propName.equals(that.propName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
BetweenExpression that = (BetweenExpression) other;
|
||||
return valueLow.equals(that.valueLow)
|
||||
&& valueHigh.equals(that.valueHigh);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-7
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -12,7 +11,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
/**
|
||||
* Between expression where a value is between two properties.
|
||||
*/
|
||||
class BetweenPropertyExpression implements SpiExpression {
|
||||
class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = 2078918165221454910L;
|
||||
|
||||
@@ -32,6 +31,7 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
return propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(lowProperty));
|
||||
@@ -51,25 +51,42 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
validation.validate(highProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
request.addBindValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty));
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenPropertyExpression.class).add(lowProperty).add(highProperty);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof BetweenPropertyExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BetweenPropertyExpression that = (BetweenPropertyExpression) other;
|
||||
return lowProperty.equals(that.lowProperty)
|
||||
&& highProperty.equals(that.highProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
BetweenPropertyExpression that = (BetweenPropertyExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-10
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -16,6 +16,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
this.value = value.toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
@@ -28,29 +29,42 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
request.addBindValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
String pname = propertyName;
|
||||
|
||||
String pname = propName;
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propertyName);
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propName);
|
||||
}
|
||||
|
||||
request.append("lower(").append(pname).append(") =? ");
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(CaseInsensitiveEqualExpression.class).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof CaseInsensitiveEqualExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CaseInsensitiveEqualExpression that = (CaseInsensitiveEqualExpression) other;
|
||||
return this.propName.equals(that.propName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
CaseInsensitiveEqualExpression that = (CaseInsensitiveEqualExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
+62
-15
@@ -86,6 +86,20 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
this.likeType = likeType;
|
||||
}
|
||||
|
||||
DefaultExampleExpression(ArrayList<SpiExpression> source) {
|
||||
this.entity = null;
|
||||
this.list = new ArrayList<SpiExpression>(source.size());
|
||||
for (SpiExpression expression : source) {
|
||||
list.add(expression.copyForPlanKey());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new DefaultExampleExpression(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -94,31 +108,37 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression includeZeros() {
|
||||
includeZeros = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression caseInsensitive() {
|
||||
caseInsensitive = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useStartsWith() {
|
||||
likeType = LikeType.STARTS_WITH;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useContains() {
|
||||
likeType = LikeType.CONTAINS;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useEndsWith() {
|
||||
likeType = LikeType.ENDS_WITH;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useEqualTo() {
|
||||
likeType = LikeType.EQUAL_TO;
|
||||
return this;
|
||||
@@ -134,6 +154,7 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
/**
|
||||
* Adds bind values to the request.
|
||||
*/
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -145,6 +166,7 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
/**
|
||||
* Generates and adds the sql to the request.
|
||||
*/
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
@@ -162,34 +184,27 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for AutoTune query identification.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
// we have not yet built the list of expressions
|
||||
// so just based on the class name
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
list = buildExpressions(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for query plan identification.
|
||||
* Return a hash for AutoTune query identification.
|
||||
*/
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
// this is always called once, and always called before
|
||||
// addSql() and addBindValues() methods
|
||||
list = buildExpressions(request);
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryPlanHash(request, builder);
|
||||
list.get(i).queryPlanHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for the actual bind values used.
|
||||
*/
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = DefaultExampleExpression.class.getName().hashCode();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -199,6 +214,38 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof DefaultExampleExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultExampleExpression that = (DefaultExampleExpression) other;
|
||||
if (this.list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (!list.get(i).isSameByPlan(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
DefaultExampleExpression that = (DefaultExampleExpression) other;
|
||||
if (this.list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (!list.get(i).isSameByBind(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the List of expressions.
|
||||
*/
|
||||
|
||||
+55
-32
@@ -1,4 +1,4 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
@@ -56,6 +56,10 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
this.listAndJoin = " and ";
|
||||
}
|
||||
|
||||
private DefaultExpressionList() {
|
||||
this(null, null, null, new ArrayList<SpiExpression>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim) {
|
||||
throw new RuntimeException("Only allowed on FilterExpressionList");
|
||||
@@ -65,17 +69,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ExpressionFactory.
|
||||
* <p>
|
||||
* After deserialisation so that it can be further modified.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void setExpressionFactory(ExpressionFactory expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the expression list.
|
||||
* <p>
|
||||
@@ -88,6 +81,14 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public DefaultExpressionList<T> copyForPlanKey() {
|
||||
DefaultExpressionList<T> copy = new DefaultExpressionList<T>();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
copy.list.add(list.get(i).copyForPlanKey());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if one of the expressions is related to a Many property.
|
||||
*/
|
||||
@@ -325,7 +326,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildSql(SpiExpressionRequest request) {
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append(listAndStart);
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
@@ -336,28 +337,19 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
expression.addSql(request);
|
||||
}
|
||||
request.append(listAndEnd);
|
||||
return request.getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<Object> buildBindValues(SpiExpressionRequest request) {
|
||||
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
expression.addBindValues(request);
|
||||
list.get(i).addBindValues(request);
|
||||
}
|
||||
return request.getBindValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(DefaultExpressionList.class);
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
expression.queryAutoTuneHash(builder);
|
||||
list.get(i).prepareExpression(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,26 +358,57 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* values.
|
||||
*/
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(DefaultExpressionList.class);
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
expression.queryPlanHash(request, builder);
|
||||
list.get(i).queryPlanHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions.
|
||||
*/
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hash = DefaultExpressionList.class.getName().hashCode();
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
hash = hash * 31 + expression.queryBindHash();
|
||||
hash = hash * 31 + list.get(i).queryBindHash();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof DefaultExpressionList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>)other;
|
||||
if (list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
if (!list.get(i).isSameByPlan(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>)other;
|
||||
if (list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
if (!list.get(i).isSameByBind(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
*/
|
||||
+17
-5
@@ -1,7 +1,8 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -20,7 +21,7 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
|
||||
private final StringBuilder sql = new StringBuilder();
|
||||
|
||||
private final ArrayList<Object> bindValues = new ArrayList<Object>();
|
||||
private final List<Object> bindValues = new ArrayList<Object>();
|
||||
|
||||
private final DeployParser deployParser;
|
||||
|
||||
@@ -39,7 +40,7 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
this.binder = binder;
|
||||
this.expressionList = expressionList;
|
||||
// immediately build the list of bind values (callback style)
|
||||
expressionList.buildBindValues(this);
|
||||
expressionList.addBindValues(this);
|
||||
}
|
||||
|
||||
public DefaultExpressionRequest(BeanDescriptor<?> beanDescriptor) {
|
||||
@@ -54,7 +55,8 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
* Build sql for the underlying expression list.
|
||||
*/
|
||||
public String buildSql() {
|
||||
return expressionList.buildSql(this);
|
||||
expressionList.addSql(this);
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,10 +72,12 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonExpressionHandler getJsonHandler() {
|
||||
return binder.getJsonExpressionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseDeploy(String logicalProp) {
|
||||
|
||||
String s = deployParser.getDeployWord(logicalProp);
|
||||
@@ -93,14 +97,17 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
*/
|
||||
@Override
|
||||
public int nextParameter() {
|
||||
return ++paramIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<?> getBeanDescriptor() {
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiOrmQueryRequest<?> getQueryRequest() {
|
||||
return queryRequest;
|
||||
}
|
||||
@@ -108,16 +115,19 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
/**
|
||||
* Append text the underlying sql expression.
|
||||
*/
|
||||
@Override
|
||||
public SpiExpressionRequest append(String sqlExpression) {
|
||||
sql.append(sqlExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindEncryptKey(Object bindValue) {
|
||||
bindValues.add(bindValue);
|
||||
bindLog("****");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValue(Object bindValue) {
|
||||
bindValues.add(bindValue);
|
||||
bindLog(bindValue);
|
||||
@@ -136,11 +146,13 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
return bindLog == null ? "" : bindLog.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
public ArrayList<Object> getBindValues() {
|
||||
@Override
|
||||
public List<Object> getBindValues() {
|
||||
return bindValues;
|
||||
}
|
||||
|
||||
@@ -17,71 +17,103 @@ public class ExistsExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = 666990277309851644L;
|
||||
|
||||
private final boolean not;
|
||||
protected final boolean not;
|
||||
|
||||
private final SpiQuery<?> subQuery;
|
||||
protected final SpiQuery<?> subQuery;
|
||||
|
||||
private transient CQuery<?> compiledSubQuery;
|
||||
protected List<Object> bindParams;
|
||||
|
||||
protected String sql;
|
||||
|
||||
public ExistsExpression(SpiQuery<?> subQuery, boolean not) {
|
||||
this.subQuery = subQuery;
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(ExistsExpression.class).add(not);
|
||||
|
||||
subQuery.queryAutoTuneHash(builder);
|
||||
ExistsExpression(boolean not, String sql , List<Object> bindParams) {
|
||||
this.not = not;
|
||||
this.sql = sql;
|
||||
this.bindParams = bindParams;
|
||||
this.subQuery = null;
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
|
||||
// queryPlanHash executes prior to addSql() or addBindValues()
|
||||
// ... so compiledQuery will exist
|
||||
compiledSubQuery = compileSubQuery(request);
|
||||
CQuery<?> subQuery = compileSubQuery(request);
|
||||
this.bindParams = subQuery.getPredicates().getWhereExprBindValues();
|
||||
this.sql = subQuery.getGeneratedSql().replace('\n', ' ');
|
||||
}
|
||||
|
||||
queryAutoTuneHash(builder);
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile/build the sub query.
|
||||
*/
|
||||
private CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
|
||||
|
||||
protected CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
|
||||
SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer();
|
||||
return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(ExistsExpression.class).add(not);
|
||||
builder.add(sql).add(bindParams.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return subQuery.queryBindHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String subSelect = compiledSubQuery.getGeneratedSql();
|
||||
subSelect = subSelect.replace('\n', ' ');
|
||||
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
request.append(" exists (");
|
||||
request.append(subSelect);
|
||||
request.append(sql);
|
||||
request.append(") ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
List<Object> bindParams = compiledSubQuery.getPredicates().getWhereExprBindValues();
|
||||
|
||||
if (bindParams == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bindParams.size(); i++) {
|
||||
request.addBindValue(bindParams.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof ExistsExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExistsExpression that = (ExistsExpression) other;
|
||||
return this.sql.equals(that.sql)
|
||||
&& this.not == that.not
|
||||
&& this.bindParams.size() == that.bindParams.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
ExistsExpression that = (ExistsExpression) other;
|
||||
if (this.bindParams.size() != that.bindParams.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < bindParams.size(); i++) {
|
||||
if (!bindParams.get(i).equals(that.bindParams.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
|
||||
// Nothing to do for exists expression
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.List;
|
||||
@@ -1,18 +1,16 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
|
||||
/**
|
||||
* Slightly redundant as Query.setId() ultimately also does the same job.
|
||||
*/
|
||||
class IdExpression implements SpiExpression {
|
||||
class IdExpression extends NonPrepareExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = -3065936341718489842L;
|
||||
|
||||
@@ -25,6 +23,7 @@ class IdExpression implements SpiExpression {
|
||||
/**
|
||||
* Always returns false.
|
||||
*/
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
}
|
||||
@@ -34,6 +33,7 @@ class IdExpression implements SpiExpression {
|
||||
// always valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
// 'flatten' EmbeddedId and multiple Id cases
|
||||
@@ -45,6 +45,7 @@ class IdExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
|
||||
@@ -56,17 +57,25 @@ class IdExpression implements SpiExpression {
|
||||
/**
|
||||
* No properties so this is just a unique static number.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdExpression.class);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
return other instanceof IdExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
IdExpression that = (IdExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -10,12 +7,13 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Slightly redundant as Query.setId() ultimately also does the same job.
|
||||
*/
|
||||
public class IdInExpression implements SpiExpression {
|
||||
public class IdInExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -25,6 +23,7 @@ public class IdInExpression implements SpiExpression {
|
||||
this.idList = idList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
}
|
||||
|
||||
@@ -33,6 +32,7 @@ public class IdInExpression implements SpiExpression {
|
||||
// always valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
// Bind the Id values including EmbeddedId and multiple Id
|
||||
@@ -60,6 +60,7 @@ public class IdInExpression implements SpiExpression {
|
||||
request.append(inClause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
|
||||
@@ -74,17 +75,38 @@ public class IdInExpression implements SpiExpression {
|
||||
/**
|
||||
* Incorporates the number of Id values to bind.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdInExpression.class).add(idList.size());
|
||||
builder.bind(idList.size());
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return idList.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof IdInExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IdInExpression that = (IdInExpression) other;
|
||||
return this.idList.size() == that.idList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
IdInExpression that = (IdInExpression) other;
|
||||
if (this.idList.size() != that.idList.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < idList.size(); i++) {
|
||||
if (!idList.get(i).equals(that.idList.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -28,6 +28,7 @@ class InExpression extends AbstractExpression {
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
@@ -51,6 +52,7 @@ class InExpression extends AbstractExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (values.length == 0) {
|
||||
@@ -59,20 +61,18 @@ class InExpression extends AbstractExpression {
|
||||
return;
|
||||
}
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && !prop.isAssocId()) {
|
||||
prop = null;
|
||||
}
|
||||
|
||||
if (prop != null) {
|
||||
request.append(prop.getAssocIdInExpr(propertyName));
|
||||
request.append(prop.getAssocIdInExpr(propName));
|
||||
String inClause = prop.getAssocIdInValueExpr(values.length);
|
||||
request.append(inClause);
|
||||
|
||||
} else {
|
||||
request.append(propertyName);
|
||||
request.append(propName);
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
@@ -88,15 +88,13 @@ class InExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on the number of values in the in clause.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InExpression.class).add(propName).add(values.length).add(not);
|
||||
builder.bind(values.length);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = 31;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
@@ -105,4 +103,29 @@ class InExpression extends AbstractExpression {
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof InExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InExpression that = (InExpression) other;
|
||||
return propName.equals(that.propName)
|
||||
&& not == that.not
|
||||
&& values.length == that.values.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
InExpression that = (InExpression) other;
|
||||
if (this.values.length != that.values.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (!values[i].equals(that.values[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* In expression using a sub query.
|
||||
*/
|
||||
@@ -20,26 +21,36 @@ class InQueryExpression extends AbstractExpression {
|
||||
|
||||
private final SpiQuery<?> subQuery;
|
||||
|
||||
private transient CQuery<?> compiledSubQuery;
|
||||
private List<Object> bindParams;
|
||||
|
||||
public InQueryExpression(String propertyName, SpiQuery<?> subQuery, boolean not) {
|
||||
private String sql;
|
||||
|
||||
InQueryExpression(String propertyName, SpiQuery<?> subQuery, boolean not) {
|
||||
super(propertyName);
|
||||
this.subQuery = subQuery;
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InQueryExpression.class).add(propName).add(not);
|
||||
subQuery.queryAutoTuneHash(builder);
|
||||
InQueryExpression(String propertyName, boolean not, String sql, List<Object> bindParams) {
|
||||
super(propertyName);
|
||||
this.subQuery = null;
|
||||
this.not = not;
|
||||
this.sql = sql;
|
||||
this.bindParams = bindParams;
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
|
||||
// queryPlanHash executes prior to addSql() or addBindValues()
|
||||
// ... so compiledQuery will exist
|
||||
compiledSubQuery = compileSubQuery(request);
|
||||
CQuery<?> subQuery = compileSubQuery(request);
|
||||
this.bindParams = subQuery.getPredicates().getWhereExprBindValues();
|
||||
this.sql = subQuery.getGeneratedSql().replace('\n', ' ');
|
||||
}
|
||||
|
||||
queryAutoTuneHash(builder);
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InQueryExpression.class).add(propName).add(not);
|
||||
builder.add(sql).add(bindParams.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,35 +62,55 @@ class InQueryExpression extends AbstractExpression {
|
||||
return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return subQuery.queryBindHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String subSelect = compiledSubQuery.getGeneratedSql();
|
||||
subSelect = subSelect.replace('\n', ' ');
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
request.append(" (").append(propertyName).append(")");
|
||||
request.append(" (").append(propName).append(")");
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
request.append(" in (");
|
||||
request.append(subSelect);
|
||||
request.append(sql);
|
||||
request.append(") ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
List<Object> bindParams = compiledSubQuery.getPredicates().getWhereExprBindValues();
|
||||
|
||||
if (bindParams == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bindParams.size(); i++) {
|
||||
request.addBindValue(bindParams.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof InQueryExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InQueryExpression that = (InQueryExpression) other;
|
||||
return propName.equals(that.propName)
|
||||
&& sql.equals(that.sql)
|
||||
&& not == that.not
|
||||
&& bindParams.size() == that.bindParams.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
InQueryExpression that = (InQueryExpression) other;
|
||||
if (this.bindParams.size() != that.bindParams.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < bindParams.size(); i++) {
|
||||
if (!bindParams.get(i).equals(that.bindParams.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
/**
|
||||
@@ -58,13 +58,10 @@ class JsonPathExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(JsonPathExpression.class).add(propName).add(path).add(operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
builder.bindIfNotNull(value);
|
||||
builder.bindIfNotNull(upperValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,6 +71,27 @@ class JsonPathExpression extends AbstractExpression {
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof JsonPathExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonPathExpression that = (JsonPathExpression) other;
|
||||
return propName.equals(that.propName)
|
||||
&& operator == that.operator
|
||||
&& Same.sameByValue(path, that.path)
|
||||
&& Same.sameByNull(value, that.value)
|
||||
&& Same.sameByNull(upperValue, that.upperValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
JsonPathExpression that = (JsonPathExpression) other;
|
||||
if (value != null ? !value.equals(that.value) : that.value != null) return false;
|
||||
return upperValue != null ? upperValue.equals(that.upperValue) : that.upperValue == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionList;
|
||||
|
||||
/**
|
||||
* Junction implementation.
|
||||
@@ -35,6 +34,14 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
Conjunction(com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
|
||||
super(false, AND, query, parent);
|
||||
}
|
||||
|
||||
Conjunction(DefaultExpressionList<T> expressionList) {
|
||||
super(false, AND, expressionList);
|
||||
}
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new Conjunction<T>(exprList.copyForPlanKey());
|
||||
}
|
||||
}
|
||||
|
||||
static class Disjunction<T> extends JunctionExpression<T> {
|
||||
@@ -44,9 +51,18 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
Disjunction(com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
|
||||
super(true, OR, query, parent);
|
||||
}
|
||||
|
||||
Disjunction(DefaultExpressionList<T> expressionList) {
|
||||
super(true, OR, expressionList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new Disjunction<T>(exprList.copyForPlanKey());
|
||||
}
|
||||
}
|
||||
|
||||
private final DefaultExpressionList<T> exprList;
|
||||
protected final DefaultExpressionList<T> exprList;
|
||||
|
||||
private final String joinType;
|
||||
|
||||
@@ -61,6 +77,15 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
this.exprList = new DefaultExpressionList<T>(query, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for copyForPlanKey.
|
||||
*/
|
||||
JunctionExpression(boolean disjunction, String joinType, DefaultExpressionList<T> exprList) {
|
||||
this.disjunction = disjunction;
|
||||
this.joinType = joinType;
|
||||
this.exprList = exprList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
@@ -131,24 +156,23 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).prepareExpression(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on Junction type and all the expression contained.
|
||||
*/
|
||||
@Override
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryAutoTuneHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryPlanHash(request, builder);
|
||||
list.get(i).queryPlanHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +188,25 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
|
||||
if (!(other instanceof JunctionExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JunctionExpression that = (JunctionExpression) other;
|
||||
return joinType.equals(that.joinType)
|
||||
&& exprList.isSameByPlan(that.exprList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
JunctionExpression that = (JunctionExpression) other;
|
||||
return joinType.equals(that.joinType)
|
||||
&& exprList.isSameByBind(that.exprList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> endJunction() {
|
||||
return exprList.endJunction();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.LikeType;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -23,6 +23,7 @@ class LikeExpression extends AbstractExpression {
|
||||
this.val = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
@@ -36,14 +37,13 @@ class LikeExpression extends AbstractExpression {
|
||||
request.addBindValue(bindValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
String pname = propertyName;
|
||||
|
||||
String pname = propName;
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propertyName);
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propName);
|
||||
}
|
||||
if (caseInsensitive) {
|
||||
request.append("lower(").append(pname).append(")");
|
||||
@@ -61,19 +61,35 @@ class LikeExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on caseInsensitive and the property name.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(LikeExpression.class).add(caseInsensitive).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return val.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof LikeExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LikeExpression that = (LikeExpression) other;
|
||||
return this.propName.equals(that.propName)
|
||||
&& this.caseInsensitive == that.caseInsensitive
|
||||
&& this.type == that.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
LikeExpression that = (LikeExpression) other;
|
||||
return val.equals(that.val);
|
||||
}
|
||||
|
||||
private static String getValue(String value, boolean caseInsensitive, LikeType type) {
|
||||
if (caseInsensitive) {
|
||||
value = value.toLowerCase();
|
||||
|
||||
@@ -26,6 +26,12 @@ abstract class LogicExpression implements SpiExpression {
|
||||
And(Expression expOne, Expression expTwo) {
|
||||
super(AND, expOne, expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new And(expOne.copyForPlanKey(), expTwo.copyForPlanKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Or extends LogicExpression {
|
||||
@@ -35,11 +41,16 @@ abstract class LogicExpression implements SpiExpression {
|
||||
Or(Expression expOne, Expression expTwo) {
|
||||
super(OR, expOne, expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new Or(expOne.copyForPlanKey(), expTwo.copyForPlanKey());
|
||||
}
|
||||
}
|
||||
|
||||
private final SpiExpression expOne;
|
||||
protected final SpiExpression expOne;
|
||||
|
||||
private final SpiExpression expTwo;
|
||||
protected final SpiExpression expTwo;
|
||||
|
||||
private final String joinType;
|
||||
|
||||
@@ -49,6 +60,7 @@ abstract class LogicExpression implements SpiExpression {
|
||||
this.expTwo = (SpiExpression) expTwo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
expOne.containsMany(desc, manyWhereJoin);
|
||||
expTwo.containsMany(desc, manyWhereJoin);
|
||||
@@ -60,11 +72,13 @@ abstract class LogicExpression implements SpiExpression {
|
||||
expTwo.validate(validation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
expOne.addBindValues(request);
|
||||
expTwo.addBindValues(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append("(");
|
||||
@@ -74,25 +88,47 @@ abstract class LogicExpression implements SpiExpression {
|
||||
request.append(") ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
expOne.prepareExpression(request);
|
||||
expTwo.prepareExpression(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the joinType plus the two expressions.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(LogicExpression.class).add(joinType);
|
||||
expOne.queryAutoTuneHash(builder);
|
||||
expTwo.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
builder.add(LogicExpression.class).add(joinType);
|
||||
expOne.queryPlanHash(request, builder);
|
||||
expTwo.queryPlanHash(request, builder);
|
||||
expOne.queryPlanHash(builder);
|
||||
expTwo.queryPlanHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = expOne.queryBindHash();
|
||||
hc = hc * 31 + expTwo.queryBindHash();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
|
||||
if (!(other instanceof LogicExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LogicExpression that = (LogicExpression) other;
|
||||
return this.joinType.equals(that.joinType)
|
||||
&& this.expOne.isSameByPlan(that.expOne)
|
||||
&& this.expTwo.isSameByPlan(that.expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
LogicExpression that = (LogicExpression) other;
|
||||
return this.expOne.isSameByBind(that.expOne)
|
||||
&& this.expTwo.isSameByBind(that.expTwo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
|
||||
/**
|
||||
* Base abstract expression that does nothing for prepareExpression().
|
||||
*/
|
||||
abstract class NonPrepareExpression implements SpiExpression {
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,11 @@ class NoopExpression implements SpiExpression {
|
||||
|
||||
protected static final NoopExpression INSTANCE = new NoopExpression();
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
|
||||
// nothing to do
|
||||
@@ -26,13 +31,13 @@ class NoopExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NoopExpression.class);
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NoopExpression.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,4 +55,14 @@ class NoopExpression implements SpiExpression {
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
return other instanceof NoopExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ final class NotExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = 5648926732402355781L;
|
||||
|
||||
private static final String NOT = "not (";
|
||||
private static final String NOT_START = "not (";
|
||||
private static final String NOT_END = ") ";
|
||||
|
||||
private final SpiExpression exp;
|
||||
|
||||
@@ -21,6 +22,12 @@ final class NotExpression implements SpiExpression {
|
||||
this.exp = (SpiExpression) exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new NotExpression(exp.copyForPlanKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
exp.containsMany(desc, manyWhereJoin);
|
||||
}
|
||||
@@ -30,31 +37,49 @@ final class NotExpression implements SpiExpression {
|
||||
exp.validate(validation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
exp.addBindValues(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.append(NOT);
|
||||
request.append(NOT_START);
|
||||
exp.addSql(request);
|
||||
request.append(") ");
|
||||
request.append(NOT_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
exp.prepareExpression(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the expression.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NotExpression.class);
|
||||
exp.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
builder.add(NotExpression.class);
|
||||
exp.queryPlanHash(request, builder);
|
||||
exp.queryPlanHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return exp.queryBindHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof NotExpression)) {
|
||||
return false;
|
||||
}
|
||||
NotExpression that = (NotExpression) other;
|
||||
return exp.isSameByPlan(that.exp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
NotExpression that = (NotExpression) other;
|
||||
return exp.isSameByBind(that.exp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -20,13 +20,15 @@ class NullExpression extends AbstractExpression {
|
||||
this.notNull = notNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
String propertyName = propName;
|
||||
|
||||
String nullExpr = notNull ? " is not null " : " is null ";
|
||||
|
||||
@@ -39,17 +41,32 @@ class NullExpression extends AbstractExpression {
|
||||
request.append(propertyName).append(nullExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof NullExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NullExpression that = (NullExpression) other;
|
||||
return this.propName.equals(that.propName)
|
||||
&& this.notNull == that.notNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
// no bind values so always true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on notNull flag and the propertyName.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NullExpression.class).add(notNull).add(propName);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return (notNull ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -8,7 +7,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
class RawExpression implements SpiExpression {
|
||||
class RawExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = 7973903141340334606L;
|
||||
|
||||
@@ -21,6 +20,7 @@ class RawExpression implements SpiExpression {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
}
|
||||
@@ -30,6 +30,7 @@ class RawExpression implements SpiExpression {
|
||||
// always ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
if (values != null) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
@@ -38,6 +39,7 @@ class RawExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.append(sql);
|
||||
}
|
||||
@@ -45,15 +47,40 @@ class RawExpression implements SpiExpression {
|
||||
/**
|
||||
* Based on the sql.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(RawExpression.class).add(sql);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return sql.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof RawExpression)) {
|
||||
return false;
|
||||
}
|
||||
RawExpression that = (RawExpression) other;
|
||||
return sql.equals(that.sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
if (!(other instanceof RawExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RawExpression that = (RawExpression) other;
|
||||
if (values.length != that.values.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (!Same.sameByValue(values[i], that.values[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Utility to help isSame methods.
|
||||
*/
|
||||
public class Same {
|
||||
|
||||
/**
|
||||
* Return true if both values are null or both an not null.
|
||||
*/
|
||||
public static boolean sameByNull(Object v1, Object v2) {
|
||||
return v1 == null ? v2 == null : v2 != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null safe equals check.
|
||||
*/
|
||||
public static boolean sameByValue(Object v1, Object v2) {
|
||||
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.
|
||||
*/
|
||||
public static boolean sameBy(boolean byValue, Object value, Object value1) {
|
||||
|
||||
if (byValue) {
|
||||
return sameByValue(value, value1);
|
||||
} else {
|
||||
return sameByNull(value, value1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -20,16 +20,25 @@ public class SimpleExpression extends AbstractExpression {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public final String getPropName() {
|
||||
return propName;
|
||||
}
|
||||
|
||||
public boolean isOpEquals() {
|
||||
return Op.EQ.equals(type);
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null) {
|
||||
if (prop.isAssocId()) {
|
||||
Object[] ids = prop.getAssocOneIdValues((EntityBean)value);
|
||||
Object[] ids = prop.getAssocOneIdValues((EntityBean) value);
|
||||
if (ids != null) {
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
request.addBindValue(ids[i]);
|
||||
@@ -50,14 +59,13 @@ public class SimpleExpression extends AbstractExpression {
|
||||
request.addBindValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null) {
|
||||
if (prop.isAssocId()) {
|
||||
request.append(prop.getAssocOneIdExpr(propertyName, type.bind()));
|
||||
request.append(prop.getAssocOneIdExpr(propName, type.bind()));
|
||||
return;
|
||||
}
|
||||
if (prop.isDbEncrypted()) {
|
||||
@@ -66,27 +74,37 @@ public class SimpleExpression extends AbstractExpression {
|
||||
return;
|
||||
}
|
||||
}
|
||||
request.append(propertyName).append(type.bind());
|
||||
request.append(propName).append(type.bind());
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the type and propertyName.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(SimpleExpression.class).add(propName).add(type.name());
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof SimpleExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SimpleExpression that = (SimpleExpression) other;
|
||||
return this.propName.equals(that.propName)
|
||||
&& this.type == that.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
SimpleExpression that = (SimpleExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,18 +54,7 @@ public abstract class DLoadBaseContext {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
int queryFetchBatch = queryProps.getQueryFetchBatch();
|
||||
if (queryFetchBatch > 0) {
|
||||
// property join was automatically set to a 'query join'
|
||||
return queryFetchBatch;
|
||||
}
|
||||
|
||||
FetchConfig fetchConfig = queryProps.getFetchConfig();
|
||||
if (fetchConfig == null) {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
int queryBatchSize = fetchConfig.getQueryBatchSize();
|
||||
int queryBatchSize = queryProps.getQueryFetchBatch();
|
||||
if (queryBatchSize == -1) {
|
||||
// not eager query fetch, just lazy loading
|
||||
return batchSize;
|
||||
@@ -84,9 +73,6 @@ public abstract class DLoadBaseContext {
|
||||
return defaultBatchSize;
|
||||
}
|
||||
FetchConfig fetchConfig = queryProps.getFetchConfig();
|
||||
if (fetchConfig == null) {
|
||||
return defaultBatchSize;
|
||||
}
|
||||
if (fetchConfig.isQueryAll()) {
|
||||
return firstBatchSize;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
if (currentBuffer.isFull()) {
|
||||
currentBuffer = createBuffer(secondaryBatchSize);
|
||||
}
|
||||
// set the persistenceContext on the bean first
|
||||
ebi.setBeanLoader(currentBuffer, getPersistenceContext());
|
||||
currentBuffer.add(ebi);
|
||||
}
|
||||
@@ -124,7 +123,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the buffer is full.
|
||||
* Add the bean to the load buffer.
|
||||
*/
|
||||
public void add(EntityBeanIntercept ebi) {
|
||||
if (persistenceContext == null) {
|
||||
@@ -180,11 +179,10 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
}
|
||||
|
||||
if (context.hitCache) {
|
||||
// Check each of the beans in the batch to see if they are in the L2 cache.
|
||||
// check each of the beans in the batch to see if they are in the L2 cache.
|
||||
Iterator<EntityBeanIntercept> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
EntityBeanIntercept bean = iterator.next();
|
||||
if (context.desc.cacheBeanLoad(bean)) {
|
||||
if (context.desc.cacheBeanLoad(iterator.next())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.LoadSecondaryQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuerySecondary;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -58,7 +59,7 @@ public class DLoadContext implements LoadContext {
|
||||
|
||||
private List<OrmQueryProperties> secQuery;
|
||||
|
||||
public DLoadContext(OrmQueryRequest<?> request) {
|
||||
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
|
||||
|
||||
this.persistenceContext = request.getPersistenceContext();
|
||||
this.ebeanServer = request.getServer();
|
||||
@@ -86,6 +87,41 @@ public class DLoadContext implements LoadContext {
|
||||
|
||||
// initialise rootBeanContext after origin and relativePath have been set
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
|
||||
registerSecondaryQueries(secondaryQueries);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the +query and +lazy secondary queries with their appropriate LoadBeanContext or LoadManyContext.
|
||||
*/
|
||||
private void registerSecondaryQueries(SpiQuerySecondary secondaryQueries) {
|
||||
|
||||
this.secQuery = secondaryQueries.getQueryJoins();
|
||||
if (secQuery != null) {
|
||||
for (OrmQueryProperties pathProperties : secQuery) {
|
||||
registerSecondaryQuery(pathProperties);
|
||||
}
|
||||
}
|
||||
|
||||
List<OrmQueryProperties> lazyJoins = secondaryQueries.getLazyJoins();
|
||||
if (lazyJoins != null) {
|
||||
for (OrmQueryProperties lazyJoin : lazyJoins) {
|
||||
registerSecondaryQuery(lazyJoin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the load context at this path with OrmQueryProperties which is
|
||||
* used to build the appropriate query for +query or +lazy loading.
|
||||
*/
|
||||
private void registerSecondaryQuery(OrmQueryProperties props) {
|
||||
|
||||
ElPropertyValue elGetValue = rootDescriptor.getElGetValue(props.getPath());
|
||||
|
||||
boolean many = elGetValue.getBeanProperty().containsMany();
|
||||
registerSecondaryNode(many, props);
|
||||
}
|
||||
|
||||
protected boolean isExcludeBeanCache() {
|
||||
@@ -119,8 +155,7 @@ public class DLoadContext implements LoadContext {
|
||||
|
||||
if (secQuery != null) {
|
||||
for (int i = 0; i < secQuery.size(); i++) {
|
||||
OrmQueryProperties properties = secQuery.get(i);
|
||||
LoadSecondaryQuery load = getLoadSecondaryQuery(properties.getPath());
|
||||
LoadSecondaryQuery load = getLoadSecondaryQuery(secQuery.get(i).getPath());
|
||||
load.loadSecondaryQuery(parentRequest);
|
||||
}
|
||||
}
|
||||
@@ -137,49 +172,6 @@ public class DLoadContext implements LoadContext {
|
||||
return beanLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the +query and +lazy secondary queries and
|
||||
* register them with their appropriate LoadBeanContext
|
||||
* or LoadManyContext.
|
||||
* <p>
|
||||
* The parts of the secondary queries are removed and used
|
||||
* by LoadBeanContext/LoadManyContext to build the appropriate
|
||||
* queries.
|
||||
* </p>
|
||||
*/
|
||||
public void registerSecondaryQueries(SpiQuery<?> query) {
|
||||
|
||||
secQuery = query.removeQueryJoins();
|
||||
if (secQuery != null) {
|
||||
for (int i = 0; i < secQuery.size(); i++) {
|
||||
OrmQueryProperties props = secQuery.get(i);
|
||||
registerSecondaryQuery(props);
|
||||
}
|
||||
}
|
||||
|
||||
List<OrmQueryProperties> lazyQueries = query.removeLazyJoins();
|
||||
if (lazyQueries != null) {
|
||||
for (int i = 0; i < lazyQueries.size(); i++) {
|
||||
OrmQueryProperties lazyProps = lazyQueries.get(i);
|
||||
registerSecondaryQuery(lazyProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the load context at this path with OrmQueryProperties which is
|
||||
* used to build the appropriate query for +query or +lazy loading.
|
||||
*/
|
||||
private void registerSecondaryQuery(OrmQueryProperties props) {
|
||||
|
||||
String propName = props.getPath();
|
||||
ElPropertyValue elGetValue = rootDescriptor.getElGetValue(propName);
|
||||
|
||||
boolean many = elGetValue.getBeanProperty().containsMany();
|
||||
registerSecondaryNode(many, props);
|
||||
}
|
||||
|
||||
|
||||
public ObjectGraphNode getObjectGraphNode(String path) {
|
||||
|
||||
ObjectGraphNode node = nodePathMap.get(path);
|
||||
@@ -247,7 +239,7 @@ public class DLoadContext implements LoadContext {
|
||||
getManyContext(path).register(bc);
|
||||
}
|
||||
|
||||
private DLoadBeanContext getBeanContext(String path) {
|
||||
protected DLoadBeanContext getBeanContext(String path) {
|
||||
if (path == null) {
|
||||
return rootBeanContext;
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
}
|
||||
|
||||
// add the loadedBean to the appropriate collection of lazyLoadParentBean
|
||||
lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, bean);
|
||||
lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, bean, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,7 +542,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
|
||||
while (hasNext()) {
|
||||
EntityBean bean = next();
|
||||
help.add(collection, bean);
|
||||
help.add(collection, bean, false);
|
||||
}
|
||||
|
||||
updateExecutionStatistics();
|
||||
|
||||
@@ -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.fetch(path, pathProps.get(path));
|
||||
}
|
||||
|
||||
// check if @Id property included in RawSql
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -18,6 +13,10 @@ import com.avaje.ebeaninternal.server.type.RsetDataReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Represents a query for a given SQL statement.
|
||||
* <p>
|
||||
@@ -45,7 +44,7 @@ public class CQueryPlan {
|
||||
|
||||
private final boolean autoTuned;
|
||||
|
||||
private final HashQueryPlan hash;
|
||||
private final CQueryPlanKey planKey;
|
||||
|
||||
private final boolean rawSql;
|
||||
|
||||
@@ -79,7 +78,7 @@ public class CQueryPlan {
|
||||
this.server = request.getServer();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.hash = request.getQueryPlanHash();
|
||||
this.planKey = request.getQueryPlanKey();
|
||||
this.autoTuned = request.getQuery().isAutoTuned();
|
||||
if (sqlRes != null) {
|
||||
this.sql = sqlRes.getSql();
|
||||
@@ -103,7 +102,7 @@ public class CQueryPlan {
|
||||
this.server = request.getServer();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.hash = buildHash(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
this.autoTuned = false;
|
||||
this.sql = sql;
|
||||
this.sqlTree = sqlTree;
|
||||
@@ -114,15 +113,13 @@ public class CQueryPlan {
|
||||
}
|
||||
|
||||
|
||||
private HashQueryPlan buildHash(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
builder.add(sql).add(rawSql).add(rowNumberIncluded).add(logWhereSql);
|
||||
builder.addRawSql(sql);
|
||||
return builder.build();
|
||||
private CQueryPlanKey buildPlanKey(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
|
||||
return new RawSqlQueryPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return beanType + " hash:" + hash;
|
||||
return beanType + " hash:" + planKey;
|
||||
}
|
||||
|
||||
public Class<?> getBeanType() {
|
||||
@@ -147,8 +144,8 @@ public class CQueryPlan {
|
||||
return autoTuned;
|
||||
}
|
||||
|
||||
public HashQueryPlan getHash() {
|
||||
return hash;
|
||||
public CQueryPlanKey getPlanKey() {
|
||||
return planKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +161,7 @@ public class CQueryPlan {
|
||||
|
||||
private String calcAuditQueryKey() {
|
||||
// rawSql needs to include the MD5 hash of the sql
|
||||
return rawSql ? hash.getPartialKey() + "_" + getSqlMd5Hash() : hash.getPartialKey();
|
||||
return rawSql ? planKey.getPartialKey() + "_" + getSqlMd5Hash() : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -233,7 +233,7 @@ public final class CQueryPlanStats {
|
||||
|
||||
@Override
|
||||
public String getQueryPlanHash() {
|
||||
return queryPlan.getHash().toString();
|
||||
return queryPlan.getPlanKey().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.RawSql;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.BindParams.OrderedList;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
@@ -23,7 +13,15 @@ import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Compile Query Predicates.
|
||||
@@ -465,7 +463,7 @@ public class CQueryPredicates {
|
||||
/**
|
||||
* Return the bind values for the where expression.
|
||||
*/
|
||||
public ArrayList<Object> getWhereExprBindValues() {
|
||||
public List<Object> getWhereExprBindValues() {
|
||||
return where.getBindValues();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
|
||||
/**
|
||||
* QueryPlanKey for RawSql queries.
|
||||
*/
|
||||
public class RawSqlQueryPlanKey implements CQueryPlanKey {
|
||||
|
||||
private final String sql;
|
||||
private final boolean rawSql;
|
||||
private final boolean rowNumberIncluded;
|
||||
private final String logWhereSql;
|
||||
|
||||
public RawSqlQueryPlanKey(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
this.sql = sql;
|
||||
this.rawSql = rawSql;
|
||||
this.rowNumberIncluded = rowNumberIncluded;
|
||||
this.logWhereSql = logWhereSql;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getPartialKey() + ":r";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as a partial key. For rawSql hash the sql is part of the key and as such
|
||||
* needs to be included in order to have a complete key. Typically the MD5 of the sql
|
||||
* can be used as a short form proxy for the actual sql.
|
||||
*/
|
||||
public String getPartialKey() {
|
||||
return hashCode() + "_0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
RawSqlQueryPlanKey that = (RawSqlQueryPlanKey) o;
|
||||
|
||||
if (rawSql != that.rawSql) return false;
|
||||
if (rowNumberIncluded != that.rowNumberIncluded) return false;
|
||||
if (!sql.equals(that.sql)) return false;
|
||||
return logWhereSql != null ? logWhereSql.equals(that.logWhereSql) : that.logWhereSql == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = sql.hashCode();
|
||||
result = 31 * result + (rawSql ? 1 : 0);
|
||||
result = 31 * result + (rowNumberIncluded ? 1 : 0);
|
||||
result = 31 * result + logWhereSql.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class SqlTreeBuilder {
|
||||
boolean includeJoins = (alias == null) ? false : alias.isIncludeJoins();
|
||||
|
||||
return new SqlTree(summary.toString(), rootNode, selectSql, fromSql, inheritanceWhereSql, encryptedProps,
|
||||
manyProperty, queryDetail.getIncludes(), includeJoins);
|
||||
manyProperty, queryDetail.getFetchPaths(), includeJoins);
|
||||
}
|
||||
|
||||
private String buildSelectClause() {
|
||||
@@ -183,7 +183,7 @@ public class SqlTreeBuilder {
|
||||
rootNode = buildSelectChain(null, null, desc, null);
|
||||
|
||||
if (!rawSql) {
|
||||
alias.addJoin(queryDetail.getIncludes(), desc);
|
||||
alias.addJoin(queryDetail.getFetchPaths(), desc);
|
||||
alias.addJoin(predicates.getPredicateIncludes(), desc);
|
||||
alias.addManyWhereJoins(manyWhereJoins.getPropertyNames());
|
||||
|
||||
@@ -471,7 +471,7 @@ public class SqlTreeBuilder {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queryDetail.includes(propName)) {
|
||||
if (queryDetail.includesPath(propName)) {
|
||||
|
||||
if (manyProperty != null) {
|
||||
// only one many associated allowed to be included in fetch
|
||||
@@ -498,7 +498,7 @@ public class SqlTreeBuilder {
|
||||
*/
|
||||
private boolean isIncludeBean(String prefix) {
|
||||
|
||||
if (queryDetail.includes(prefix)) {
|
||||
if (queryDetail.includesPath(prefix)) {
|
||||
// explicitly included
|
||||
summary.append(", ").append(prefix);
|
||||
String[] splitNames = SplitName.split(prefix);
|
||||
|
||||
@@ -232,6 +232,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
PersistenceContext persistenceContext = (!readId || temporalVersions) ? null : ctx.getPersistenceContext();
|
||||
|
||||
boolean newBean = false;
|
||||
if (readId) {
|
||||
Object id = localIdBinder.readSet(ctx, localBean);
|
||||
if (id == null) {
|
||||
@@ -242,6 +243,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
contextBean = (EntityBean) persistenceContext.putIfAbsent(id, localBean);
|
||||
if (contextBean == null) {
|
||||
// bean just added to the persistenceContext
|
||||
newBean = true;
|
||||
contextBean = localBean;
|
||||
} else {
|
||||
// bean already exists in persistenceContext
|
||||
|
||||
@@ -24,7 +24,7 @@ public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
// being set to the parentBean directly
|
||||
EntityBean detailBean = super.load(cquery, null, null);
|
||||
// initialise the collection and add detailBean if it is not null
|
||||
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean);
|
||||
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, false);
|
||||
return detailBean;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuerySecondary;
|
||||
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
@@ -28,7 +28,7 @@ import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.expression.SimpleExpression;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionList;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionList;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
@@ -234,7 +234,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
/**
|
||||
* Hash of final query after AutoTune tuning.
|
||||
*/
|
||||
private HashQueryPlan queryPlanHash;
|
||||
private CQueryPlanKey queryPlanKey;
|
||||
|
||||
private transient PersistenceContext persistenceContext;
|
||||
|
||||
@@ -345,20 +345,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
this.beanDescriptor = beanDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if select all properties was used to ensure the property invoking a lazy load was
|
||||
* included in the query.
|
||||
*/
|
||||
public boolean selectAllForLazyLoadProperty() {
|
||||
if (lazyLoadProperty != null) {
|
||||
if (!detail.containsProperty(lazyLoadProperty)) {
|
||||
detail.select("*");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RawSql getRawSql() {
|
||||
return rawSql;
|
||||
}
|
||||
@@ -390,22 +376,38 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return expressionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the where expressions contains a many property.
|
||||
*/
|
||||
public boolean initManyWhereJoins() {
|
||||
private void createExtraJoinsToSupportManyWhereClause() {
|
||||
manyWhereJoins = new ManyWhereJoins();
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.containsMany(beanDescriptor, manyWhereJoins);
|
||||
}
|
||||
return !manyWhereJoins.isEmpty();
|
||||
if (!manyWhereJoins.isEmpty()) {
|
||||
setSqlDistinct(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra joins required to support the where clause for 'Many' properties.
|
||||
*/
|
||||
public ManyWhereJoins getManyWhereJoins() {
|
||||
return manyWhereJoins;
|
||||
}
|
||||
|
||||
public List<OrmQueryProperties> removeQueryJoins() {
|
||||
/**
|
||||
* Return true if select all properties was used to ensure the property invoking a lazy load was
|
||||
* included in the query.
|
||||
*/
|
||||
public boolean selectAllForLazyLoadProperty() {
|
||||
if (lazyLoadProperty != null) {
|
||||
if (!detail.containsProperty(lazyLoadProperty)) {
|
||||
detail.select("*");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List<OrmQueryProperties> removeQueryJoins() {
|
||||
List<OrmQueryProperties> queryJoins = detail.removeSecondaryQueries();
|
||||
if (queryJoins != null) {
|
||||
if (orderBy != null) {
|
||||
@@ -433,7 +435,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
public List<OrmQueryProperties> removeLazyJoins() {
|
||||
protected List<OrmQueryProperties> removeLazyJoins() {
|
||||
return detail.removeSecondaryLazyQueries();
|
||||
}
|
||||
|
||||
@@ -441,11 +443,63 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
this.lazyLoadManyPath = lazyLoadManyPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiQuerySecondary convertJoins() {
|
||||
|
||||
createExtraJoinsToSupportManyWhereClause();
|
||||
markQueryJoins();
|
||||
|
||||
return new OrmQuerySecondary(removeQueryJoins(), removeLazyJoins());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert any many joins fetch joins to query joins.
|
||||
* Limit the number of fetch joins to Many properties, mark as query joins as needed.
|
||||
*/
|
||||
public void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch) {
|
||||
detail.convertManyFetchJoinsToQueryJoins(beanDescriptor, lazyLoadManyPath, allowOne, queryBatch);
|
||||
private void markQueryJoins() {
|
||||
detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch());
|
||||
}
|
||||
|
||||
private boolean isAllowOneManyFetch() {
|
||||
|
||||
if (Mode.LAZYLOAD_MANY.equals(getMode())) {
|
||||
return false;
|
||||
} else if (hasMaxRowsOrFirstRow() && !isRawSql() && !isSqlSelect()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void setOrmQueryDetail(OrmQueryDetail detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public void setDefaultSelectClause() {
|
||||
detail.setDefaultSelectClause(beanDescriptor);
|
||||
}
|
||||
|
||||
public void setDetail(OrmQueryDetail detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) {
|
||||
return detail.tuneFetchProperties(tunedDetail);
|
||||
}
|
||||
|
||||
public OrmQueryDetail getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public ExpressionList<T> filterMany(String prop) {
|
||||
|
||||
OrmQueryProperties chunk = detail.getChunk(prop, true);
|
||||
return chunk.filterMany(this);
|
||||
}
|
||||
|
||||
public void setFilterMany(String prop, ExpressionList<?> filterMany) {
|
||||
if (filterMany != null) {
|
||||
OrmQueryProperties chunk = detail.getChunk(prop, true);
|
||||
chunk.setFilterMany((SpiExpressionList<?>) filterMany);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,7 +520,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public void setSelectId() {
|
||||
// clear select and fetch joins..
|
||||
detail.clear();
|
||||
|
||||
select(beanDescriptor.getIdBinder().getIdProperty());
|
||||
}
|
||||
|
||||
@@ -493,7 +546,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
if (se instanceof SimpleExpression) {
|
||||
SimpleExpression e = (SimpleExpression) se;
|
||||
if (e.isOpEquals()) {
|
||||
return new NaturalKeyBindParam(e.getPropertyName(), e.getValue());
|
||||
return new NaturalKeyBindParam(e.getPropName(), e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -759,79 +812,38 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
/**
|
||||
* Calculate the query hash for either AutoTune query tuning or Query Plan caching.
|
||||
*/
|
||||
private HashQueryPlan calculateHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
CQueryPlanKey createQueryPlanKey() {
|
||||
|
||||
// exclude bind values and things unrelated to the sql being generated
|
||||
queryPlanKey = new OrmQueryPlanKey(includeTableJoin, type, detail, maxRows, firstRow,
|
||||
disableLazyLoading, rawWhereClause, orderBy, query, additionalWhere, additionalHaving,
|
||||
distinct, sqlDistinct, mapKey, id, bindParams, whereExpressions, havingExpressions,
|
||||
temporalMode, forUpdate, rootTableAlias, rawSql);
|
||||
|
||||
if (builder == null) {
|
||||
builder = new HashQueryPlanBuilder();
|
||||
}
|
||||
|
||||
builder.add((type == null ? 0 : type.ordinal() + 1));
|
||||
builder.add(autoTuned).add(distinct).add(sqlDistinct).add(query);
|
||||
builder.add(firstRow).add(maxRows).add(orderBy).add(forUpdate);
|
||||
builder.add(rawWhereClause).add(additionalWhere).add(additionalHaving);
|
||||
builder.add(mapKey);
|
||||
builder.add(disableLazyLoading);
|
||||
builder.add(id != null);
|
||||
builder.add(temporalMode);
|
||||
builder.add(rawSql == null ? 0 : rawSql.queryHash());
|
||||
builder.add(includeTableJoin != null ? includeTableJoin.queryHash() : 0);
|
||||
builder.add(rootTableAlias);
|
||||
|
||||
if (detail != null) {
|
||||
detail.queryPlanHash(request, builder);
|
||||
}
|
||||
if (bindParams != null) {
|
||||
bindParams.buildQueryPlanHash(builder);
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
// for AutoTune...
|
||||
builder.add(true);
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.queryAutoTuneHash(builder);
|
||||
}
|
||||
if (havingExpressions != null) {
|
||||
havingExpressions.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
} else {
|
||||
// for query plan...
|
||||
builder.add(false);
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.queryPlanHash(request, builder);
|
||||
}
|
||||
if (havingExpressions != null) {
|
||||
havingExpressions.queryPlanHash(request, builder);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
return queryPlanKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash used by AutoTune to identify when a query has changed (and hence potentially
|
||||
* needs a new tuned query plan to be developed).
|
||||
* Prepare the query which prepares any expressions (sub-query expressions etc) and calculates the query plan key.
|
||||
*/
|
||||
public HashQueryPlan queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
public CQueryPlanKey prepare(BeanQueryRequest<?> request) {
|
||||
|
||||
return calculateHash(null, builder);
|
||||
prepareExpressions(request);
|
||||
|
||||
queryPlanKey = createQueryPlanKey();
|
||||
return queryPlanKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash that should be unique for the generated SQL across a given bean type.
|
||||
* <p>
|
||||
* This can used to enable the caching and reuse of a 'query plan'.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is calculated AFTER AutoTune query tuning has occurred.
|
||||
* </p>
|
||||
* Prepare the expressions (compile sub-queries etc).
|
||||
*/
|
||||
public HashQueryPlan queryPlanHash(BeanQueryRequest<?> request) {
|
||||
private void prepareExpressions(BeanQueryRequest<?> request) {
|
||||
|
||||
queryPlanHash = calculateHash(request, null);
|
||||
return queryPlanHash;
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.prepareExpression(request);
|
||||
}
|
||||
if (havingExpressions != null) {
|
||||
havingExpressions.prepareExpression(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -863,7 +875,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
// so queryPlanHash is calculated well before this method is called
|
||||
int hc = queryBindHash();
|
||||
|
||||
return new HashQuery(queryPlanHash, hc);
|
||||
return new HashQuery(queryPlanKey, hc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -974,18 +986,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
parser.assign(this);
|
||||
}
|
||||
|
||||
protected void setOrmQueryDetail(OrmQueryDetail detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
protected void setRawWhereClause(String rawWhereClause) {
|
||||
this.rawWhereClause = rawWhereClause;
|
||||
}
|
||||
|
||||
public void setDefaultSelectClause() {
|
||||
detail.setDefaultSelectClause(beanDescriptor);
|
||||
}
|
||||
|
||||
public DefaultOrmQuery<T> select(String columns) {
|
||||
detail.select(columns);
|
||||
return this;
|
||||
@@ -1004,7 +1009,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
public DefaultOrmQuery<T> fetch(String property, String columns, FetchConfig config) {
|
||||
detail.addFetch(property, columns, config);
|
||||
detail.fetch(property, columns, config);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1207,18 +1212,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
public void setDetail(OrmQueryDetail detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) {
|
||||
return detail.tuneFetchProperties(tunedDetail);
|
||||
}
|
||||
|
||||
public OrmQueryDetail getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Query [" + whereExpressions + "]";
|
||||
}
|
||||
@@ -1312,19 +1305,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return whereExpressions;
|
||||
}
|
||||
|
||||
public ExpressionList<T> filterMany(String prop) {
|
||||
|
||||
OrmQueryProperties chunk = detail.getChunk(prop, true);
|
||||
return chunk.filterMany(this);
|
||||
}
|
||||
|
||||
public void setFilterMany(String prop, ExpressionList<?> filterMany) {
|
||||
if (filterMany != null) {
|
||||
OrmQueryProperties chunk = detail.getChunk(prop, true);
|
||||
chunk.setFilterMany((SpiExpressionList<?>) filterMany);
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultOrmQuery<T> having(String addToHavingClause) {
|
||||
if (additionalHaving == null) {
|
||||
additionalHaving = addToHavingClause;
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.FetchConfig;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
@@ -22,6 +8,17 @@ import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the internal structure of an Object Relational query.
|
||||
* <p>
|
||||
@@ -45,9 +42,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
/**
|
||||
* 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 LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>();
|
||||
|
||||
/**
|
||||
* Return a deep copy of the OrmQueryDetail.
|
||||
@@ -58,34 +53,32 @@ public class OrmQueryDetail implements Serializable {
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
|
||||
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
copy.includes = new LinkedHashSet<String>(includes);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int queryPlanHash() {
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
queryPlanHash(builder);
|
||||
return builder.getPlanHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
baseProps.queryPlanHash(builder);
|
||||
if (fetchPaths != null) {
|
||||
for (OrmQueryProperties p : fetchPaths.values()) {
|
||||
p.queryPlanHash(request, builder);
|
||||
p.queryPlanHash(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
@@ -94,10 +87,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;
|
||||
}
|
||||
}
|
||||
@@ -105,21 +104,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();
|
||||
@@ -133,11 +165,11 @@ public class OrmQueryDetail implements Serializable {
|
||||
* set the properties to include on the base / root entity.
|
||||
*/
|
||||
public void select(String columns) {
|
||||
baseProps = new OrmQueryProperties(null, columns);
|
||||
baseProps = new OrmQueryProperties(null, columns, null);
|
||||
}
|
||||
|
||||
public boolean containsProperty(String property) {
|
||||
return baseProps == null || baseProps.isIncluded(property);
|
||||
return baseProps.isIncluded(property);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,17 +206,12 @@ public class OrmQueryDetail implements Serializable {
|
||||
Collections.sort(matchingPaths);
|
||||
|
||||
// the list of secondary queries
|
||||
ArrayList<OrmQueryProperties> props = new ArrayList<OrmQueryProperties>(2);
|
||||
ArrayList<OrmQueryProperties> props = new ArrayList<OrmQueryProperties>();
|
||||
|
||||
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 {
|
||||
if (secQuery != null) {
|
||||
props.add(secQuery);
|
||||
|
||||
// remove any child properties for this path
|
||||
@@ -195,7 +222,6 @@ public class OrmQueryDetail implements Serializable {
|
||||
// remove join to secondary query from the main query
|
||||
// and add to this secondary query
|
||||
pass2It.remove();
|
||||
includes.remove(pass2Prop.getPath());
|
||||
secQuery.add(pass2Prop);
|
||||
}
|
||||
}
|
||||
@@ -224,29 +250,24 @@ public class OrmQueryDetail implements Serializable {
|
||||
OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false);
|
||||
if (tunedRoot != null) {
|
||||
tuned = true;
|
||||
baseProps.setTunedProperties(tunedRoot);
|
||||
|
||||
baseProps = tunedRoot;
|
||||
for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) {
|
||||
OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false);
|
||||
if (chunk != null) {
|
||||
// set the properties to select
|
||||
chunk.setTunedProperties(tunedChunk);
|
||||
} else {
|
||||
// add a missing join
|
||||
putFetchPath(tunedChunk.copy());
|
||||
}
|
||||
fetch(tunedChunk.copy());
|
||||
}
|
||||
}
|
||||
return tuned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a join() method of the query.
|
||||
* Add or replace the fetch detail.
|
||||
*/
|
||||
public void putFetchPath(OrmQueryProperties chunk) {
|
||||
protected void fetch(OrmQueryProperties chunk) {
|
||||
String path = chunk.getPath();
|
||||
fetchPaths.put(path, chunk);
|
||||
includes.add(path);
|
||||
if (path == null) {
|
||||
baseProps = chunk;
|
||||
} else {
|
||||
fetchPaths.put(path, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +277,6 @@ public class OrmQueryDetail implements Serializable {
|
||||
* </p>
|
||||
*/
|
||||
public void clear() {
|
||||
includes.clear();
|
||||
fetchPaths.clear();
|
||||
}
|
||||
|
||||
@@ -266,26 +286,33 @@ public class OrmQueryDetail implements Serializable {
|
||||
* @param path the property to join
|
||||
* @param partialProps the properties on the join property to include
|
||||
*/
|
||||
public void addFetch(String path, String partialProps, FetchConfig fetchConfig) {
|
||||
public void fetch(String path, String partialProps, FetchConfig fetchConfig) {
|
||||
|
||||
OrmQueryProperties chunk = getChunk(path, true);
|
||||
chunk.setProperties(partialProps);
|
||||
chunk.setFetchConfig(fetchConfig);
|
||||
fetch(new OrmQueryProperties(path, partialProps, fetchConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add for raw sql etc when the properties are already parsed into a set.
|
||||
*/
|
||||
public void fetch(String path, LinkedHashSet<String> properties) {
|
||||
fetch(new OrmQueryProperties(path, properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the fetch paths into depth order adding any missing parent paths if necessary.
|
||||
*/
|
||||
public void sortFetchPaths(BeanDescriptor<?> d) {
|
||||
|
||||
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>(fetchPaths.size());
|
||||
|
||||
for (OrmQueryProperties p : fetchPaths.values()) {
|
||||
sortFetchPaths(d, p, sorted);
|
||||
if (!fetchPaths.isEmpty()) {
|
||||
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>();
|
||||
for (OrmQueryProperties p : fetchPaths.values()) {
|
||||
sortFetchPaths(d, p, sorted);
|
||||
}
|
||||
fetchPaths = sorted;
|
||||
}
|
||||
|
||||
fetchPaths = sorted;
|
||||
}
|
||||
|
||||
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p,
|
||||
LinkedHashMap<String, OrmQueryProperties> sorted) {
|
||||
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p, LinkedHashMap<String, OrmQueryProperties> sorted) {
|
||||
|
||||
String path = p.getPath();
|
||||
if (!sorted.containsKey(path)) {
|
||||
@@ -298,8 +325,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
if (parentProp == null) {
|
||||
ElPropertyValue el = d.getElGetValue(parentPath);
|
||||
if (el == null) {
|
||||
String msg = "Path [" + parentPath + "] not valid from " + d.getFullName();
|
||||
throw new PersistenceException(msg);
|
||||
throw new PersistenceException("Path [" + parentPath + "] not valid from " + d.getFullName());
|
||||
}
|
||||
// add a missing parent path just fetching the Id property
|
||||
BeanPropertyAssoc<?> assocOne = (BeanPropertyAssoc<?>) el.getBeanProperty();
|
||||
@@ -313,12 +339,9 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert 'fetch joins' to 'many' properties over to 'query joins'.
|
||||
* Mark 'fetch joins' to 'many' properties over to 'query joins' where needed.
|
||||
*/
|
||||
public void convertManyFetchJoinsToQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath,
|
||||
boolean allowOne, int queryBatch) {
|
||||
|
||||
ArrayList<OrmQueryProperties> manyChunks = new ArrayList<OrmQueryProperties>(3);
|
||||
public void markQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath, boolean allowOne) {
|
||||
|
||||
// the name of the many fetch property if there is one
|
||||
String manyFetchProperty = null;
|
||||
@@ -334,8 +357,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
|
||||
// this is a join to a *ToMany
|
||||
OrmQueryProperties chunk = fetchPaths.get(fetchPath);
|
||||
if (chunk.isFetchJoin() && !isLazyLoadManyRoot(lazyLoadManyPath, chunk)
|
||||
&& !hasParentSecJoin(lazyLoadManyPath, chunk)) {
|
||||
if (isQueryJoinCandidate(lazyLoadManyPath, chunk)) {
|
||||
// this is a 'fetch join' (included in main query)
|
||||
if (fetchJoinFirstMany) {
|
||||
// letting the first one remain a 'fetch join'
|
||||
@@ -343,16 +365,20 @@ public class OrmQueryDetail implements Serializable {
|
||||
manyFetchProperty = fetchPath;
|
||||
} else {
|
||||
// convert this one over to a 'query join'
|
||||
manyChunks.add(chunk);
|
||||
chunk.markForQueryJoin();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 path is a candidate for converting to a query join.
|
||||
*/
|
||||
private boolean isQueryJoinCandidate(String lazyLoadManyPath, OrmQueryProperties chunk) {
|
||||
return chunk.isFetchJoin()
|
||||
&& !isLazyLoadManyRoot(lazyLoadManyPath, chunk)
|
||||
&& !hasParentSecJoin(lazyLoadManyPath, chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,32 +423,28 @@ 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());
|
||||
baseProps = new OrmQueryProperties(null, desc.getDefaultSelectClause());
|
||||
}
|
||||
|
||||
for (OrmQueryProperties joinProps : fetchPaths.values()) {
|
||||
if (!joinProps.hasSelectClause()) {
|
||||
BeanDescriptor<?> assocDesc = desc.getBeanDescriptor(joinProps.getPath());
|
||||
if (assocDesc.hasDefaultSelectClause()) {
|
||||
// use the default select clause
|
||||
joinProps.setDefaultProperties(assocDesc.getDefaultSelectClause(), assocDesc.getDefaultSelectClauseSet());
|
||||
fetch(joinProps.getPath(), assocDesc.getDefaultSelectClause(), joinProps.getFetchConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.allProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,7 +473,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
OrmQueryProperties props = fetchPaths.get(path);
|
||||
if (create && props == null) {
|
||||
props = new OrmQueryProperties(path);
|
||||
putFetchPath(props);
|
||||
fetch(props);
|
||||
return props;
|
||||
|
||||
} else {
|
||||
@@ -460,9 +482,9 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property is included.
|
||||
* Return true if the fetch path is included.
|
||||
*/
|
||||
public boolean includes(String path) {
|
||||
public boolean includesPath(String path) {
|
||||
|
||||
OrmQueryProperties chunk = fetchPaths.get(path);
|
||||
|
||||
@@ -471,9 +493,9 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property includes for this detail.
|
||||
* Return the fetch paths for this detail.
|
||||
*/
|
||||
public HashSet<String> getIncludes() {
|
||||
return includes;
|
||||
public Set<String> getFetchPaths() {
|
||||
return fetchPaths.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ public class OrmQueryDetailParser {
|
||||
|
||||
public OrmQueryDetail parse() throws PersistenceException {
|
||||
|
||||
if (parser.isEmpty()) return detail;
|
||||
|
||||
parser.nextWord();
|
||||
processInitial();
|
||||
return detail;
|
||||
@@ -62,8 +64,7 @@ public class OrmQueryDetailParser {
|
||||
|
||||
private void process() {
|
||||
if (isFetch()) {
|
||||
OrmQueryProperties props = readFindFetch();
|
||||
detail.putFetchPath(props);
|
||||
detail.fetch(readFindFetch());
|
||||
|
||||
} else if (parser.isMatch("where")) {
|
||||
readWhere();
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
/**
|
||||
* Query plan key for ORM queries.
|
||||
*/
|
||||
public class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
|
||||
private final TableJoin includeTableJoin;
|
||||
private final String orderByAsSting;
|
||||
private final OrmQueryDetail detail;
|
||||
private final SpiExpression where;
|
||||
private final SpiExpression having;
|
||||
private final RawSql.Key rawSqlKey;
|
||||
private final boolean hasIdValue;
|
||||
private final SpiQuery.Type type;
|
||||
private final int maxRows;
|
||||
private final int firstRow;
|
||||
private final boolean disableLazyLoading;
|
||||
private final String rawWhereClause;
|
||||
private final String query;
|
||||
private final String additionalWhere;
|
||||
private final String additionalHaving;
|
||||
private final boolean distinct;
|
||||
private final boolean sqlDistinct;
|
||||
private final String mapKey;
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
private final boolean forUpdate;
|
||||
private final String rootTableAlias;
|
||||
|
||||
private final int planHash;
|
||||
private final int bindCount;
|
||||
|
||||
public OrmQueryPlanKey(TableJoin includeTableJoin, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading, String rawWhereClause, OrderBy<?> orderBy, String query, String additionalWhere, String additionalHaving, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams, SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode, boolean forUpdate, String rootTableAlias, RawSql rawSql) {
|
||||
|
||||
this.includeTableJoin = includeTableJoin;
|
||||
this.type = type;
|
||||
this.detail = detail;
|
||||
this.maxRows = maxRows;
|
||||
this.firstRow = firstRow;
|
||||
this.disableLazyLoading = disableLazyLoading;
|
||||
this.rawWhereClause = rawWhereClause;
|
||||
this.orderByAsSting = (orderBy == null) ? null : orderBy.toStringFormat();
|
||||
this.query = query;
|
||||
this.additionalWhere = additionalWhere;
|
||||
this.additionalHaving = additionalHaving;
|
||||
this.distinct = distinct;
|
||||
this.sqlDistinct = sqlDistinct;
|
||||
this.mapKey = mapKey;
|
||||
this.hasIdValue = (id != null);
|
||||
this.where = (whereExpressions == null) ? null : whereExpressions.copyForPlanKey();
|
||||
this.having = (havingExpressions == null) ? null : havingExpressions.copyForPlanKey();
|
||||
this.temporalMode = temporalMode;
|
||||
this.forUpdate = forUpdate;
|
||||
this.rootTableAlias = rootTableAlias;
|
||||
this.rawSqlKey = (rawSql == null) ? null : rawSql.getKey();
|
||||
|
||||
// exclude bind values and things unrelated to the sql being generated
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
|
||||
builder.add((type == null ? 0 : type.ordinal() + 1));
|
||||
builder.add(distinct).add(sqlDistinct).add(query);
|
||||
builder.add(firstRow).add(maxRows);
|
||||
builder.add(orderBy).add(forUpdate);
|
||||
builder.add(rawWhereClause).add(additionalWhere).add(additionalHaving);
|
||||
builder.add(mapKey);
|
||||
builder.add(disableLazyLoading);
|
||||
builder.add(hasIdValue);
|
||||
builder.add(temporalMode);
|
||||
builder.add(rawSqlKey == null ? 0 : rawSqlKey.hashCode());
|
||||
builder.add(includeTableJoin != null ? includeTableJoin.queryHash() : 0);
|
||||
builder.add(rootTableAlias);
|
||||
|
||||
if (detail != null) {
|
||||
detail.queryPlanHash(builder);
|
||||
}
|
||||
if (bindParams != null) {
|
||||
bindParams.buildQueryPlanHash(builder);
|
||||
}
|
||||
if (where != null) {
|
||||
where.queryPlanHash(builder);
|
||||
}
|
||||
if (having != null) {
|
||||
having.queryPlanHash(builder);
|
||||
}
|
||||
|
||||
this.planHash = builder.getPlanHash();
|
||||
this.bindCount = builder.getBindCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPartialKey() {
|
||||
return planHash + "_" + bindCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
OrmQueryPlanKey that = (OrmQueryPlanKey) o;
|
||||
|
||||
if (planHash != that.planHash) return false;
|
||||
if (bindCount != that.bindCount) return false;
|
||||
if (maxRows != that.maxRows) return false;
|
||||
if (firstRow != that.firstRow) return false;
|
||||
if (disableLazyLoading != that.disableLazyLoading) return false;
|
||||
if (distinct != that.distinct) return false;
|
||||
if (sqlDistinct != that.sqlDistinct) return false;
|
||||
if (forUpdate != that.forUpdate) return false;
|
||||
if (hasIdValue != that.hasIdValue) return false;
|
||||
if (type != that.type) return false;
|
||||
if (temporalMode != that.temporalMode) return false;
|
||||
if (includeTableJoin != null ? !includeTableJoin.equals(that.includeTableJoin) : that.includeTableJoin != null) return false;
|
||||
if (orderByAsSting != null ? !orderByAsSting.equals(that.orderByAsSting) : that.orderByAsSting != null) return false;
|
||||
if (where != null ? !where.isSameByPlan(that.where) : that.where != null) return false;
|
||||
if (having != null ? !having.isSameByPlan(that.having) : that.having != null) return false;
|
||||
if (rawSqlKey != null ? !rawSqlKey.equals(that.rawSqlKey) : that.rawSqlKey != null) return false;
|
||||
|
||||
// if (detail != null ? !detail.equals(that.detail) : that.detail != null) return false;
|
||||
|
||||
if (rawWhereClause != null ? !rawWhereClause.equals(that.rawWhereClause) : that.rawWhereClause != null) return false;
|
||||
if (query != null ? !query.equals(that.query) : that.query != null) return false;
|
||||
if (additionalWhere != null ? !additionalWhere.equals(that.additionalWhere) : that.additionalWhere != null) return false;
|
||||
if (additionalHaving != null ? !additionalHaving.equals(that.additionalHaving) : that.additionalHaving != null) return false;
|
||||
if (mapKey != null ? !mapKey.equals(that.mapKey) : that.mapKey != null) return false;
|
||||
return rootTableAlias != null ? rootTableAlias.equals(that.rootTableAlias) : that.rootTableAlias == null;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,25 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.ExpressionFactory;
|
||||
import com.avaje.ebean.FetchConfig;
|
||||
import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
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.expression.FilterExpressionList;
|
||||
import com.avaje.ebeaninternal.server.expression.Same;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.util.FilterExpressionList;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the Properties of an Object Relational query.
|
||||
@@ -30,36 +28,27 @@ public class OrmQueryProperties implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8785582703966455658L;
|
||||
|
||||
private String parentPath;
|
||||
private String path;
|
||||
protected static final FetchConfig DEFAULT_FETCH = new FetchConfig();
|
||||
|
||||
private String properties;
|
||||
private final String parentPath;
|
||||
private final String path;
|
||||
|
||||
private String trimmedProperties;
|
||||
private final String rawProperties;
|
||||
private final String trimmedProperties;
|
||||
|
||||
private final LinkedHashSet<String> included;
|
||||
|
||||
private final FetchConfig fetchConfig;
|
||||
|
||||
/**
|
||||
* NB: -1 means no +query, 0 means use the default batch size.
|
||||
* Flag set when this fetch path needs to be a query join.
|
||||
*/
|
||||
private int queryFetchBatch = -1;
|
||||
private boolean queryFetchAll;
|
||||
|
||||
/**
|
||||
* NB: -1 means no +lazy, 0 means use the default batch size.
|
||||
*/
|
||||
private int lazyFetchBatch = -1;
|
||||
|
||||
private FetchConfig fetchConfig;
|
||||
private boolean markForQueryJoin;
|
||||
|
||||
private boolean cache;
|
||||
|
||||
private boolean readOnly;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Included bean joins.
|
||||
*/
|
||||
@@ -73,8 +62,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
private List<OrmQueryProperties> secondaryChildren;
|
||||
|
||||
/**
|
||||
* OrderBy properties that where on the main query but moved here as they relate to this (query
|
||||
* join).
|
||||
* OrderBy properties that where on the main query but moved here as they relate to this (query join).
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private OrderBy orderBy;
|
||||
@@ -86,23 +74,103 @@ public class OrmQueryProperties implements Serializable {
|
||||
private SpiExpressionList filterMany;
|
||||
|
||||
/**
|
||||
* Construct with a given path (null == root path).
|
||||
* Construct for root so path (and parentPath) are null.
|
||||
*/
|
||||
public OrmQueryProperties() {
|
||||
this((String)null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with a given path.
|
||||
*/
|
||||
public OrmQueryProperties(String path) {
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
this.rawProperties = null;
|
||||
this.trimmedProperties = null;
|
||||
this.included = null;
|
||||
this.fetchConfig = DEFAULT_FETCH;
|
||||
}
|
||||
|
||||
public OrmQueryProperties() {
|
||||
this(null);
|
||||
public OrmQueryProperties(String path, String rawProperties) {
|
||||
this(path, rawProperties, null);
|
||||
}
|
||||
|
||||
public OrmQueryProperties(String path, String rawProperties, FetchConfig fetchConfig) {
|
||||
|
||||
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
|
||||
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
this.rawProperties = rawProperties;
|
||||
this.trimmedProperties = response.properties;
|
||||
this.included = response.included;
|
||||
this.cache = response.cache;
|
||||
this.readOnly = response.readOnly;
|
||||
if (fetchConfig != null) {
|
||||
this.fetchConfig = fetchConfig;
|
||||
} else {
|
||||
this.fetchConfig = response.fetchConfig;
|
||||
}
|
||||
}
|
||||
|
||||
public OrmQueryProperties(String path, LinkedHashSet<String> parsedProperties) {
|
||||
if (parsedProperties == null) {
|
||||
throw new IllegalArgumentException("parsedProperties is null");
|
||||
}
|
||||
|
||||
this.path = path;
|
||||
this.parentPath = SplitName.parent(path);
|
||||
// for rawSql parsedProperties can be empty (when only fetching Id property)
|
||||
this.included = parsedProperties;
|
||||
this.rawProperties = join(parsedProperties);
|
||||
this.trimmedProperties = rawProperties;
|
||||
this.cache = false;
|
||||
this.readOnly = false;
|
||||
this.fetchConfig = DEFAULT_FETCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by query language parser.
|
||||
* Join the set of properties into a comma delimited string.
|
||||
*/
|
||||
public OrmQueryProperties(String path, String properties) {
|
||||
this(path);
|
||||
setProperties(properties);
|
||||
private String join(LinkedHashSet<String> parsedProperties) {
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
boolean first = true;
|
||||
for (String property : parsedProperties) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(property);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
private OrmQueryProperties(OrmQueryProperties source) {
|
||||
|
||||
this.parentPath = source.parentPath;
|
||||
this.path = source.path;
|
||||
this.rawProperties = source.rawProperties;
|
||||
this.trimmedProperties = source.trimmedProperties;
|
||||
this.cache = source.cache;
|
||||
this.readOnly = source.readOnly;
|
||||
this.fetchConfig = source.fetchConfig;
|
||||
this.filterMany = source.filterMany;
|
||||
this.included = (source.included == null) ? null : new LinkedHashSet<String>(source.included);
|
||||
if (includedBeanJoin != null) {
|
||||
this.includedBeanJoin = new HashSet<String>(source.includedBeanJoin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the OrmQueryProperties.
|
||||
*/
|
||||
public OrmQueryProperties copy() {
|
||||
return new OrmQueryProperties(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,49 +184,10 @@ public class OrmQueryProperties implements Serializable {
|
||||
orderBy.add(orderProp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Fetch configuration options for this path.
|
||||
*/
|
||||
public void setFetchConfig(FetchConfig fetchConfig) {
|
||||
if (fetchConfig != null) {
|
||||
this.fetchConfig = fetchConfig;
|
||||
lazyFetchBatch = fetchConfig.getLazyBatchSize();
|
||||
queryFetchBatch = fetchConfig.getQueryBatchSize();
|
||||
queryFetchAll = fetchConfig.isQueryAll();
|
||||
}
|
||||
}
|
||||
|
||||
public FetchConfig getFetchConfig() {
|
||||
return fetchConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the comma delimited properties to fetch for this path.
|
||||
* <p>
|
||||
* This can include the +query and +lazy type hints.
|
||||
* </p>
|
||||
*/
|
||||
public void setProperties(String properties) {
|
||||
this.properties = properties;
|
||||
this.trimmedProperties = properties;
|
||||
parseProperties();
|
||||
|
||||
if (!isAllProperties()) {
|
||||
Set<String> parsed = parseIncluded(trimmedProperties);
|
||||
if (parsed.contains("*")) {
|
||||
this.included = null;
|
||||
} else {
|
||||
this.included = parsed;
|
||||
}
|
||||
} else {
|
||||
this.included = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllProperties() {
|
||||
return (trimmedProperties == null) || (trimmedProperties.length() == 0) || "*".equals(trimmedProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expressions used to filter on this path. This should be a many path to use this
|
||||
* method.
|
||||
@@ -171,9 +200,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
|
||||
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
|
||||
// by default we need to make this a 'query join' now
|
||||
queryFetchAll = true;
|
||||
queryFetchBatch = 100;
|
||||
lazyFetchBatch = 100;
|
||||
markForQueryJoin = true;
|
||||
}
|
||||
return filterMany;
|
||||
}
|
||||
@@ -202,28 +229,6 @@ public class OrmQueryProperties implements Serializable {
|
||||
this.filterMany = filterMany;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the properties from deployment default FetchTypes.
|
||||
*/
|
||||
public void setDefaultProperties(String properties, Set<String> included) {
|
||||
this.properties = properties;
|
||||
this.trimmedProperties = properties;
|
||||
this.included = included;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the properties from a matching AutoTune tuned properties.
|
||||
*/
|
||||
public void setTunedProperties(OrmQueryProperties tunedProperties) {
|
||||
if (tunedProperties.hasProperties()) {
|
||||
this.properties = tunedProperties.properties;
|
||||
this.trimmedProperties = tunedProperties.trimmedProperties;
|
||||
this.included = tunedProperties.included;
|
||||
this.queryFetchBatch = Math.max(queryFetchBatch, tunedProperties.queryFetchBatch);
|
||||
this.lazyFetchBatch = Math.max(lazyFetchBatch, tunedProperties.lazyFetchBatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the select and joins for this query.
|
||||
*/
|
||||
@@ -258,30 +263,6 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the OrmQueryProperties.
|
||||
*/
|
||||
public OrmQueryProperties copy() {
|
||||
OrmQueryProperties copy = new OrmQueryProperties();
|
||||
copy.parentPath = parentPath;
|
||||
copy.path = path;
|
||||
copy.properties = properties;
|
||||
copy.trimmedProperties = trimmedProperties;
|
||||
copy.cache = cache;
|
||||
copy.readOnly = readOnly;
|
||||
copy.queryFetchAll = queryFetchAll;
|
||||
copy.queryFetchBatch = queryFetchBatch;
|
||||
copy.lazyFetchBatch = lazyFetchBatch;
|
||||
copy.filterMany = filterMany;
|
||||
if (included != null) {
|
||||
copy.included = new HashSet<String>(included);
|
||||
}
|
||||
if (includedBeanJoin != null) {
|
||||
copy.includedBeanJoin = new HashSet<String>(includedBeanJoin);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public boolean hasSelectClause() {
|
||||
if ("*".equals(trimmedProperties)) {
|
||||
// explicitly selected all properties
|
||||
@@ -291,17 +272,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,48 +310,18 @@ public class OrmQueryProperties implements Serializable {
|
||||
secondaryChildren.add(child);
|
||||
}
|
||||
|
||||
public int autoTunePlanHash() {
|
||||
|
||||
int hc = (path != null ? path.hashCode() : 1);
|
||||
if (properties != null) {
|
||||
hc = hc * 31 + properties.hashCode();
|
||||
} else {
|
||||
hc = hc * 31 + (included != null ? included.hashCode() : 1);
|
||||
}
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query plan hash.
|
||||
* Return the raw properties.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(path);
|
||||
if (properties != null) {
|
||||
builder.add(properties);
|
||||
} else {
|
||||
builder.add(included);
|
||||
}
|
||||
builder.add(filterMany != null);
|
||||
if (filterMany != null) {
|
||||
filterMany.queryPlanHash(request, 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 includes all properties on the path.
|
||||
*/
|
||||
public boolean hasProperties() {
|
||||
return properties != null || included != null;
|
||||
public boolean allProperties() {
|
||||
return included == null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,10 +345,6 @@ public class OrmQueryProperties implements Serializable {
|
||||
includedBeanJoin.add(propertyName);
|
||||
}
|
||||
|
||||
public boolean allProperties() {
|
||||
return included == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This excludes the bean joined properties.
|
||||
* <p>
|
||||
@@ -399,7 +357,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;
|
||||
@@ -413,30 +371,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) {
|
||||
@@ -448,141 +386,120 @@ public class OrmQueryProperties implements Serializable {
|
||||
return included == null || included.contains(propName);
|
||||
}
|
||||
|
||||
public void setQueryFetch(int batch, boolean queryFetchAll) {
|
||||
this.queryFetchBatch = batch;
|
||||
this.queryFetchAll = queryFetchAll;
|
||||
/**
|
||||
* Mark this path as needing to be a query join.
|
||||
*/
|
||||
public void markForQueryJoin() {
|
||||
markForQueryJoin = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path is a 'query join'.
|
||||
*/
|
||||
public boolean isQueryFetch() {
|
||||
return markForQueryJoin || getQueryFetchBatch() > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path is a 'fetch join'.
|
||||
*/
|
||||
public boolean isFetchJoin() {
|
||||
return !isQueryFetch() && !isLazyFetch();
|
||||
}
|
||||
|
||||
public boolean isQueryFetch() {
|
||||
return queryFetchBatch > -1;
|
||||
}
|
||||
|
||||
public int getQueryFetchBatch() {
|
||||
return queryFetchBatch;
|
||||
}
|
||||
|
||||
public boolean isQueryFetchAll() {
|
||||
return queryFetchAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path is a lazy fetch.
|
||||
*/
|
||||
public boolean isLazyFetch() {
|
||||
return lazyFetchBatch > -1;
|
||||
return getLazyFetchBatch() > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size to use for the query join.
|
||||
*/
|
||||
public int getQueryFetchBatch() {
|
||||
return fetchConfig.getQueryBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a query join should eagerly fetch 'all' rather than the 'first'.
|
||||
*/
|
||||
public boolean isQueryFetchAll() {
|
||||
return fetchConfig.isQueryAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size to use for lazy loading.
|
||||
*/
|
||||
public int getLazyFetchBatch() {
|
||||
return lazyFetchBatch;
|
||||
return fetchConfig.getLazyBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path has the +readonly option.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path has the +cache option to hit the cache.
|
||||
*/
|
||||
public boolean isCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent path.
|
||||
*/
|
||||
public String getParentPath() {
|
||||
return parentPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path relative to the root of the graph.
|
||||
*/
|
||||
public String getPath() {
|
||||
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 fetchConfig.equals(p2.fetchConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(fetchConfig.hashCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import com.avaje.ebean.FetchConfig;
|
||||
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 FetchConfig fetchConfig;
|
||||
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.properties = properties;
|
||||
this.included = included;
|
||||
if (lazyFetchBatch > -1 || queryFetchBatch > -1) {
|
||||
this.fetchConfig = new FetchConfig().lazy(lazyFetchBatch).query(queryFetchBatch);
|
||||
} else {
|
||||
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
|
||||
}
|
||||
}
|
||||
|
||||
public Response() {
|
||||
this.readOnly = false;
|
||||
this.cache = false;
|
||||
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiQuerySecondary;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The secondary query paths for 'query joins' and 'lazy loading'.
|
||||
*/
|
||||
public class OrmQuerySecondary implements SpiQuerySecondary {
|
||||
|
||||
private final List<OrmQueryProperties> queryJoins;
|
||||
|
||||
private final List<OrmQueryProperties> lazyJoins;
|
||||
|
||||
/**
|
||||
* Construct with the 'query join' and 'lazy join' path properties.
|
||||
*/
|
||||
public OrmQuerySecondary(List<OrmQueryProperties> queryJoins, List<OrmQueryProperties> lazyJoins) {
|
||||
this.queryJoins = queryJoins;
|
||||
this.lazyJoins = lazyJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of path/properties that are query join loaded.
|
||||
*/
|
||||
@Override
|
||||
public List<OrmQueryProperties> getQueryJoins() {
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of path/properties that are lazy loaded.
|
||||
*/
|
||||
@Override
|
||||
public List<OrmQueryProperties> getLazyJoins() {
|
||||
return lazyJoins;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@ public class SimpleTextParser {
|
||||
this.eof = oql.length();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return oql.isEmpty();
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.avaje.ebean.annotation.EnumValue;
|
||||
import com.avaje.ebean.config.*;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.*;
|
||||
@@ -285,7 +284,15 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> ScalarType<T> getScalarType(Class<T> type) {
|
||||
return (ScalarType<T>) typeMap.get(type);
|
||||
ScalarType<T> found = (ScalarType<T>) typeMap.get(type);
|
||||
if (found == null) {
|
||||
if (type.getName().equals("org.joda.time.LocalTime")) {
|
||||
throw new IllegalStateException(
|
||||
"ScalarType of Joda LocalTime not defined. You need to set ServerConfig.jodaLocalTimeMode to"
|
||||
+ " either 'normal' or 'utc'. UTC is the old mode using UTC timezone but local time zone is now preferred as 'normal' mode.");
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
public ScalarDataReader<?> getScalarDataReader(Class<?> propertyType, int sqlType) {
|
||||
@@ -805,8 +812,18 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
typeMap.put(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(mode));
|
||||
typeMap.put(DateTime.class, new ScalarTypeJodaDateTime(mode));
|
||||
typeMap.put(LocalDate.class, new ScalarTypeJodaLocalDate());
|
||||
typeMap.put(LocalTime.class, new ScalarTypeJodaLocalTime());
|
||||
typeMap.put(DateMidnight.class, new ScalarTypeJodaDateMidnight());
|
||||
|
||||
String jodaLocalTimeMode = config.getJodaLocalTimeMode();
|
||||
if ("normal".equalsIgnoreCase(jodaLocalTimeMode)) {
|
||||
// use the expected/normal local time zone
|
||||
typeMap.put(LocalTime.class, new ScalarTypeJodaLocalTime());
|
||||
logger.debug("registered ScalarTypeJodaLocalTime");
|
||||
} else if ("utc".equalsIgnoreCase(jodaLocalTimeMode)) {
|
||||
// use the old UTC based
|
||||
typeMap.put(LocalTime.class, new ScalarTypeJodaLocalTimeUTC());
|
||||
logger.debug("registered ScalarTypeJodaLocalTimeUTC");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase<LocalTime> {
|
||||
if (value == null) {
|
||||
b.setNull(Types.TIME);
|
||||
} else {
|
||||
Time sqlTime = new Time(value.getMillisOfDay());
|
||||
b.setTime(sqlTime);
|
||||
b.setTime(new Time(value.getHourOfDay(), value.getMinuteOfHour(), value.getSecondOfMinute()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,14 +39,15 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase<LocalTime> {
|
||||
if (sqlTime == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new LocalTime(sqlTime, DateTimeZone.UTC);
|
||||
return new LocalTime(sqlTime, DateTimeZone.getDefault());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
if (value instanceof LocalTime) {
|
||||
return new Time(((LocalTime) value).getMillisOfDay());
|
||||
LocalTime lt = (LocalTime) value;
|
||||
return new Time(lt.getHourOfDay(), lt.getMinuteOfHour(), lt.getSecondOfMinute());
|
||||
}
|
||||
return BasicTypeConverter.toTime(value);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase<LocalTime> {
|
||||
@Override
|
||||
public LocalTime toBeanType(Object value) {
|
||||
if (value instanceof java.util.Date) {
|
||||
return new LocalTime(value, DateTimeZone.UTC);
|
||||
return new LocalTime(value, DateTimeZone.getDefault());
|
||||
}
|
||||
return (LocalTime) value;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase<LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convertFromMillis(long systemTimeMillis) {
|
||||
return new LocalTime(systemTimeMillis);
|
||||
return new LocalTime(systemTimeMillis, DateTimeZone.getDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.LocalTime;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Time;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* ScalarType for Joda LocalTime. This maps to a JDBC Time.
|
||||
*/
|
||||
public class ScalarTypeJodaLocalTimeUTC extends ScalarTypeJodaLocalTime {
|
||||
|
||||
public ScalarTypeJodaLocalTimeUTC() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, LocalTime value) throws SQLException {
|
||||
if (value == null) {
|
||||
b.setNull(Types.TIME);
|
||||
} else {
|
||||
Time sqlTime = new Time(value.getMillisOfDay());
|
||||
b.setTime(sqlTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime read(DataReader dataReader) throws SQLException {
|
||||
|
||||
Time sqlTime = dataReader.getTime();
|
||||
if (sqlTime == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new LocalTime(sqlTime, DateTimeZone.UTC);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
if (value instanceof LocalTime) {
|
||||
return new Time(((LocalTime) value).getMillisOfDay());
|
||||
}
|
||||
return BasicTypeConverter.toTime(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime toBeanType(Object value) {
|
||||
if (value instanceof java.util.Date) {
|
||||
return new LocalTime(value, DateTimeZone.UTC);
|
||||
}
|
||||
return (LocalTime) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime convertFromMillis(long systemTimeMillis) {
|
||||
return new LocalTime(systemTimeMillis);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import org.avaje.agentloader.AgentLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -29,4 +30,16 @@ public class BaseTestCase {
|
||||
SpiEbeanServer spi = (SpiEbeanServer)Ebean.getDefaultServer();
|
||||
return spi.getDatabasePlatform().getName().equals("h2");
|
||||
}
|
||||
|
||||
protected <T> BeanDescriptor<T> getBeanDescriptor(Class<T> cls) {
|
||||
return spiEbeanServer().getBeanDescriptor(cls);
|
||||
}
|
||||
|
||||
protected SpiEbeanServer spiEbeanServer() {
|
||||
return (SpiEbeanServer) Ebean.getDefaultServer();
|
||||
}
|
||||
|
||||
protected EbeanServer server() {
|
||||
return Ebean.getDefaultServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class ColumnMappingTest {
|
||||
|
||||
RawSql.ColumnMapping.Column col(int indexPos, String dbColumn, String dbAlias) {
|
||||
return new RawSql.ColumnMapping.Column(indexPos, dbColumn, dbAlias);
|
||||
}
|
||||
|
||||
RawSql.ColumnMapping mapping(RawSql.ColumnMapping.Column... cols) {
|
||||
return new RawSql.ColumnMapping(Arrays.asList(cols));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_same() {
|
||||
|
||||
RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
|
||||
RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "name", null));
|
||||
|
||||
assertSame(mapping1, mapping2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_diffPropertyName() {
|
||||
|
||||
RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
|
||||
RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "diff", null));
|
||||
|
||||
assertDifferent(mapping1, mapping2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_moreColumns() {
|
||||
|
||||
RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
|
||||
RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null), col(2, "name", null), col(2, "diff", null));
|
||||
|
||||
assertDifferent(mapping1, mapping2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void equals_lessColumns() {
|
||||
|
||||
RawSql.ColumnMapping mapping1 = mapping(col(1, "id", null), col(2, "name", null));
|
||||
RawSql.ColumnMapping mapping2 = mapping(col(1, "id", null));
|
||||
|
||||
assertDifferent(mapping1, mapping2);
|
||||
}
|
||||
|
||||
private void assertSame(Object key, Object key1) {
|
||||
assertThat(key).isEqualTo(key1);
|
||||
assertThat(key.hashCode()).isEqualTo(key1.hashCode());
|
||||
}
|
||||
|
||||
private void assertDifferent(Object key, Object key1) {
|
||||
assertThat(key).isNotEqualTo(key1);
|
||||
assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class ColumnTest {
|
||||
|
||||
RawSql.ColumnMapping.Column col(int indexPos, String dbColumn, String dbAlias) {
|
||||
return new RawSql.ColumnMapping.Column(indexPos, dbColumn, dbAlias);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_same() {
|
||||
assertSame(col(1, "name", null),col(1, "name", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_same_withAlias() {
|
||||
assertSame(col(1, "t0.name", "t0"),col(1, "t0.name", "t0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffIndex() {
|
||||
assertDifferent(col(1, "name", null),col(2, "name", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffProperty() {
|
||||
assertDifferent(col(1, "name", null),col(1, "diffName", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffAlias() {
|
||||
assertDifferent(col(1, "t1.name", "t1"),col(1, "t1.name", "t0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffAliasNullLast() {
|
||||
assertDifferent(col(1, "t1.name", "t1"),col(1, "t1.name", null));
|
||||
}
|
||||
@Test
|
||||
public void equals_when_diffAliasNullFirst() {
|
||||
assertDifferent(col(1, "t1.name", null),col(1, "t1.name", "t1"));
|
||||
}
|
||||
|
||||
|
||||
private void assertSame(Object key, Object key1) {
|
||||
assertThat(key).isEqualTo(key1);
|
||||
assertThat(key.hashCode()).isEqualTo(key1.hashCode());
|
||||
}
|
||||
|
||||
private void assertDifferent(Object key, Object key1) {
|
||||
assertThat(key).isNotEqualTo(key1);
|
||||
assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class FetchConfigTest {
|
||||
|
||||
@Test
|
||||
public void testLazy() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().lazy();
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(0);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazy_withParameter() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().lazy(50);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(50);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuery() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().query();
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(0);
|
||||
assertThat(config.isQueryAll()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuery_withParameter() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().query(50);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryFirst() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().queryFirst(50);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(-1);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAndLazy_withParameters() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().query(50).lazy(10);
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(10);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAndLazy() throws Exception {
|
||||
|
||||
FetchConfig config = new FetchConfig().query(50).lazy();
|
||||
|
||||
assertThat(config.getLazyBatchSize()).isEqualTo(0);
|
||||
assertThat(config.getQueryBatchSize()).isEqualTo(50);
|
||||
assertThat(config.isQueryAll()).isEqualTo(false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEquals_when_noOptions() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig(), new FetchConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_query_50_lazy_40() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().query(50).lazy(40), new FetchConfig().query(50).lazy(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_query_50_lazy() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().query(50).lazy(), new FetchConfig().query(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_query_50() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().query(50), new FetchConfig().query(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_queryFirst_50_lazy_40() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().queryFirst(50).lazy(40), new FetchConfig().queryFirst(50).lazy(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_queryFirst_50_lazy() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().queryFirst(50).lazy(), new FetchConfig().queryFirst(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals_when_queryFirst_50() throws Exception {
|
||||
|
||||
assertSame(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_query_50() throws Exception {
|
||||
|
||||
assertDifferent(new FetchConfig().query(50), new FetchConfig().query(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_query_50_lazy() throws Exception {
|
||||
|
||||
assertDifferent(new FetchConfig().query(50), new FetchConfig().query(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_query_50_lazy_40() throws Exception {
|
||||
|
||||
assertDifferent(new FetchConfig().query(50), new FetchConfig().query(50).lazy(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_queryFirst_50() throws Exception {
|
||||
|
||||
assertDifferent(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_queryFirst_50_lazy() throws Exception {
|
||||
|
||||
assertDifferent(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(50).lazy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals_when_queryFirst_50_lazy_40() throws Exception {
|
||||
|
||||
assertDifferent(new FetchConfig().queryFirst(50), new FetchConfig().queryFirst(50).lazy(40));
|
||||
}
|
||||
|
||||
void assertDifferent(FetchConfig v1, FetchConfig v2) {
|
||||
assertThat(v1).isNotEqualTo(v2);
|
||||
assertThat(v1.hashCode()).isNotEqualTo(v2.hashCode());
|
||||
}
|
||||
|
||||
|
||||
void assertSame(FetchConfig v1, FetchConfig v2) {
|
||||
assertThat(v1).isEqualTo(v2);
|
||||
assertThat(v1.hashCode()).isEqualTo(v2.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class RawSqlKeyTest {
|
||||
|
||||
@Test
|
||||
public void equals_when_sameParsedSql() {
|
||||
|
||||
RawSql.Key key = RawSqlBuilder.parse("select id from customer").create().getKey();
|
||||
RawSql.Key key1 = RawSqlBuilder.parse("select id from customer").create().getKey();
|
||||
|
||||
assertSame(key, key1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffParsedSql() {
|
||||
|
||||
RawSql.Key key = RawSqlBuilder.parse("select id from customer").create().getKey();
|
||||
RawSql.Key key1 = RawSqlBuilder.parse("select name from customer").create().getKey();
|
||||
|
||||
assertDifferent(key, key1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_sameColumnMapping() {
|
||||
|
||||
RawSql.Key key = RawSqlBuilder.parse("select id from customer").columnMapping("id","b").create().getKey();
|
||||
RawSql.Key key1 = RawSqlBuilder.parse("select id from customer").columnMapping("id","b").create().getKey();
|
||||
|
||||
assertSame(key, key1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffColumnMapping() {
|
||||
|
||||
RawSql.Key key = RawSqlBuilder.parse("select a from customer").columnMapping("a","b").create().getKey();
|
||||
RawSql.Key key1 = RawSqlBuilder.parse("select a from customer").columnMapping("a","c").create().getKey();
|
||||
|
||||
assertDifferent(key, key1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_parseToUnpased() {
|
||||
|
||||
RawSql.Key key = RawSqlBuilder.parse("select a from customer").columnMapping("a","b").create().getKey();
|
||||
RawSql.Key key1 = RawSqlBuilder.unparsed("select a from customer").columnMapping("a","c").create().getKey();
|
||||
|
||||
assertDifferent(key, key1);
|
||||
}
|
||||
|
||||
private void assertSame(RawSql.Key key, RawSql.Key key1) {
|
||||
assertThat(key).isEqualTo(key1);
|
||||
assertThat(key.hashCode()).isEqualTo(key1.hashCode());
|
||||
}
|
||||
|
||||
private void assertDifferent(RawSql.Key key, RawSql.Key key1) {
|
||||
assertThat(key).isNotEqualTo(key1);
|
||||
assertThat(key.hashCode()).isNotEqualTo(key1.hashCode());
|
||||
}
|
||||
}
|
||||
+16
@@ -10,6 +10,22 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class PlatformTypeConverterTest {
|
||||
|
||||
@Test
|
||||
public void convert_withSuffix_expect_suffix() {
|
||||
|
||||
PostgresPlatform pg = new PostgresPlatform();
|
||||
DbTypeMap dbTypeMap = pg.getDbTypeMap();
|
||||
|
||||
PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap);
|
||||
|
||||
assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)");
|
||||
assertThat(converter.convert("VARCHAR(10) default 'en' not null")).isEqualTo("varchar(10) default 'en' not null");
|
||||
assertThat(converter.convert("DECIMAL(10,2) DEFAULT '0.00' NOT NULL")).isEqualTo("decimal(10,2) DEFAULT '0.00' NOT NULL");
|
||||
assertThat(converter.convert("CRAZY(12,0) suffix")).isEqualTo("CRAZY(12,0) suffix");
|
||||
assertThat(converter.convert("CRAZY(12) suffix")).isEqualTo("CRAZY(12) suffix");
|
||||
assertThat(converter.convert("CRAZY suffix")).isEqualTo("CRAZY suffix");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvert_given_postgres() throws Exception {
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class HashQueryPlanBuilderTest {
|
||||
|
||||
int combine(int v0, int v1) {
|
||||
return new HashQueryPlanBuilder().add(v0).add(v1).build().hashCode();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_pair_0_1() {
|
||||
assertThat(combine(0, 31)).isNotEqualTo(combine(1,0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_pair_0_10_adjust_0() {
|
||||
assertThat(combine(0, 310)).isNotEqualTo(combine(10,0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_pair_0_10_adjust_10() {
|
||||
assertThat(combine(0, 320)).isNotEqualTo(combine(10,10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_pair_0_10_adjust_40() {
|
||||
assertThat(combine(0, 350)).isNotEqualTo(combine(10,40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_pair_0_10_adjust_90() {
|
||||
assertThat(combine(0, 400)).isNotEqualTo(combine(10,90));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -147,21 +147,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> q, Transaction t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CQuery<T> compileQuery(Query<T> query, Transaction t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CQueryEngine getQueryEngine() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int delete(Query<T> query, Transaction t) {
|
||||
return 0;
|
||||
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.FetchConfig;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DefaultServer_createOrmQueryRequestTest extends BaseTestCase {
|
||||
|
||||
Query<Order> query() {
|
||||
return server().find(Order.class);
|
||||
}
|
||||
|
||||
OrmQueryRequest<Order> queryRequest(Query<Order> query) {
|
||||
return OrmQueryRequestTestHelper.queryRequest(query);
|
||||
}
|
||||
|
||||
OrmQueryDetail detail(Query<Order> query) {
|
||||
return queryRequest(query).getQuery().getDetail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_empty_then_same() {
|
||||
|
||||
assertSame(detail(query()), detail(query()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void select_all_then_same() {
|
||||
|
||||
assertSame(detail(query().select("*")), detail(query().select("*")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_propertiesInOrder_then_same() {
|
||||
|
||||
assertSame(detail(query().select("id,name")), detail(query().select("id,name")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_propertiesInDifferentOrder_then_different() {
|
||||
|
||||
assertDifferent(detail(query().select("id,name")), detail(query().select("name, id")));
|
||||
|
||||
// but same from autotune perspective
|
||||
assertThat(detail(query().select("id,name")).isAutoTuneEqual(detail(query().select("name, id")))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_additional_fetch_then_different() {
|
||||
|
||||
assertDifferent(detail(query().select("id,name")), detail(query().select("id,name").fetch("details")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_same_fetch_then_same() {
|
||||
|
||||
assertSame(detail(query().select("id,name").fetch("details")), detail(query().select("id,name").fetch("details")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_fetch_order_different_then_different() {
|
||||
|
||||
assertDifferent(detail(query().select("id,name").fetch("details").fetch("customer")),
|
||||
detail(query().select("id,name").fetch("customer").fetch("details")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_extra_queryFetchToMany_then_same() {
|
||||
|
||||
assertDifferent(detail(query().select("id,name").fetch("customer")),
|
||||
detail(query().select("id,name").fetch("customer").fetch("details", new FetchConfig().query())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_extra_queryToOne_fetch_then_different() {
|
||||
|
||||
// with the fetch of customer the foreign key must be added to the root query
|
||||
assertDifferent(detail(query().select("id,name")),
|
||||
detail(query().select("id,name").fetch("customer", new FetchConfig().query())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_additional_fetch_V2_then_different() {
|
||||
|
||||
assertDifferent(detail(query().select("id,name")), detail(query().select("id,name").fetch("customer", "id")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void when_fetchConfig_then_differentPlan() throws Exception {
|
||||
|
||||
DefaultOrmQuery<Order> query1 = (DefaultOrmQuery<Order>) Ebean.find(Order.class)
|
||||
.select("status, shipDate")
|
||||
.fetch("details", "orderQty, unitPrice", new FetchConfig().query())
|
||||
.fetch("details.product", "sku, name");
|
||||
|
||||
|
||||
DefaultOrmQuery<Order> query2 = (DefaultOrmQuery<Order>) Ebean.find(Order.class)
|
||||
.select("status, shipDate")
|
||||
.fetch("details", "orderQty, unitPrice")
|
||||
.fetch("details.product", "sku, name");
|
||||
|
||||
assertDifferent(detail(query1), detail(query2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_fetchJoins_expect_detailJoinsPreserveOrder() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details");
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer", "details");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_fetchJoinsAndWhere_expect_fetchJoinsOnlyInFetchPaths() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("details")
|
||||
.where().eq("customer.name", "rob").query();
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("details");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_queryFetch_expect_getFetchPaths_doesNotIncludeQueryJoin() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details", new FetchConfig().query());
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_lazyFetch_expect_getFetchPaths_doesNotIncludeQueryJoin() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details", new FetchConfig().lazy());
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_lazyFetchAndHasChildren_expect_getFetchPaths_doesNotIncludeJoinOrChild() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("customer", "name")
|
||||
.fetch("details", new FetchConfig().lazy())
|
||||
.fetch("details.product");
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_fetchMany_expect_getFetchPaths_containsAllInOrder() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("details")
|
||||
.fetch("details.product")
|
||||
.fetch("customer", "name");
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("details", "details.product", "customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOrder_when_queryJoin_expect_getFetchPaths_excludesQueryJoinAndChildren() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.select("status, orderDate")
|
||||
.fetch("details", new FetchConfig().query())
|
||||
.fetch("details.product")
|
||||
.fetch("customer", "name");
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_removeJoinToMany_when_multipleManyPaths() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.fetch("details")
|
||||
.fetch("details.product")
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts"); // second many path
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("details", "details.product", "customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_removeAllJoinToMany_when_firstRow() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.setFirstRow(1)
|
||||
.fetch("details") // many path
|
||||
.fetch("details.product")
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts"); // many path
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_removeAllJoinToMany_when_maxRows() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.setMaxRows(1)
|
||||
.fetch("details") // many path
|
||||
.fetch("details.product")
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts"); // many path
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_removeJoinToMany_when_filterMany() {
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.fetch("details")
|
||||
.fetch("details.product")
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts")
|
||||
.filterMany("details").eq("orderQuantity", 10)
|
||||
.query();
|
||||
|
||||
OrmQueryRequest<Order> queryRequest = queryRequest(query);
|
||||
OrmQueryDetail detail = queryRequest.getQuery().getDetail();
|
||||
|
||||
assertThat(detail.getFetchPaths()).containsExactly("customer", "customer.contacts");
|
||||
}
|
||||
|
||||
private void assertSame(OrmQueryDetail detail1, OrmQueryDetail detail2) {
|
||||
assertThat(detail1.isSameByPlan(detail2)).isTrue();
|
||||
assertThat(detail1.queryPlanHash()).isEqualTo(detail2.queryPlanHash());
|
||||
}
|
||||
|
||||
private void assertDifferent(OrmQueryDetail detail1, OrmQueryDetail detail2) {
|
||||
assertThat(detail1.isSameByPlan(detail2)).isFalse();
|
||||
assertThat(detail1.queryPlanHash()).isNotEqualTo(detail2.queryPlanHash());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
|
||||
public class OrmQueryRequestTestHelper {
|
||||
|
||||
static DefaultServer defaultServer = (DefaultServer) Ebean.getDefaultServer();
|
||||
|
||||
/**
|
||||
* Create and return a OrmQueryRequest for the given query.
|
||||
*/
|
||||
public static <T> OrmQueryRequest<T> queryRequest(Query<T> query) {
|
||||
return (OrmQueryRequest<T>) defaultServer.createQueryRequest(SpiQuery.Type.LIST, query, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TableJoinColumnTest {
|
||||
|
||||
TableJoinColumn col(String localDbColumn, String foreignDbColumn, boolean insertable, boolean updateable) {
|
||||
DeployTableJoinColumn column = new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable);
|
||||
return new TableJoinColumn(column);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_same() {
|
||||
assertSame(col("a", "b", true, true), col("a", "b", true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffFirstCol() {
|
||||
assertDifferent(col("a", "b", true, true), col("c", "b", true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffSecondCol() {
|
||||
assertDifferent(col("a", "b", true, true), col("a", "c", true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffInsertable() {
|
||||
assertDifferent(col("a", "b", true, true), col("a", "b", false, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffUpdateable() {
|
||||
assertDifferent(col("a", "b", true, true), col("a", "b", true, false));
|
||||
}
|
||||
|
||||
private void assertDifferent(TableJoinColumn col, TableJoinColumn col2) {
|
||||
assertThat(col).isNotEqualTo(col2);
|
||||
assertThat(col.hashCode()).isNotEqualTo(col2.hashCode());
|
||||
}
|
||||
|
||||
private void assertSame(TableJoinColumn col, TableJoinColumn col2) {
|
||||
assertThat(col).isEqualTo(col2);
|
||||
assertThat(col.hashCode()).isEqualTo(col2.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TableJoinTest {
|
||||
|
||||
DeployTableJoinColumn col(String localDbColumn, String foreignDbColumn, boolean insertable, boolean updateable) {
|
||||
return new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable);
|
||||
}
|
||||
|
||||
TableJoin table(String tableName, String col1, String col2) {
|
||||
DeployTableJoin deploy = new DeployTableJoin();
|
||||
deploy.setTable(tableName);
|
||||
deploy.addJoinColumn(col(col1, col2, true, true));
|
||||
return new TableJoin(deploy);
|
||||
}
|
||||
|
||||
TableJoin table(String tableName, String col1, String col2, String col3, String col4) {
|
||||
DeployTableJoin deploy = new DeployTableJoin();
|
||||
deploy.setTable(tableName);
|
||||
deploy.addJoinColumn(col(col1, col2, true, true));
|
||||
deploy.addJoinColumn(col(col3, col4, true, true));
|
||||
return new TableJoin(deploy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_same() {
|
||||
assertSame(table("myTable", "a", "b"), table("myTable", "a", "b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffTable() {
|
||||
assertDifferent(table("myTable", "a", "b"), table("diffTable", "a", "b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffColumn() {
|
||||
assertDifferent(table("myTable", "a", "b"), table("myTable", "c", "b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_moreColumns() {
|
||||
assertDifferent(table("myTable", "a", "b"), table("myTable", "a", "b", "c", "d"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_lessColumns() {
|
||||
assertDifferent(table("myTable", "a", "b", "c", "d"), table("myTable", "a", "b"));
|
||||
}
|
||||
|
||||
void assertSame(TableJoin join1, TableJoin join2) {
|
||||
assertThat(join1).isEqualTo(join1);
|
||||
assertThat(join1.hashCode()).isEqualTo(join1.hashCode());
|
||||
}
|
||||
|
||||
|
||||
void assertDifferent(TableJoin join1, TableJoin join2) {
|
||||
assertThat(join1).isNotEqualTo(join2);
|
||||
assertThat(join1.hashCode()).isNotEqualTo(join2.hashCode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class AllEqualsExpressionTest {
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_same() {
|
||||
|
||||
assertThat(exp("a", 10).isSameByPlan(exp("a", 10))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_sameMultiple() {
|
||||
|
||||
assertThat(exp("a", 10, "b", "23").isSameByBind(exp("a", 10, "b", "23"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffBindValue() {
|
||||
|
||||
assertThat(exp("a", 10).isSameByPlan(exp("a", 20))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_multiple() {
|
||||
|
||||
assertThat(exp("a", 10, "b", 20, "c", 30).isSameByPlan(exp("a", 10, "b", 20, "c", 30))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_less() {
|
||||
|
||||
assertThat(exp("a", 10, "b", 20, "c", 30).isSameByPlan(exp("a", 10, "b", 20))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_more() {
|
||||
|
||||
assertThat(exp("a", 10, "b", 20).isSameByPlan(exp("a", 10, "b", 20, "c", 30))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffProperty_diff() {
|
||||
|
||||
assertThat(exp("a", 10).isSameByPlan(exp("b", 10))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffType_diff() {
|
||||
|
||||
assertThat(exp("a", 10).isSameByPlan(new NoopExpression())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffBindByNull_last() {
|
||||
|
||||
assertThat(exp("a", 10).isSameByPlan(exp("a", null))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffBindByNull_first() {
|
||||
|
||||
assertThat(exp("a", null).isSameByPlan(exp("a", 10))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_differentExpressionType() {
|
||||
|
||||
assertThat(exp("a", null).isSameByPlan(new NoopExpression())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_diffBindByNull_last() {
|
||||
|
||||
assertThat(exp("a", 10).isSameByBind(exp("a", null))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_diffBindByNull_first() {
|
||||
|
||||
assertThat(exp("a", null).isSameByBind(exp("a", 10))).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private AllEqualsExpression exp(Map<String, Object> propMap) {
|
||||
return new AllEqualsExpression(propMap);
|
||||
}
|
||||
|
||||
AllEqualsExpression exp(String key0, Object val1) {
|
||||
LinkedHashMap<String,Object> map = new LinkedHashMap<String,Object>();
|
||||
map.put(key0, val1);
|
||||
return exp(map);
|
||||
}
|
||||
|
||||
|
||||
AllEqualsExpression exp(String key0, Object val1, String key2, Object val2) {
|
||||
LinkedHashMap<String,Object> map = new LinkedHashMap<String,Object>();
|
||||
map.put(key0, val1);
|
||||
map.put(key2, val2);
|
||||
return exp(map);
|
||||
}
|
||||
|
||||
AllEqualsExpression exp(String key0, Object val1, String key2, Object val2, String key3, Object val3) {
|
||||
LinkedHashMap<String,Object> map = new LinkedHashMap<String,Object>();
|
||||
map.put(key0, val1);
|
||||
map.put(key2, val2);
|
||||
map.put(key3, val3);
|
||||
return exp(map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
|
||||
public abstract class BaseExpressionTest extends BaseTestCase {
|
||||
|
||||
protected DefaultExpressionRequest newExpressionRequest() {
|
||||
BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
|
||||
return new DefaultExpressionRequest(desc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class BetweenExpressionTest extends BaseExpressionTest {
|
||||
|
||||
@Test
|
||||
public void addSql() throws Exception {
|
||||
|
||||
DefaultExpressionRequest expReq = newExpressionRequest();
|
||||
|
||||
BetweenExpression exp = new BetweenExpression("startDate", 1, 2);
|
||||
exp.addSql(expReq);
|
||||
|
||||
assertThat(expReq.getSql()).isEqualTo("startDate between ? and ? ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyForPlanKey_isSameInstance() throws Exception {
|
||||
|
||||
BetweenExpression exp = new BetweenExpression("startDate", 1, 2);
|
||||
SpiExpression other = exp.copyForPlanKey();
|
||||
|
||||
assertThat(exp).isSameAs(other);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_properties_match() throws Exception {
|
||||
|
||||
BetweenExpression exp0 = new BetweenExpression("startDate", 1, 2);
|
||||
BetweenExpression exp1 = new BetweenExpression("startDate", 3, 4);
|
||||
|
||||
assertThat(exp0.isSameByPlan(exp1)).isTrue();
|
||||
assertThat(exp1.isSameByPlan(exp0)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_properties_do_not_match() throws Exception {
|
||||
|
||||
BetweenExpression exp0 = new BetweenExpression("startDate", 1, 2);
|
||||
BetweenExpression exp1 = new BetweenExpression("endDate", 1, 2);
|
||||
|
||||
assertThat(exp0.isSameByPlan(exp1)).isFalse();
|
||||
assertThat(exp1.isSameByPlan(exp0)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_values_do_not_match() throws Exception {
|
||||
|
||||
BetweenExpression exp0 = new BetweenExpression("startDate", 1, 2);
|
||||
BetweenExpression exp1 = new BetweenExpression("startDate", 1, 3);
|
||||
|
||||
assertThat(exp0.isSameByBind(exp1)).isFalse();
|
||||
assertThat(exp1.isSameByBind(exp0)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_values_match() throws Exception {
|
||||
|
||||
BetweenExpression exp0 = new BetweenExpression("startDate", 1, 2);
|
||||
BetweenExpression exp1 = new BetweenExpression("startDate", 1, 2);
|
||||
|
||||
assertThat(exp0.isSameByBind(exp1)).isTrue();
|
||||
assertThat(exp1.isSameByBind(exp0)).isTrue();
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class BetweenPropertyExpressionTest {
|
||||
|
||||
@NotNull
|
||||
private BetweenPropertyExpression exp(String lowProperty, String highProperty, Object value) {
|
||||
return new BetweenPropertyExpression(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_same() {
|
||||
assertThat(exp("a", "b", 10).isSameByPlan(exp("a", "b", 10))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffValue() {
|
||||
assertThat(exp("a", "b", 10).isSameByPlan(exp("a", "b", 20))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffProperty() {
|
||||
assertThat(exp("a", "b", 10).isSameByPlan(exp("a", "c", 10))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffExpressionType() {
|
||||
assertThat(exp("a", "b", 10).isSameByPlan(new NoopExpression())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_same() {
|
||||
assertThat(exp("a", "b", 10).isSameByBind(exp("a", "b", 10))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_diff() {
|
||||
assertThat(exp("a", "b", 10).isSameByBind(exp("a", "b", 20))).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class CaseInsensitiveEqualExpressionTest {
|
||||
|
||||
CaseInsensitiveEqualExpression exp(String propName, String value) {
|
||||
return new CaseInsensitiveEqualExpression(propName, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_same() {
|
||||
|
||||
assertThat(exp("a", "10").isSameByPlan(exp("a", "10"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffBind_same() {
|
||||
|
||||
assertThat(exp("a", "10").isSameByPlan(exp("a", "20"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffProperty_diff() {
|
||||
|
||||
assertThat(exp("a", "10").isSameByPlan(exp("b", "10"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffType_diff() {
|
||||
|
||||
assertThat(exp("a", "10").isSameByPlan(new NoopExpression())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_sameBindValues() {
|
||||
|
||||
assertThat(exp("a", "10").isSameByBind(exp("b", "10"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByBind_when_diffBindValues() {
|
||||
|
||||
assertThat(exp("a", "10").isSameByBind(exp("b", "20"))).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
+66
-16
@@ -1,13 +1,11 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.LikeType;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -22,40 +20,68 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class DefaultExampleExpressionTest extends BaseTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer)Ebean.getDefaultServer();
|
||||
BeanDescriptor<Customer> desc = server.getBeanDescriptor(Customer.class);
|
||||
|
||||
SpiQuery<Customer> query = (SpiQuery<Customer>)server.find(Customer.class);
|
||||
|
||||
Customer customer() {
|
||||
Address address = new Address();
|
||||
address.setCity("billingAddress.city");
|
||||
|
||||
Customer customer = new Customer();
|
||||
customer.setName("name");
|
||||
customer.setBillingAddress(address);
|
||||
return customer;
|
||||
}
|
||||
|
||||
DefaultExampleExpression exp() {
|
||||
Customer customer = customer();
|
||||
return new DefaultExampleExpression((EntityBean)customer, false, LikeType.EQUAL_TO);
|
||||
}
|
||||
|
||||
DefaultExampleExpression expr = new DefaultExampleExpression((EntityBean)customer, false, LikeType.EQUAL_TO);
|
||||
DefaultExampleExpression expExtra() {
|
||||
Customer customer = customer();
|
||||
customer.setSmallnote("smallNote");
|
||||
return new DefaultExampleExpression((EntityBean)customer, false, LikeType.EQUAL_TO);
|
||||
}
|
||||
|
||||
DefaultExampleExpression expDiffName() {
|
||||
Customer customer = customer();
|
||||
customer.setName("otherName");
|
||||
return new DefaultExampleExpression((EntityBean)customer, false, LikeType.EQUAL_TO);
|
||||
}
|
||||
|
||||
BeanDescriptor<Customer> customerBeanDescriptor() {
|
||||
return getBeanDescriptor(Customer.class);
|
||||
}
|
||||
|
||||
DefaultExampleExpression prepare(DefaultExampleExpression expr) {
|
||||
|
||||
SpiQuery<Customer> query = (SpiQuery<Customer>)spiEbeanServer().find(Customer.class);
|
||||
BeanQueryRequest<?> request = create(query, customerBeanDescriptor());
|
||||
expr.prepareExpression(request);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
DefaultExampleExpression expr = exp();
|
||||
|
||||
prepare(expr);
|
||||
|
||||
BeanQueryRequest<?> request = create(query, desc);
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
expr.queryPlanHash(request, builder);
|
||||
expr.queryPlanHash(builder);
|
||||
|
||||
TDSpiExpressionRequest req = new TDSpiExpressionRequest(desc);
|
||||
TDSpiExpressionRequest req = new TDSpiExpressionRequest(customerBeanDescriptor());
|
||||
expr.addBindValues(req);
|
||||
|
||||
assertThat(req.bindValues).contains("name", "billingAddress.city");
|
||||
|
||||
address.setCity("Auckland");
|
||||
Customer customer = customer();
|
||||
customer.setName("Rob");
|
||||
customer.getBillingAddress().setCity("Auckland");
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query1 = server.find(Customer.class)
|
||||
Query<Customer> query1 = server().find(Customer.class)
|
||||
.where().exampleLike(customer)
|
||||
.query();
|
||||
|
||||
@@ -70,4 +96,28 @@ public class DefaultExampleExpressionTest extends BaseTestCase {
|
||||
return new OrmQueryRequest<T>(null, null, query, desc, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_whenSame() {
|
||||
|
||||
assertThat(prepare(exp()).isSameByPlan(prepare(exp()))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_diffBindValue_stillSame() {
|
||||
|
||||
assertThat(prepare(exp()).isSameByPlan(prepare(expDiffName()))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_extraExpression_then_different() {
|
||||
|
||||
assertThat(prepare(exp()).isSameByPlan(prepare(expExtra()))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSameByPlan_when_lessExpression_then_different() {
|
||||
|
||||
assertThat(prepare(expExtra()).isSameByPlan(prepare(exp()))).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user