diff --git a/src/main/java/com/avaje/ebean/OrderBy.java b/src/main/java/com/avaje/ebean/OrderBy.java
index 734960217..83c2b3404 100644
--- a/src/main/java/com/avaje/ebean/OrderBy.java
+++ b/src/main/java/com/avaje/ebean/OrderBy.java
@@ -15,8 +15,6 @@ import java.util.List;
* Typically you will not construct an OrderBy yourself but use one that exists
* on the Query object.
*
- *
- * @author rbygrave
*/
public final class OrderBy implements Serializable {
@@ -24,7 +22,7 @@ public final class OrderBy implements Serializable {
private transient Query query;
- private List list;
+ private final List list;
/**
* Create an empty OrderBy with no associated query.
@@ -168,31 +166,23 @@ public final class OrderBy implements Serializable {
@Override
public boolean equals(Object obj) {
- if (obj instanceof OrderBy>) {
- if (obj == this) {
- return true;
- }
- OrderBy> other = (OrderBy>) obj;
- return hashCode() == other.hashCode();
+ if (obj == this) {
+ return true;
}
- return false;
- }
-
- @Override
- public int hashCode() {
- return hash();
+ if (!(obj instanceof OrderBy>)) {
+ return false;
+ }
+
+ OrderBy> e = (OrderBy>) obj;
+ return e.list.equals(list);
}
/**
* Return a hash value for this OrderBy. This can be to determine logical
* equality for OrderBy clauses.
*/
- public int hash() {
- int hc = OrderBy.class.getName().hashCode();
- for (int i = 0; i < list.size(); i++) {
- hc = hc * 31 + list.get(i).hash();
- }
- return hc;
+ public int hashCode() {
+ return list.hashCode();
}
/**
@@ -218,11 +208,24 @@ public final class OrderBy implements Serializable {
return new Property(property.substring(path.length() + 1), ascending);
}
- protected int hash() {
+ public int hashCode() {
int hc = property.hashCode();
hc = hc * 31 + (ascending ? 0 : 1);
return hc;
}
+
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Property)) {
+ return false;
+ }
+
+ Property e = (Property) obj;
+ return e.ascending == ascending
+ && e.property.equals(property);
+ }
public String toString() {
return toStringFormat();
diff --git a/src/main/java/com/avaje/ebean/meta/MetaBeanQueryPlanStatistic.java b/src/main/java/com/avaje/ebean/meta/MetaBeanQueryPlanStatistic.java
index 6980c59c8..1b4ffe9a1 100644
--- a/src/main/java/com/avaje/ebean/meta/MetaBeanQueryPlanStatistic.java
+++ b/src/main/java/com/avaje/ebean/meta/MetaBeanQueryPlanStatistic.java
@@ -1,7 +1,5 @@
package com.avaje.ebean.meta;
-
-
/**
* Query execution statistics Meta data.
*/
@@ -18,9 +16,9 @@ public interface MetaBeanQueryPlanStatistic {
public boolean isAutofetchTuned();
/**
- * Return the query plan hash.
+ * Return a string representation of the query plan hash.
*/
- public int getQueryPlanHash();
+ public String getQueryPlanHash();
/**
* Return the sql executed.
diff --git a/src/main/java/com/avaje/ebeaninternal/api/BindParams.java b/src/main/java/com/avaje/ebeaninternal/api/BindParams.java
index f5342779b..23f43cd63 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/BindParams.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/BindParams.java
@@ -50,7 +50,7 @@ public class BindParams implements Serializable {
* taken into account when calculating the query hash.
*
*/
- public int getQueryPlanHash() {
+ public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
int hc = 31;
for (Param param : positionedParameters) {
hc = hc * 31 + param.queryBindCount();
@@ -61,7 +61,8 @@ public class BindParams implements Serializable {
hc = hc * 31 + entry.getValue().queryBindCount();
}
- return hc;
+ int bindCount = positionedParameters.size() + namedParameters.size();
+ builder.add(hc).bind(bindCount);
}
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/api/HashQuery.java b/src/main/java/com/avaje/ebeaninternal/api/HashQuery.java
new file mode 100644
index 000000000..0a560a57b
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/api/HashQuery.java
@@ -0,0 +1,51 @@
+package com.avaje.ebeaninternal.api;
+
+/**
+ * A hash key for a query including both the query plan and bind values.
+ */
+public class HashQuery {
+
+ private final HashQueryPlan planHash;
+
+ private final int bindHash;
+
+ /**
+ * Create the HashQuery.
+ */
+ public HashQuery(HashQueryPlan 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;
+ return hc;
+ }
+
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof HashQuery)) {
+ return false;
+ }
+
+ HashQuery e = (HashQuery) obj;
+ return e.bindHash == bindHash && e.planHash.equals(planHash);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/HashQueryPlan.java b/src/main/java/com/avaje/ebeaninternal/api/HashQueryPlan.java
new file mode 100644
index 000000000..96055b245
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/api/HashQueryPlan.java
@@ -0,0 +1,46 @@
+package com.avaje.ebeaninternal.api;
+
+import java.util.Objects;
+
+/**
+ * A hash for a query plan.
+ */
+public class HashQueryPlan {
+
+ private final String rawSql;
+
+ private final int planHash;
+
+ private final int bindCount;
+
+ public HashQueryPlan(String rawSql, int planHash, int bindCount) {
+ this.rawSql = rawSql;
+ this.planHash = planHash;
+ this.bindCount = bindCount;
+ }
+
+ public String toString() {
+ return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
+ }
+
+ public int hashCode() {
+ int hc = planHash;
+ hc = hc * 31 + bindCount;
+ hc = hc * 31 + Objects.hashCode(rawSql);
+ return hc;
+ }
+
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof HashQueryPlan)) {
+ return false;
+ }
+
+ HashQueryPlan e = (HashQueryPlan) obj;
+ return e.planHash == planHash
+ && e.bindCount == bindCount
+ && Objects.equals(e.rawSql, rawSql);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/HashQueryPlanBuilder.java b/src/main/java/com/avaje/ebeaninternal/api/HashQueryPlanBuilder.java
new file mode 100644
index 000000000..1d9a70f4d
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/api/HashQueryPlanBuilder.java
@@ -0,0 +1,80 @@
+package com.avaje.ebeaninternal.api;
+
+import java.util.Objects;
+
+/**
+ * Used to build HashQueryPlan instances.
+ */
+public class HashQueryPlanBuilder {
+
+ private int planHash;
+
+ private int bindCount;
+
+ private String rawSql;
+
+ public HashQueryPlanBuilder() {
+ this.planHash = 31;
+ }
+
+ public String toString() {
+ return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
+ }
+
+ /**
+ * Add a class to the hash calculation.
+ */
+ public HashQueryPlanBuilder add(Class> cls) {
+ planHash = planHash * 31 + cls.getName().hashCode();
+ return this;
+ }
+
+ /**
+ * Add an object to the hash calculation.
+ */
+ public HashQueryPlanBuilder add(Object object) {
+ planHash = planHash * 31 + Objects.hashCode(object);
+ return this;
+ }
+
+ /**
+ * Add an integer to the hash calculation.
+ */
+ public HashQueryPlanBuilder add(int hashValue) {
+ planHash = planHash * 31 + (hashValue);
+ return this;
+ }
+
+ /**
+ * Add a boolean to the hash calculation.
+ */
+ public HashQueryPlanBuilder add(boolean booleanValue) {
+ planHash = planHash * 31 + (booleanValue ? 31 : 0);
+ return this;
+ }
+
+ /**
+ * Add a number to the bind count for the hash.
+ */
+ public HashQueryPlanBuilder bind(int extraBindCount) {
+ bindCount += extraBindCount;
+ return this;
+ }
+
+ /**
+ * Add raw sql to the hash.
+ */
+ public HashQueryPlanBuilder addRawSql(String rawSql) {
+ this.rawSql = rawSql;
+ return this;
+ }
+
+ /**
+ * Build and return the calculated HashQueryPlan.
+ */
+ public HashQueryPlan build() {
+ return new HashQueryPlan(rawSql, planHash, bindCount);
+ }
+
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java
index d3af546ce..09c3574f4 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java
@@ -26,7 +26,7 @@ public interface SpiExpression extends Expression {
* from an AutoFetch perspective and get different tuning.
*
*/
- public int queryAutoFetchHash();
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder);
/**
* Calculate a hash value for the expression.
@@ -37,7 +37,7 @@ public interface SpiExpression extends Expression {
* case the query execution plan can be reused.
*
*/
- public int queryPlanHash(BeanQueryRequest> request);
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder);
/**
* Return the hash value for the values that will be bound.
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
index 52190c5e0..d5467593d 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
@@ -28,14 +28,13 @@ public interface SpiExpressionList extends ExpressionList {
*/
public void setExpressionFactory(ExpressionFactory expr);
- /**
- * Process "Many" properties populating ManyWhereJoins.
- *
- * Predicates on Many properties require an extra independent
- * join clause.
- *
- */
- public void containsMany(BeanDescriptor> desc, ManyWhereJoins manyWhereJoins);
+ /**
+ * Process "Many" properties populating ManyWhereJoins.
+ *
+ * Predicates on Many properties require an extra independent join clause.
+ *
+ */
+ public void containsMany(BeanDescriptor> desc, ManyWhereJoins manyWhereJoins);
/**
* Return true if this list is empty.
@@ -62,10 +61,10 @@ public interface SpiExpressionList extends ExpressionList {
*/
public ArrayList buildBindValues(SpiExpressionRequest request);
- /**
- * Calculate a hash based on the expressions but excluding the actual bind
- * values.
- */
- public int queryPlanHash(BeanQueryRequest> request);
+ /**
+ * Calculate a hash based on the expressions but excluding the actual bind
+ * values.
+ */
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
index 9c9c3593d..7a59ce06e 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
@@ -341,7 +341,7 @@ public interface SpiQuery extends Query {
* tuning/modifying the query.
*
*/
- public int queryAutofetchHash();
+ public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder);
/**
* Identifies queries that are the same bar the bind variables.
@@ -354,7 +354,7 @@ public interface SpiQuery extends Query {
* Excludes the actual bind values (as they don't effect the query plan).
*
*/
- public int queryPlanHash(BeanQueryRequest> request);
+ public HashQueryPlan queryPlanHash(BeanQueryRequest> request);
/**
* Calculate a hash based on the bind values used in the query.
@@ -368,7 +368,7 @@ public interface SpiQuery extends Query {
/**
* Identifies queries that are exactly the same including bind variables.
*/
- public int queryHash();
+ public HashQuery queryHash();
/**
* Return true if this is a query based on a SqlSelect rather than
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
index 754876e2f..690f50bca 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
@@ -14,6 +14,8 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.BeanIdList;
+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;
@@ -49,9 +51,9 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
private PersistenceContext persistenceContext;
- private Integer cacheKey;
+ private HashQuery cacheKey;
- private int queryPlanHash;
+ private HashQueryPlan queryPlanHash;
/**
* Flag set if background fetching taking place. In this case the transaction
@@ -332,7 +334,7 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
* with just the bind variables changing.
*
*/
- public int getQueryPlanHash() {
+ public HashQueryPlan getQueryPlanHash() {
return queryPlanHash;
}
@@ -356,14 +358,7 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
return null;
}
- if (query.getType() == null) {
- // the query plan and bind values must be the same
- cacheKey = Integer.valueOf(query.queryHash());
-
- } else {
- // additionally the return type (List/Set/Map) must be the same
- cacheKey = Integer.valueOf(31 * query.queryHash() + query.getType().hashCode());
- }
+ cacheKey = query.queryHash();
// TODO: Sort out returning BeanCollection from L2 cache
return null;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
index c3c873b1e..4b6024c92 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -40,6 +40,7 @@ import com.avaje.ebean.meta.MetaBeanInfo;
import com.avaje.ebean.meta.MetaBeanQueryPlanStatistic;
import com.avaje.ebean.text.TextException;
import com.avaje.ebean.text.json.JsonWriteBeanVisitor;
+import com.avaje.ebeaninternal.api.HashQueryPlan;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
@@ -87,7 +88,7 @@ public class BeanDescriptor implements MetaBeanInfo {
private final ConcurrentHashMap updatePlanCache = new ConcurrentHashMap();
- private final ConcurrentHashMap queryPlanCache = new ConcurrentHashMap();
+ private final ConcurrentHashMap queryPlanCache = new ConcurrentHashMap();
private final ConcurrentHashMap elGetCache = new ConcurrentHashMap();
@@ -1202,11 +1203,11 @@ public class BeanDescriptor implements MetaBeanInfo {
return queryPlanCache.values().iterator();
}
- public CQueryPlan getQueryPlan(Integer key) {
+ public CQueryPlan getQueryPlan(HashQueryPlan key) {
return queryPlanCache.get(key);
}
- public void putQueryPlan(Integer key, CQueryPlan plan) {
+ public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) {
queryPlanCache.put(key, plan);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/AllEqualsExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/AllEqualsExpression.java
index c82a66abf..17f12deb3 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/AllEqualsExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/AllEqualsExpression.java
@@ -3,9 +3,11 @@ package com.avaje.ebeaninternal.server.expression;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
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;
@@ -105,29 +107,29 @@ class AllEqualsExpression implements SpiExpression {
* The null check is required due to the "is null" sql being generated.
*
*/
- public int queryAutoFetchHash() {
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
- int hc = AllEqualsExpression.class.getName().hashCode();
- Set> entries = propMap.entrySet();
- Iterator> it = entries.iterator();
+ builder.add(AllEqualsExpression.class);
- while (it.hasNext()) {
- Map.Entry entry = it.next();
+ for (Entry entry : propMap.entrySet()) {
Object value = entry.getValue();
String propName = entry.getKey();
+ builder.add(propName).add(value == null ? 0 : 1);
+ builder.bind(value == null ? 0 : 1);
+ }
+ }
- hc = hc * 31 + propName.hashCode();
- hc = hc * 31 + (value == null ? 0 : 1);
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
+ }
+
+ public int queryBindHash() {
+
+ int hc = 31;
+ for (Object value : propMap.values()) {
+ hc = hc * 31 + Objects.hashCode(value);
}
return hc;
}
-
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
- }
-
- public int queryBindHash() {
- return queryAutoFetchHash();
- }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenExpression.java
index e25b9d025..a5e36c7fc 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenExpression.java
@@ -1,6 +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.SpiExpressionRequest;
@@ -30,14 +31,13 @@ class BetweenExpression extends AbstractExpression {
request.append(getPropertyName()).append(BETWEEN).append(" ? and ? ");
}
- public int queryAutoFetchHash() {
- int hc = BetweenExpression.class.getName().hashCode();
- hc = hc * 31 + propName.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(BetweenExpression.class).add(propName);
+ builder.bind(2);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java
index 7a2f14b66..917986aed 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java
@@ -1,6 +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.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
@@ -65,15 +66,13 @@ class BetweenPropertyExpression implements SpiExpression {
request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty));
}
- public int queryAutoFetchHash() {
- int hc = BetweenPropertyExpression.class.getName().hashCode();
- hc = hc * 31 + lowProperty.hashCode();
- hc = hc * 31 + highProperty.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(BetweenPropertyExpression.class).add(lowProperty).add(highProperty);
+ builder.bind(1);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java
index 517185f8c..553eb203a 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java
@@ -1,10 +1,11 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.event.BeanQueryRequest;
+import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
-class CaseInsensitiveEqualExpression extends AbstractExpression implements LuceneAwareExpression {
+class CaseInsensitiveEqualExpression extends AbstractExpression {
private static final long serialVersionUID = -6406036750998971064L;
@@ -40,14 +41,13 @@ class CaseInsensitiveEqualExpression extends AbstractExpression implements Lucen
request.append("lower(").append(pname).append(") =? ");
}
- public int queryAutoFetchHash() {
- int hc = CaseInsensitiveEqualExpression.class.getName().hashCode();
- hc = hc * 31 + propName.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(CaseInsensitiveEqualExpression.class).add(propName);
+ builder.bind(1);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExampleExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExampleExpression.java
index 04edff69c..710eec230 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExampleExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExampleExpression.java
@@ -6,6 +6,7 @@ import java.util.Iterator;
import com.avaje.ebean.ExampleExpression;
import com.avaje.ebean.LikeType;
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;
@@ -163,28 +164,26 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
/**
* Return a hash for autoFetch query identification.
*/
- public int queryAutoFetchHash() {
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
// we have not yet built the list of expressions
// so just based on the class name
- return DefaultExampleExpression.class.getName().hashCode();
+ builder.add(DefaultExampleExpression.class);
}
/**
* Return a hash for query plan identification.
*/
- public int queryPlanHash(BeanQueryRequest> request) {
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
// this is always called once, and always called before
// addSql() and addBindValues() methods
list = buildExpressions(request);
- int hc = DefaultExampleExpression.class.getName().hashCode();
+ builder.add(DefaultExampleExpression.class);
for (int i = 0; i < list.size(); i++) {
- hc = hc * 31 + list.get(i).queryPlanHash(request);
+ list.get(i).queryPlanHash(request, builder);
}
-
- return hc;
}
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/IdExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/IdExpression.java
index 43c203ff9..a30579894 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/IdExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/IdExpression.java
@@ -1,6 +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.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
@@ -49,14 +50,13 @@ class IdExpression implements SpiExpression {
/**
* No properties so this is just a unique static number.
*/
- public int queryAutoFetchHash() {
- // this number is unique for a given bean type
- // which is all that is required
- return IdExpression.class.getName().hashCode();
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(IdExpression.class);
+ builder.bind(1);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/IdInExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/IdInExpression.java
index 7026db7f6..9d6d327b3 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/IdInExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/IdInExpression.java
@@ -3,6 +3,7 @@ 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;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
@@ -67,16 +68,13 @@ public class IdInExpression implements SpiExpression {
/**
* Incorporates the number of Id values to bind.
*/
- public int queryAutoFetchHash() {
- // this number is unique for a given bean type
- // which is all that is required
- int hc = IdInExpression.class.getName().hashCode();
- hc = hc * 31 + idList.size();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(IdInExpression.class).add(idList.size());
+ builder.bind(idList.size());
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/InExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/InExpression.java
index dc43a320d..06a6c5e4e 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/InExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/InExpression.java
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
import java.util.Collection;
import com.avaje.ebean.event.BeanQueryRequest;
+import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
@@ -79,19 +80,18 @@ class InExpression extends AbstractExpression {
/**
* Based on the number of values in the in clause.
*/
- public int queryAutoFetchHash() {
- int hc = InExpression.class.getName().hashCode() + 31 * values.length;
- hc = hc * 31 + propName.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(InExpression.class).add(propName).add(values.length);
+ builder.bind(values.length);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
- int hc = 0;
- for (int i = 1; i < values.length; i++) {
+ int hc = 31;
+ for (int i = 0; i < values.length; i++) {
hc = 31 * hc + values[i].hashCode();
}
return hc;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/InQueryExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/InQueryExpression.java
index 4fa82d4bf..5c0c6a83d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/InQueryExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/InQueryExpression.java
@@ -3,6 +3,7 @@ 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.SpiExpressionRequest;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -26,23 +27,19 @@ class InQueryExpression extends AbstractExpression {
this.subQuery = subQuery;
}
- public int queryAutoFetchHash() {
- int hc = InQueryExpression.class.getName().hashCode();
- hc = hc * 31 + propName.hashCode();
- hc = hc * 31 + subQuery.queryAutofetchHash();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(InQueryExpression.class).add(propName);
+
+ subQuery.queryAutofetchHash(builder);
}
- public int queryPlanHash(BeanQueryRequest> request) {
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
// queryPlanHash executes prior to addSql() or addBindValues()
// ... so compiledQuery will exist
compiledSubQuery = compileSubQuery(request);
- int hc = InQueryExpression.class.getName().hashCode();
- hc = hc * 31 + propName.hashCode();
- hc = hc * 31 + subQuery.queryPlanHash(request);
- return hc;
+ queryAutoFetchHash(builder);
}
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
index c8d0b7af1..33cf5c1bf 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
@@ -17,6 +17,7 @@ import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.QueryResultVisitor;
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;
@@ -115,28 +116,20 @@ abstract class JunctionExpression implements Junction, SpiExpression, Expr
/**
* Based on Junction type and all the expression contained.
*/
- public int queryAutoFetchHash() {
- int hc = JunctionExpression.class.getName().hashCode();
- hc = hc * 31 + joinType.hashCode();
-
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(JunctionExpression.class).add(joinType);
List list = exprList.internalList();
for (int i = 0; i < list.size(); i++) {
- hc = hc * 31 + list.get(i).queryAutoFetchHash();
+ list.get(i).queryAutoFetchHash(builder);
}
-
- return hc;
}
- public int queryPlanHash(BeanQueryRequest> request) {
- int hc = JunctionExpression.class.getName().hashCode();
- hc = hc * 31 + joinType.hashCode();
-
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ builder.add(JunctionExpression.class).add(joinType);
List list = exprList.internalList();
for (int i = 0; i < list.size(); i++) {
- hc = hc * 31 + list.get(i).queryPlanHash(request);
+ list.get(i).queryPlanHash(request, builder);
}
-
- return hc;
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java
index d0bec6978..ea20c61f9 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java
@@ -2,10 +2,11 @@ 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.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
-class LikeExpression extends AbstractExpression implements LuceneAwareExpression {
+class LikeExpression extends AbstractExpression {
private static final long serialVersionUID = -5398151809111172380L;
@@ -59,15 +60,13 @@ class LikeExpression extends AbstractExpression implements LuceneAwareExpression
/**
* Based on caseInsensitive and the property name.
*/
- public int queryAutoFetchHash() {
- int hc = LikeExpression.class.getName().hashCode();
- hc = hc * 31 + (caseInsensitive ? 0 : 1);
- hc = hc * 31 + propName.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(LikeExpression.class).add(caseInsensitive).add(propName);
+ builder.bind(1);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/LogicExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/LogicExpression.java
index a8e813227..9a8058f48 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/LogicExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/LogicExpression.java
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.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;
@@ -69,18 +70,16 @@ abstract class LogicExpression implements SpiExpression {
/**
* Based on the joinType plus the two expressions.
*/
- public int queryAutoFetchHash() {
- int hc = LogicExpression.class.getName().hashCode() + joinType.hashCode();
- hc = hc * 31 + expOne.queryAutoFetchHash();
- hc = hc * 31 + expTwo.queryAutoFetchHash();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(LogicExpression.class).add(joinType);
+ expOne.queryAutoFetchHash(builder);
+ expTwo.queryAutoFetchHash(builder);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- int hc = LogicExpression.class.getName().hashCode() + joinType.hashCode();
- hc = hc * 31 + expOne.queryPlanHash(request);
- hc = hc * 31 + expTwo.queryPlanHash(request);
- return hc;
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ builder.add(LogicExpression.class).add(joinType);
+ expOne.queryPlanHash(request, builder);
+ expTwo.queryPlanHash(request, builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java
deleted file mode 100644
index f80115e4e..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.avaje.ebeaninternal.server.expression;
-
-/**
- * Marker interface for lucene aware expressions.
- *
- * @author rbygrave
- */
-public interface LuceneAwareExpression {
-
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/NotExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/NotExpression.java
index e17885ada..6badb6947 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/NotExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/NotExpression.java
@@ -2,12 +2,13 @@ package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.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.server.deploy.BeanDescriptor;
-final class NotExpression implements SpiExpression, LuceneAwareExpression {
+final class NotExpression implements SpiExpression {
private static final long serialVersionUID = 5648926732402355781L;
@@ -36,16 +37,13 @@ final class NotExpression implements SpiExpression, LuceneAwareExpression {
/**
* Based on the expression.
*/
- public int queryAutoFetchHash() {
- int hc = NotExpression.class.getName().hashCode();
- hc = hc * 31 + exp.queryAutoFetchHash();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(NotExpression.class);
+ exp.queryAutoFetchHash(builder);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- int hc = NotExpression.class.getName().hashCode();
- hc = hc * 31 + exp.queryPlanHash(request);
- return hc;
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/NullExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/NullExpression.java
index e5f80d1e0..01533d5a0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/NullExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/NullExpression.java
@@ -1,6 +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.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
@@ -41,15 +42,12 @@ class NullExpression extends AbstractExpression {
/**
* Based on notNull flag and the propertyName.
*/
- public int queryAutoFetchHash() {
- int hc = NullExpression.class.getName().hashCode();
- hc = hc * 31 + (notNull ? 1 : 0);
- hc = hc * 31 + propName.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(NullExpression.class).add(notNull).add(propName);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java b/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java
deleted file mode 100644
index f5a8e6364..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.avaje.ebeaninternal.server.expression;
-
-import javax.persistence.PersistenceException;
-
-/**
- * Exception used to wrap Lucene parsing exceptions.
- */
-public class PersistenceLuceneParseException extends PersistenceException {
-
- private static final long serialVersionUID = 838790249273928392L;
-
- public PersistenceLuceneParseException(Throwable e){
- super(e);
- }
-
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/RawExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/RawExpression.java
index 048c31f39..3f76082de 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/RawExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/RawExpression.java
@@ -1,6 +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.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
@@ -38,14 +39,12 @@ class RawExpression implements SpiExpression {
/**
* Based on the sql.
*/
- public int queryAutoFetchHash() {
- int hc = RawExpression.class.getName().hashCode();
- hc = hc * 31 + sql.hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(RawExpression.class).add(sql);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java
index 49e0fbe7d..5fb5de70d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java
@@ -1,10 +1,11 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.event.BeanQueryRequest;
+import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
-public class SimpleExpression extends AbstractExpression implements LuceneAwareExpression {
+public class SimpleExpression extends AbstractExpression {
private static final long serialVersionUID = -382881395755603790L;
@@ -91,15 +92,13 @@ public class SimpleExpression extends AbstractExpression implements LuceneAwareE
/**
* Based on the type and propertyName.
*/
- public int queryAutoFetchHash() {
- int hc = SimpleExpression.class.getName().hashCode();
- hc = hc * 31 + propName.hashCode();
- hc = hc * 31 + type.name().hashCode();
- return hc;
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(SimpleExpression.class).add(propName).add(type.name());
+ builder.bind(1);
}
- public int queryPlanHash(BeanQueryRequest> request) {
- return queryAutoFetchHash();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ queryAutoFetchHash(builder);
}
public int queryBindHash() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java
index 0f6380346..526ce1e42 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlan.java
@@ -4,6 +4,8 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
+import com.avaje.ebeaninternal.api.HashQueryPlan;
+import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
@@ -34,7 +36,7 @@ public class CQueryPlan {
private final boolean autofetchTuned;
- private final int hash;
+ private final HashQueryPlan hash;
private final boolean rawSql;
@@ -86,7 +88,7 @@ public class CQueryPlan {
this.beanType = beanType;
this.stats = new CQueryPlanStats(this);
- this.hash = 0;
+ this.hash = buildHash(sql, rawSql, rowNumberIncluded, logWhereSql);
this.autofetchTuned = false;
this.sql = sql;
this.sqlTree = sqlTree;
@@ -95,6 +97,13 @@ public class CQueryPlan {
this.logWhereSql = logWhereSql;
this.encryptedProps = sqlTree.getEncryptedProps();
}
+
+ 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();
+ }
public String toString() {
return beanType+" hash:"+hash;
@@ -122,7 +131,7 @@ public class CQueryPlan {
return autofetchTuned;
}
- public int getHash() {
+ public HashQueryPlan getHash() {
return hash;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanStats.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanStats.java
index d492403fe..72de23d8c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanStats.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanStats.java
@@ -131,8 +131,8 @@ public final class CQueryPlanStats {
@Override
- public int getQueryPlanHash() {
- return queryPlan.getHash();
+ public String getQueryPlanHash() {
+ return queryPlan.getHash().toString();
}
@Override
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 6d6d748a8..2defb4988 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -33,6 +33,9 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
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.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionList;
@@ -207,7 +210,7 @@ public class DefaultOrmQuery implements SpiQuery {
/**
* Hash of final query after AutoFetch tuning.
*/
- private int queryPlanHash;
+ private HashQueryPlan queryPlanHash;
private transient PersistenceContext persistenceContext;
@@ -627,62 +630,61 @@ public class DefaultOrmQuery implements SpiQuery {
/**
* Calculate the query hash for either AutoFetch query tuning or Query Plan caching.
*/
- private int calculateHash(BeanQueryRequest> request) {
+ private HashQueryPlan calculateHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
// exclude bind values and things unrelated to
// the sql being generated
+
+ if (builder == null) {
+ builder = new HashQueryPlanBuilder();
+ }
- // must use String name of class as actual class hashCode
- // can change between JVM restarts.
- int hc = beanType.getName().hashCode();
+ builder.add((type == null ? 0 : type.ordinal()+1));
+ builder.add(useIndex).add(autoFetchTuned).add(distinct).add(query);
+ builder.add(firstRow).add(maxRows).add(orderBy).add(forUpdate);
+ builder.add(rawWhereClause).add(additionalWhere).add(additionalHaving);
+ builder.add(mapKey);
+ builder.add(id != null);
+ builder.add(rawSql == null ? 0 : rawSql.queryHash());
- hc = hc * 31 + (type == null ? 0 : type.ordinal());
- hc = hc * 31 + (useIndex == null ? 0 : useIndex.hashCode());
-
- hc = hc * 31 + (rawSql == null ? 0 : rawSql.queryHash());
-
- hc = hc * 31 + (autoFetchTuned ? 31 : 0);
- hc = hc * 31 + (distinct ? 31 : 0);
- hc = hc * 31 + (query == null ? 0 : query.hashCode());
- hc = hc * 31 + detail.queryPlanHash(request);
-
- hc = hc * 31 + (firstRow == 0 ? 0 : firstRow);
- hc = hc * 31 + (maxRows == 0 ? 0 : maxRows);
- hc = hc * 31 + (orderBy == null ? 0 : orderBy.hash());
- hc = hc * 31 + (rawWhereClause == null ? 0 : rawWhereClause.hashCode());
-
- hc = hc * 31 + (additionalWhere == null ? 0 : additionalWhere.hashCode());
- hc = hc * 31 + (additionalHaving == null ? 0 : additionalHaving.hashCode());
- hc = hc * 31 + (mapKey == null ? 0 : mapKey.hashCode());
- hc = hc * 31 + (id == null ? 0 : 1);
-
- if (bindParams != null) {
- hc = hc * 31 + bindParams.getQueryPlanHash();
+ if (detail != null) {
+ detail.queryPlanHash(request, builder);
}
-
+ if (bindParams != null) {
+ bindParams.buildQueryPlanHash(builder);
+ }
+
if (request == null) {
// for AutoFetch...
- hc = hc * 31 + (whereExpressions == null ? 0 : whereExpressions.queryAutoFetchHash());
- hc = hc * 31 + (havingExpressions == null ? 0 : havingExpressions.queryAutoFetchHash());
+ builder.add(true);
+ if (whereExpressions != null) {
+ whereExpressions.queryAutoFetchHash(builder);
+ }
+ if (havingExpressions != null) {
+ havingExpressions.queryAutoFetchHash(builder);
+ }
} else {
// for query plan...
- hc = hc * 31 + (whereExpressions == null ? 0 : whereExpressions.queryPlanHash(request));
- hc = hc * 31 + (havingExpressions == null ? 0 : havingExpressions.queryPlanHash(request));
+ builder.add(false);
+ if (whereExpressions != null) {
+ whereExpressions.queryPlanHash(request, builder);
+ }
+ if (havingExpressions != null) {
+ havingExpressions.queryPlanHash(request, builder);
+ }
}
- hc = hc * 31 + (forUpdate ? 31 : 0);
-
- return hc;
+ return builder.build();
}
/**
* Calculate a hash used by AutoFetch to identify when a query has changed
* (and hence potentially needs a new tuned query plan to be developed).
*/
- public int queryAutofetchHash() {
+ public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder) {
- return calculateHash(null);
+ return calculateHash(null, builder);
}
/**
@@ -695,9 +697,9 @@ public class DefaultOrmQuery implements SpiQuery {
* This is calculated AFTER AutoFetch query tuning has occurred.
*
*/
- public int queryPlanHash(BeanQueryRequest> request) {
+ public HashQueryPlan queryPlanHash(BeanQueryRequest> request) {
- queryPlanHash = calculateHash(request);
+ queryPlanHash = calculateHash(request, null);
return queryPlanHash;
}
@@ -724,12 +726,12 @@ public class DefaultOrmQuery implements SpiQuery {
* (including bind values) before.
*
*/
- public int queryHash() {
+ public HashQuery queryHash() {
// calculateQueryPlanHash is called just after potential AutoFetch tuning
// so queryPlanHash is calculated well before this method is called
- int hc = queryPlanHash;
- hc = hc * 31 + queryBindHash();
- return hc;
+ int hc = queryBindHash();
+
+ return new HashQuery(queryPlanHash, hc);
}
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java
index d525cb077..3e70e6fc7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java
@@ -14,6 +14,7 @@ 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;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
@@ -63,17 +64,19 @@ public class OrmQueryDetail implements Serializable {
/**
* Calculate the hash for the query plan.
*/
- public int queryPlanHash(BeanQueryRequest> request) {
-
- int hc = (baseProps == null ? 1 : baseProps.queryPlanHash(request));
-
- if (fetchPaths != null) {
- for (OrmQueryProperties p : fetchPaths.values()) {
- hc = hc * 31 + p.queryPlanHash(request);
- }
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ if (baseProps == null) {
+ builder.add(false);
+ } else {
+ builder.add(true);
+ baseProps.queryPlanHash(request, builder);
+ }
+
+ if (fetchPaths != null) {
+ for (OrmQueryProperties p : fetchPaths.values()) {
+ p.queryPlanHash(request, builder);
}
-
- return hc;
+ }
}
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
index f813629e0..def146384 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -14,6 +14,7 @@ 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.SpiExpressionFactory;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -355,17 +356,18 @@ public class OrmQueryProperties implements Serializable {
* Calculate the query plan hash.
*/
@SuppressWarnings("unchecked")
- public int queryPlanHash(BeanQueryRequest> request) {
-
- int hc = (path != null ? path.hashCode() : 1);
- if (properties != null){
- hc = hc * 31 + properties.hashCode();
- } else {
- hc = hc * 31 + (included != null ? included.hashCode() : 1);
- }
- hc = hc * 31 + (filterMany != null ? filterMany.queryPlanHash(request) : 1);
-
- return hc;
+ 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);
+ }
}
public String getProperties() {
diff --git a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
index 8c18dd40a..eee8f5eae 100644
--- a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
@@ -20,6 +20,7 @@ import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.QueryResultVisitor;
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.SpiExpressionList;
@@ -283,26 +284,24 @@ public class DefaultExpressionList implements SpiExpressionList {
* Calculate a hash based on the expressions but excluding the actual bind
* values.
*/
- public int queryAutoFetchHash() {
- int hash = DefaultExpressionList.class.getName().hashCode();
+ public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
+ builder.add(DefaultExpressionList.class);
for (int i = 0, size = list.size(); i < size; i++) {
SpiExpression expression = list.get(i);
- hash = hash * 31 + expression.queryAutoFetchHash();
+ expression.queryAutoFetchHash(builder);
}
- return hash;
}
/**
* Calculate a hash based on the expressions but excluding the actual bind
* values.
*/
- public int queryPlanHash(BeanQueryRequest> request) {
- int hash = DefaultExpressionList.class.getName().hashCode();
+ public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ builder.add(DefaultExpressionList.class);
for (int i = 0, size = list.size(); i < size; i++) {
SpiExpression expression = list.get(i);
- hash = hash * 31 + expression.queryPlanHash(request);
+ expression.queryPlanHash(request, builder);
}
- return hash;
}
/**
diff --git a/src/test/java/com/avaje/tests/query/other/TestQueryPlanHash.java b/src/test/java/com/avaje/tests/query/other/TestQueryPlanHash.java
new file mode 100644
index 000000000..34e7b6827
--- /dev/null
+++ b/src/test/java/com/avaje/tests/query/other/TestQueryPlanHash.java
@@ -0,0 +1,56 @@
+package com.avaje.tests.query.other;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.avaje.ebean.Ebean;
+import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
+import com.avaje.ebeaninternal.api.SpiQuery;
+import com.avaje.tests.model.basic.Order;
+
+public class TestQueryPlanHash {
+
+ @Test
+ public void test() {
+
+
+ HashQueryPlanBuilder builder1 = new HashQueryPlanBuilder();
+
+ SpiQuery squery = (SpiQuery)Ebean.find(Order.class);
+ squery.queryAutofetchHash(builder1);
+
+ squery.where().in("id", 1,2,3);
+
+ HashQueryPlanBuilder builder2 = new HashQueryPlanBuilder();
+ squery.queryAutofetchHash(builder2);
+ int q1BindHash = squery.queryBindHash();
+
+ Assert.assertNotSame(builder1.build(), builder2.build());
+
+ HashQueryPlanBuilder builder3 = new HashQueryPlanBuilder();
+ SpiQuery squery2 = (SpiQuery)Ebean.find(Order.class);
+ squery2.where().in("id", 2,2,3);
+
+ squery2.queryAutofetchHash(builder3);
+ int q2BindHash = squery2.queryBindHash();
+
+ Assert.assertEquals(builder3.build(), builder2.build());
+
+ Assert.assertTrue(q1BindHash != q2BindHash);
+
+ HashQueryPlanBuilder builder4 = new HashQueryPlanBuilder();
+ SpiQuery squery4 = (SpiQuery)Ebean.find(Order.class);
+ squery4.where().in("name", 2,2,3);
+
+ squery4.queryAutofetchHash(builder4);
+ int q4BindHash = squery4.queryBindHash();
+
+ //different query plan
+ Assert.assertTrue(!builder3.build().equals(builder4.build()));
+
+ //same bind hash
+ Assert.assertTrue(q4BindHash == q2BindHash);
+
+ }
+
+}