diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java
index afb1e44b8..64a0a0287 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java
@@ -4,11 +4,7 @@ import io.ebeaninternal.server.persist.MultiValueWrapper;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
/**
@@ -54,12 +50,11 @@ public class BindParams implements Serializable {
positionedParameters.clear();
}
- public int queryBindHash() {
- int hc = namedParameters.hashCode();
- for (Param positionedParameter : positionedParameters) {
- hc = hc * 92821 + positionedParameter.hashCode();
+ public void queryBindHash(BindValuesKey key) {
+ key.add(positionedParameters.size());
+ for (Param param : positionedParameters) {
+ param.queryBindHash(key);
}
- return hc;
}
/**
@@ -424,7 +419,14 @@ public class BindParams implements Serializable {
@Override
public boolean equals(Object o) {
- return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode());
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Param param = (Param) o;
+ return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue);
+ }
+
+ void queryBindHash(BindValuesKey key) {
+ key.add(isInParam).add(isOutParam).add(type).add(inValue);
}
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java
new file mode 100644
index 000000000..4a6eac324
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java
@@ -0,0 +1,35 @@
+package io.ebeaninternal.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BindValues used for L2 query cache key matching.
+ *
+ * The equals/hashCode implementation must meet the requirement that the query bind values
+ * match for L2 query cache hit (given the query plan hash is already a match).
+ */
+public class BindValuesKey {
+
+ private final List values = new ArrayList<>();
+
+ /**
+ * Add a bind value.
+ */
+ public BindValuesKey add(Object value) {
+ values.add(value);
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof BindValuesKey && ((BindValuesKey) obj).values.equals(values);
+ }
+
+ @Override
+ public int hashCode() {
+ return values.hashCode();
+ }
+
+
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java
index c14cb88d7..faddb7bee 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java
@@ -6,15 +6,14 @@ package io.ebeaninternal.api;
public class HashQuery {
private final CQueryPlanKey planHash;
-
- private final int bindHash;
+ private final BindValuesKey bindValuesKey;
/**
* Create the HashQuery.
*/
- public HashQuery(CQueryPlanKey planHash, int bindHash) {
+ public HashQuery(CQueryPlanKey planHash, BindValuesKey bindValuesKey) {
this.planHash = planHash;
- this.bindHash = bindHash;
+ this.bindValuesKey = bindValuesKey;
}
@Override
@@ -25,7 +24,7 @@ public class HashQuery {
@Override
public int hashCode() {
int hc = 92821 * planHash.hashCode();
- hc = 92821 * hc + bindHash;
+ hc = 92821 * hc + bindValuesKey.hashCode();
return hc;
}
@@ -37,8 +36,7 @@ public class HashQuery {
if (!(obj instanceof HashQuery)) {
return false;
}
-
HashQuery e = (HashQuery) obj;
- return e.bindHash == bindHash && e.planHash.equals(planHash);
+ return e.bindValuesKey.equals(bindValuesKey) && e.planHash.equals(planHash);
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
index 007e9f29f..8fe986259 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
@@ -54,9 +54,9 @@ public interface SpiExpression extends Expression {
void queryPlanHash(StringBuilder builder);
/**
- * Return the hash value for the values that will be bound.
+ * Build the key for bind values of the query.
*/
- int queryBindHash();
+ void queryBindKey(BindValuesKey key);
/**
* Return true if the expression is the same with respect to bind values.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java
index 12942e7d6..5ba0b6eeb 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java
@@ -629,13 +629,11 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod
CQueryPlanKey prepare(SpiOrmQueryRequest request);
/**
- * Calculate a hash based on the bind values used in the query.
+ * Build the key for the bind values used in the query (for l2 query cache).
*
- * Combined with queryPlanHash() to return getQueryHash (a unique hash for a
- * query).
- *
+ * Combined with queryPlanHash() to return queryHash (a unique key for a query).
*/
- int queryBindHash();
+ void queryBindKey(BindValuesKey key);
/**
* Identifies queries that are exactly the same including bind variables.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java
index d30649149..363bcb08d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -37,9 +38,9 @@ public abstract class AbstractTextExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- return 0;
- }
+ public void queryBindKey(BindValuesKey key) {
+ // do nothing, only execute against document store
+ };
@Override
public boolean isSameByBind(SpiExpression other) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java
index 5a931ec5a..56761dcf2 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -122,14 +123,11 @@ class AllEqualsExpression extends NonPrepareExpression {
}
@Override
- public int queryBindHash() {
-
- int hc = 92821;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(propMap.size());
for (Object value : propMap.values()) {
- hc = hc * 92821 + (value == null ? 0 : value.hashCode());
+ key.add(value);
}
-
- return hc;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java
index 8c12bec60..7a9e903f6 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -49,12 +50,11 @@ public class ArrayContainsExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- int hc = values[0].hashCode();
- for (int i = 1; i < values.length; i++) {
- hc = hc * 92821 + values[i].hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(values.length);
+ for (Object value : values) {
+ key.add(value);
}
- return hc;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java
index 815223dfe..f9cd530c8 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -33,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- return empty ? 0 : 92821;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(empty);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java
index bfdfa1d52..ed3d58d8b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -49,10 +50,8 @@ class BetweenExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- int hc = low().hashCode();
- hc = hc * 92821 + high().hashCode();
- return hc;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(low()).add(high());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java
index a50ec888f..55b08380d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -95,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression {
}
@Override
- public int queryBindHash() {
- return val().hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(val());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java
index 073ee51df..460af37ec 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -39,8 +40,8 @@ class BitwiseExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- return Long.hashCode(flags);
+ public void queryBindKey(BindValuesKey key) {
+ key.add(flags).add(match);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java
index 52e39fbfa..3a6224d9c 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -69,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression {
}
@Override
- public int queryBindHash() {
- return val().hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(val());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java
index aade99f5a..e6353edd0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java
@@ -5,6 +5,7 @@ import io.ebean.LikeType;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebean.util.SplitName;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -237,16 +238,12 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
builder.append("]");
}
- /**
- * Return a hash for the actual bind values used.
- */
@Override
- public int queryBindHash() {
- int hc = DefaultExampleExpression.class.getName().hashCode();
- for (SpiExpression aList : list) {
- hc = hc * 92821 + aList.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(list.size());
+ for (SpiExpression expr : list) {
+ expr.queryBindKey(key);
}
- return hc;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java
index ab80ae9e5..97899f091 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java
@@ -26,6 +26,7 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -674,16 +675,12 @@ public class DefaultExpressionList implements SpiExpressionList {
builder.append("]");
}
- /**
- * Calculate a hash based on the expressions.
- */
@Override
- public int queryBindHash() {
- int hash = DefaultExpressionList.class.getName().hashCode();
- for (SpiExpression aList : list) {
- hash = hash * 92821 + aList.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(list.size());
+ for (SpiExpression expr : list) {
+ expr.queryBindKey(key);
}
- return hash;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java
index 32ae2bccc..4ba939066 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -91,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
}
@Override
- public int queryBindHash() {
- return subQuery.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ subQuery.queryBindKey(key);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java
index b5280b868..94cab6de7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -77,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
}
@Override
- public int queryBindHash() {
- return value.hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(value);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java
index 4c08b24d2..edf56b443 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -133,8 +134,11 @@ public class IdInExpression extends NonPrepareExpression {
}
@Override
- public int queryBindHash() {
- return idCollection.hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(idCollection.size());
+ for (Object elem : idCollection) {
+ key.add(elem);
+ }
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java
index 7009a8b44..0bc5a4588 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -176,12 +177,11 @@ class InExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- int hc = 92821;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(bindValues.size());
for (Object bindValue : bindValues) {
- hc = 92821 * hc + bindValue.hashCode();
+ key.add(bindValue);
}
- return hc;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java
index 96cc7c182..e5bd27413 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Pairs;
import io.ebean.Pairs.Entry;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -124,12 +125,11 @@ class InPairsExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- int hc = 92821;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(entries.size());
for (Pairs.Entry entry : entries) {
- hc = 92821 * hc + entry.hashCode();
+ key.add(entry.getA()).add(entry.getB());
}
- return hc;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java
index 83507af4b..48647d0c2 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -72,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor
}
@Override
- public int queryBindHash() {
- return subQuery.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ subQuery.queryBindKey(key);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java
index e1d170b43..04b242ba8 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -47,10 +48,8 @@ class InRangeExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- int hc = low().hashCode();
- hc = hc * 92821 + high().hashCode();
- return hc;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(low()).add(high());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java
index d3a3a1da6..d6b878bfa 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -103,8 +104,8 @@ class IsEmptyExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- return 1;
+ public void queryBindKey(BindValuesKey key) {
+ // no bind values
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java
index f60b125cb..1d954dcc1 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -83,10 +84,8 @@ class JsonPathExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- int hc = (value == null) ? 0 : value.hashCode();
- hc = (upperValue == null) ? hc : hc * 92821 + upperValue.hashCode();
- return hc;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(value).add(upperValue);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java
index 0aae30886..a253bbb2b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java
@@ -25,6 +25,7 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -113,9 +114,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startBool(type);
- List list = exprList.internalList();
- for (SpiExpression aList : list) {
- aList.writeDocQuery(context);
+ for (SpiExpression expr : exprList.internalList()) {
+ expr.writeDocQuery(context);
}
context.endBool();
}
@@ -123,9 +123,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
@Override
public void writeDocQueryJunction(DocQueryContext context) throws IOException {
context.startBoolGroupList(type);
- List list = exprList.internalList();
- for (SpiExpression aList : list) {
- aList.writeDocQuery(context);
+ for (SpiExpression expr : exprList.internalList()) {
+ expr.writeDocQuery(context);
}
context.endBoolGroupList();
}
@@ -138,18 +137,15 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
@Override
public void containsMany(BeanDescriptor> desc, ManyWhereJoins manyWhereJoin) {
-
List list = exprList.internalList();
-
// get the current state for 'require outer joins'
boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins();
if (type == Type.OR) {
// turn on outer joins required for disjunction expressions
manyWhereJoin.setRequireOuterJoins(true);
}
-
- for (SpiExpression aList : list) {
- aList.containsMany(desc, manyWhereJoin);
+ for (SpiExpression expr : list) {
+ expr.containsMany(desc, manyWhereJoin);
}
if (type == Type.OR && !parentOuterJoins) {
// restore state to not forcing outer joins
@@ -176,18 +172,14 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
@Override
public void addBindValues(SpiExpressionRequest request) {
-
- List list = exprList.internalList();
- for (SpiExpression aList : list) {
- aList.addBindValues(request);
+ for (SpiExpression expr : exprList.internalList()) {
+ expr.addBindValues(request);
}
}
@Override
public void addSql(SpiExpressionRequest request) {
-
List list = exprList.internalList();
-
if (!list.isEmpty()) {
request.append(type.prefix());
request.append("(");
@@ -204,9 +196,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
@Override
public void prepareExpression(BeanQueryRequest> request) {
- List list = exprList.internalList();
- for (SpiExpression aList : list) {
- aList.prepareExpression(request);
+ for (SpiExpression expr : exprList.internalList()) {
+ expr.prepareExpression(request);
}
}
@@ -225,13 +216,10 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
}
@Override
- public int queryBindHash() {
- int hc = JunctionExpression.class.getName().hashCode();
- List list = exprList.internalList();
- for (SpiExpression aList : list) {
- hc = hc * 92821 + aList.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ for (SpiExpression expr : exprList.internalList()) {
+ expr.queryBindKey(key);
}
- return hc;
}
@Override
@@ -275,7 +263,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
return exprList.textCommonTerms(search, options);
}
-
@Override
public ExpressionList allEq(Map propertyMap) {
return exprList.allEq(propertyMap);
@@ -1025,7 +1012,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
@Override
public String nestedPath(BeanDescriptor> desc) {
-
PrepareDocNested.prepare(exprList, desc, type);
String nestedPath = exprList.allDocNestedPath;
if (nestedPath != null) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java
index 6886203c4..f40501929 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -70,8 +71,8 @@ class LikeExpression extends AbstractValueExpression {
}
@Override
- public int queryBindHash() {
- return strValue().hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(strValue());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java
index 482aac6e8..a66e2e88a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.Junction;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -168,10 +169,8 @@ abstract class LogicExpression implements SpiExpression {
}
@Override
- public int queryBindHash() {
- int hc = expOne.queryBindHash();
- hc = hc * 92821 + expTwo.queryBindHash();
- return hc;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(expOne).add(expTwo);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java
index c3376f656..f64933df5 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -54,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- return val.hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(val);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java
index 77f46828b..b76bfef4d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -79,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression {
}
@Override
- public int queryBindHash() {
- return delegate.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ delegate.queryBindKey(key);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java
index 28c9e3c78..6c8d709e7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -74,9 +75,8 @@ class NoopExpression implements SpiExpression {
}
@Override
- public int queryBindHash() {
+ public void queryBindKey(BindValuesKey key) {
// no bind values
- return 0;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java
index 60ade47b0..462e60d9e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -99,8 +100,8 @@ final class NotExpression implements SpiExpression {
}
@Override
- public int queryBindHash() {
- return exp.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ exp.queryBindKey(key);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java
index 90894dbde..2808acce7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -94,7 +95,7 @@ class NullExpression extends AbstractExpression {
}
@Override
- public int queryBindHash() {
- return (notNull ? 1 : 0);
+ public void queryBindKey(BindValuesKey key) {
+ key.add(notNull);
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java
index 295ddc75b..9e1c3e8dc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -73,12 +74,11 @@ class RawExpression extends NonPrepareExpression {
}
@Override
- public int queryBindHash() {
- int hc = sql.hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(values.length);
for (Object value : values) {
- hc = hc * 92821 + value.hashCode();
+ key.add(value);
}
- return hc;
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java
index 0f8de90ec..bc23b84f2 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java
@@ -5,6 +5,7 @@ import io.ebean.plugin.ExpressionPath;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import java.io.IOException;
@@ -121,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression {
}
@Override
- public int queryBindHash() {
- return value().hashCode();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(value());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java
deleted file mode 100644
index 6d71e8d79..000000000
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package io.ebeaninternal.server.persist;
-
-import java.util.ArrayList;
-
-/**
- * Holds a list of bind values for binding to a PreparedStatement.
- */
-class BindValues {
-
- private final ArrayList list = new ArrayList<>();
-
- /**
- * Create with a Binder.
- */
- public BindValues() {
- }
-
- /**
- * Add a bind value with its JDBC datatype.
- *
- * @param value the bind value
- * @param dbType the type as per java.sql.Types
- */
- public void add(Object value, int dbType, String name) {
- list.add(new Value(value, dbType, name));
- }
-
- /**
- * List of bind values.
- */
- public ArrayList values() {
- return list;
- }
-
- /**
- * A Value has additionally the JDBC data type.
- */
- public static class Value {
-
- private final Object value;
-
- private final int dbType;
-
- private final String name;
-
- /**
- * Create the value.
- */
- Value(Object value, int dbType, String name) {
- this.value = value;
- this.dbType = dbType;
- this.name = name;
- }
-
- /**
- * Return the type as per java.sql.Types.
- */
- public int getDbType() {
- return dbType;
- }
-
- /**
- * Return the value.
- */
- public Object getValue() {
- return value;
- }
-
- /**
- * Return the property name.
- */
- public String getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return String.valueOf(value);
- }
- }
-}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java
index 1d94d7a81..600a7e21a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java
@@ -77,33 +77,6 @@ public class Binder {
return asOfStandardsBased;
}
- /**
- * Bind the values to the Prepared Statement.
- */
- public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf) throws SQLException {
- String logPrefix = "";
- ArrayList list = bindValues.values();
- for (BindValues.Value bindValue : list) {
- Object val = bindValue.getValue();
- int dt = bindValue.getDbType();
- bindObject(dataBind, val, dt);
-
- if (bindBuf != null) {
- bindBuf.append(logPrefix);
- if (logPrefix.isEmpty()) {
- logPrefix = ", ";
- }
- bindBuf.append(bindValue.getName());
- bindBuf.append("=");
- if (isLob(dt)) {
- bindBuf.append("[LOB]");
- } else {
- bindBuf.append(val);
- }
- }
- }
- }
-
/**
* Bind the parameters to the preparedStatement returning the bind log.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 96a923637..834abb856 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -1,30 +1,7 @@
package io.ebeaninternal.server.querydefn;
-import io.ebean.CacheMode;
-import io.ebean.CountDistinctOrder;
-import io.ebean.Database;
-import io.ebean.DtoQuery;
-import io.ebean.Expression;
-import io.ebean.ExpressionFactory;
-import io.ebean.ExpressionList;
-import io.ebean.FetchConfig;
-import io.ebean.FetchGroup;
-import io.ebean.FetchPath;
-import io.ebean.FutureIds;
-import io.ebean.FutureList;
-import io.ebean.FutureRowCount;
-import io.ebean.OrderBy;
+import io.ebean.*;
import io.ebean.OrderBy.Property;
-import io.ebean.PagedList;
-import io.ebean.PersistenceContextScope;
-import io.ebean.ProfileLocation;
-import io.ebean.Query;
-import io.ebean.QueryIterator;
-import io.ebean.QueryType;
-import io.ebean.RawSql;
-import io.ebean.Transaction;
-import io.ebean.UpdateQuery;
-import io.ebean.Version;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
@@ -32,29 +9,10 @@ import io.ebean.bean.PersistenceContext;
import io.ebean.event.BeanQueryRequest;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.plugin.BeanType;
-import io.ebeaninternal.api.BindParams;
-import io.ebeaninternal.api.CQueryPlanKey;
-import io.ebeaninternal.api.CacheIdLookup;
-import io.ebeaninternal.api.CacheIdLookupMany;
-import io.ebeaninternal.api.CacheIdLookupSingle;
-import io.ebeaninternal.api.HashQuery;
-import io.ebeaninternal.api.ManyWhereJoins;
-import io.ebeaninternal.api.NaturalKeyQueryData;
-import io.ebeaninternal.api.SpiEbeanServer;
-import io.ebeaninternal.api.SpiExpression;
-import io.ebeaninternal.api.SpiExpressionList;
-import io.ebeaninternal.api.SpiExpressionValidation;
-import io.ebeaninternal.api.SpiNamedParam;
-import io.ebeaninternal.api.SpiQuery;
-import io.ebeaninternal.api.SpiQuerySecondary;
-import io.ebeaninternal.api.SpiTransaction;
+import io.ebeaninternal.api.*;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
-import io.ebeaninternal.server.deploy.BeanDescriptor;
-import io.ebeaninternal.server.deploy.BeanNaturalKey;
-import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
-import io.ebeaninternal.server.deploy.InheritInfo;
-import io.ebeaninternal.server.deploy.TableJoin;
+import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.el.ElPropertyDeploy;
import io.ebeaninternal.server.expression.DefaultExpressionList;
import io.ebeaninternal.server.expression.IdInExpression;
@@ -66,14 +24,7 @@ import io.ebeaninternal.server.transaction.ExternalJdbcTransaction;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -91,8 +42,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery {
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
- private final ReentrantLock lock = new ReentrantLock();
-
private final Class beanType;
private final ExpressionFactory expressionFactory;
@@ -1269,22 +1218,13 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery {
}
}
- /**
- * Calculate a hash based on the bind values used in the query.
- *
- * Used with queryPlanHash() to get a unique hash for a query.
- *
- */
@Override
- public int queryBindHash() {
- int hc = (id == null ? 0 : id.hashCode());
- hc = hc * 92821 + (whereExpressions == null ? 0 : whereExpressions.queryBindHash());
- hc = hc * 92821 + (havingExpressions == null ? 0 : havingExpressions.queryBindHash());
- hc = hc * 92821 + (bindParams == null ? 0 : bindParams.queryBindHash());
- hc = hc * 92821 + (asOf == null ? 0 : asOf.hashCode());
- hc = hc * 92821 + (versionsStart == null ? 0 : versionsStart.hashCode());
- hc = hc * 92821 + (versionsEnd == null ? 0 : versionsEnd.hashCode());
- return hc;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(id);
+ if (whereExpressions != null) whereExpressions.queryBindKey(key);
+ if (havingExpressions != null) havingExpressions.queryBindKey(key);
+ if (bindParams != null) bindParams.queryBindHash(key);
+ key.add(asOf).add(versionsStart).add(versionsEnd);
}
/**
@@ -1298,8 +1238,9 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery {
public HashQuery queryHash() {
// calculateQueryPlanHash is called just after potential AutoTune tuning
// so queryPlanHash is calculated well before this method is called
- int hc = queryBindHash();
- return new HashQuery(queryPlanKey, hc);
+ BindValuesKey bindKey = new BindValuesKey();
+ queryBindKey(bindKey);
+ return new HashQuery(queryPlanKey, bindKey);
}
@Override
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java
index 1953f2959..eb12ff460 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
+import io.ebeaninternal.api.BindValuesKey;
public class RawExpressionTest extends BaseExpressionTest {
@@ -61,11 +62,16 @@ public class RawExpressionTest extends BaseExpressionTest {
}
public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) {
- assertThat(exp0.queryBindHash()).isNotEqualTo(exp1.queryBindHash());
+ assertThat(bindKey(exp0)).isNotEqualTo(bindKey(exp1));
}
public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) {
- assertThat(exp0.queryBindHash()).isEqualTo(exp1.queryBindHash());
+ assertThat(bindKey(exp0)).isEqualTo(bindKey(exp1));
}
+ private BindValuesKey bindKey(RawExpression query) {
+ BindValuesKey bindValuesKey = new BindValuesKey();
+ query.queryBindKey(bindValuesKey);
+ return bindValuesKey;
+ }
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java
new file mode 100644
index 000000000..1a7d3b39f
--- /dev/null
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java
@@ -0,0 +1,38 @@
+package io.ebeaninternal.server.querydefn;
+
+import io.ebeaninternal.api.BindValuesKey;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BindValuesKeyTest {
+
+ @Test
+ public void update_with_null() {
+
+ BindValuesKey hash = new BindValuesKey();
+ hash.add(1).add(null).add("hello");
+
+ BindValuesKey hash2 = new BindValuesKey();
+ hash2.add(1).add(null).add("hello");
+
+ assertThat(hash).isEqualTo(hash2);
+ }
+
+ @Test
+ public void notEqual() {
+
+ BindValuesKey hash = new BindValuesKey();
+ hash.add(1).add(null).add("hello");
+
+ BindValuesKey hash2 = new BindValuesKey();
+ hash2.add(1).add("hello");
+
+ BindValuesKey hash3 = new BindValuesKey();
+ hash2.add(1).add(null);
+
+ assertThat(hash).isNotEqualTo(hash2);
+ assertThat(hash).isNotEqualTo(hash3);
+ assertThat(hash2).isNotEqualTo(hash3);
+ }
+}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java
index d9e621221..b2b4902ad 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java
@@ -4,6 +4,7 @@ package io.ebeaninternal.server.querydefn;
import io.ebean.BaseTestCase;
import io.ebean.CacheMode;
import io.ebean.Ebean;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.core.OrmQueryRequest;
import org.junit.Test;
@@ -62,7 +63,7 @@ public class DefaultOrmQueryTest extends BaseTestCase {
prepare(q1, q2);
assertThat(q1.createQueryPlanKey()).isNotEqualTo(q2.createQueryPlanKey());
- assertThat(q1.queryBindHash()).isNotEqualTo(q2.queryBindHash());
+ assertThat(bindKey(q1)).isNotEqualTo(bindKey(q2));
}
@Test
@@ -73,7 +74,7 @@ public class DefaultOrmQueryTest extends BaseTestCase {
prepare(q1, q2);
assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey());
- assertThat(q1.queryBindHash()).isNotEqualTo(q2.queryBindHash());
+ assertThat(bindKey(q1)).isNotEqualTo(bindKey(q2));
}
@Test
@@ -84,7 +85,7 @@ public class DefaultOrmQueryTest extends BaseTestCase {
prepare(q1, q2);
assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey());
- assertThat(q1.queryBindHash()).isEqualTo(q2.queryBindHash());
+ assertThat(bindKey(q1)).isEqualTo(bindKey(q2));
}
@Test
@@ -110,4 +111,10 @@ public class DefaultOrmQueryTest extends BaseTestCase {
OrmQueryRequest r2 = createQueryRequest(SpiQuery.Type.LIST, q2, null);
q2.prepare(r2);
}
+
+ private BindValuesKey bindKey(DefaultOrmQuery query) {
+ BindValuesKey key = new BindValuesKey();
+ query.queryBindKey(key);
+ return key;
+ }
}
diff --git a/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java b/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java
index f9e732220..4c4d56109 100644
--- a/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java
+++ b/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java
@@ -3,7 +3,7 @@ package org.tests.cache;
import io.ebean.BaseTestCase;
import io.ebean.CacheMode;
import io.ebean.DB;
-import io.ebean.Ebean;
+import io.ebean.ExpressionList;
import io.ebean.bean.BeanCollection;
import io.ebean.cache.ServerCache;
import org.ebeantest.LoggedSqlCollector;
@@ -14,6 +14,7 @@ import org.tests.model.basic.ResetBasicData;
import org.tests.model.cache.EColAB;
import java.util.List;
+import java.util.function.Consumer;
import static org.assertj.core.api.Assertions.assertThat;
@@ -26,8 +27,7 @@ public class TestQueryCache extends BaseTestCase {
new EColAB("02", "10").save();
List list1 =
- Ebean.getServer(null)
- .find(EColAB.class)
+ DB.find(EColAB.class)
.setUseQueryCache(true)
.where()
.eq("columnA", "01")
@@ -35,8 +35,7 @@ public class TestQueryCache extends BaseTestCase {
.findList();
List list2 =
- Ebean.getServer(null)
- .find(EColAB.class)
+ DB.find(EColAB.class)
.setUseQueryCache(true)
.where()
.eq("columnA", "02")
@@ -57,7 +56,7 @@ public class TestQueryCache extends BaseTestCase {
new EColAB("03", "SingleAttribute").save();
new EColAB("03", "SingleAttribute").save();
- List colA_first = Ebean.getServer(null)
+ List colA_first = DB
.find(EColAB.class)
.setUseQueryCache(true)
.setDistinct(true)
@@ -66,7 +65,7 @@ public class TestQueryCache extends BaseTestCase {
.eq("columnB", "SingleAttribute")
.findSingleAttributeList();
- List colA_Second = Ebean.getServer(null)
+ List colA_Second = DB
.find(EColAB.class)
.setUseQueryCache(true)
.setDistinct(true)
@@ -77,7 +76,7 @@ public class TestQueryCache extends BaseTestCase {
assertThat(colA_Second).isSameAs(colA_first);
- List colA_NotDistinct = Ebean.getServer(null)
+ List colA_NotDistinct = DB
.find(EColAB.class)
.setUseQueryCache(true)
.select("columnA")
@@ -89,7 +88,7 @@ public class TestQueryCache extends BaseTestCase {
// ensure that findCount & findSingleAttribute use different
// slots in cache. If not a "Cannot cast List to int" should happen.
- int count = Ebean.getServer(null)
+ int count = DB
.find(EColAB.class)
.setUseQueryCache(true)
.select("columnA")
@@ -107,13 +106,13 @@ public class TestQueryCache extends BaseTestCase {
LoggedSqlCollector.start();
- int count0 = Ebean.find(EColAB.class)
+ int count0 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "count")
.findCount();
- int count1 = Ebean.find(EColAB.class)
+ int count1 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "count")
@@ -126,7 +125,7 @@ public class TestQueryCache extends BaseTestCase {
// and now, ensure that we hit the database
LoggedSqlCollector.start();
- int count2 = Ebean.find(EColAB.class)
+ int count2 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.OFF)
.where()
.eq("columnB", "count")
@@ -142,13 +141,13 @@ public class TestQueryCache extends BaseTestCase {
LoggedSqlCollector.start();
- int count0 = Ebean.find(EColAB.class)
+ int count0 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "abc")
.findCount();
- int count1 = Ebean.find(EColAB.class)
+ int count1 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "def")
@@ -167,13 +166,13 @@ public class TestQueryCache extends BaseTestCase {
LoggedSqlCollector.start();
- int count0 = Ebean.find(EColAB.class)
+ int count0 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "uvw")
.findCount();
- int count1 = Ebean.find(EColAB.class)
+ int count1 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.PUT)
.where()
.eq("columnB", "uvw")
@@ -193,13 +192,13 @@ public class TestQueryCache extends BaseTestCase {
LoggedSqlCollector.start();
- int count0 = Ebean.find(EColAB.class)
+ int count0 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.PUT)
.where()
.eq("columnB", "xyz")
.findCount();
- int count1 = Ebean.find(EColAB.class)
+ int count1 = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "xyz")
@@ -214,26 +213,26 @@ public class TestQueryCache extends BaseTestCase {
@Test
@SuppressWarnings("unchecked")
- public void test() {
+ public void testReadOnlyFind() {
ResetBasicData.reset();
- ServerCache customerCache = Ebean.getServerCacheManager().getQueryCache(Customer.class);
+ ServerCache customerCache = DB.getServerCacheManager().getQueryCache(Customer.class);
customerCache.clear();
- List list = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where()
+ List list = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where()
.ilike("name", "Rob").findList();
BeanCollection bc = (BeanCollection) list;
Assert.assertTrue(bc.isReadOnly());
Assert.assertFalse(bc.isEmpty());
Assert.assertTrue(!list.isEmpty());
- Assert.assertTrue(Ebean.getBeanState(list.get(0)).isReadOnly());
+ Assert.assertTrue(DB.getBeanState(list.get(0)).isReadOnly());
- List list2 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where()
+ List list2 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where()
.ilike("name", "Rob").findList();
- List list2B = Ebean.find(Customer.class).setUseQueryCache(true)
+ List list2B = DB.find(Customer.class).setUseQueryCache(true)
// .setReadOnly(true)
.where().ilike("name", "Rob").findList();
@@ -245,7 +244,7 @@ public class TestQueryCache extends BaseTestCase {
- List list3 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where()
+ List list3 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where()
.ilike("name", "Rob").findList();
Assert.assertNotSame(list, list3);
@@ -269,13 +268,13 @@ public class TestQueryCache extends BaseTestCase {
LoggedSqlCollector.start();
- List colA_first = Ebean.find(EColAB.class)
+ List colA_first = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "someId")
.findIds();
- List colA_second = Ebean.find(EColAB.class)
+ List colA_second = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.ON)
.where()
.eq("columnB", "someId")
@@ -289,7 +288,7 @@ public class TestQueryCache extends BaseTestCase {
// and now, ensure that we hit the database
LoggedSqlCollector.start();
- colA_second = Ebean.find(EColAB.class)
+ colA_second = DB.find(EColAB.class)
.setUseQueryCache(CacheMode.PUT)
.where()
.eq("columnB", "someId")
@@ -299,4 +298,35 @@ public class TestQueryCache extends BaseTestCase {
assertThat(sql).hasSize(1);
}
+ @Test
+ public void findCountDifferentQueriesBit() {
+ DB.getDefault().getPluginApi().getServerCacheManager().clearAll();
+ differentFindCount(q->q.bitwiseAny("id",1), q->q.bitwiseAny("id",0));
+ differentFindCount(q->q.bitwiseAll("id",1), q->q.bitwiseAll("id",0));
+ // differentFindCount(q->q.bitwiseNot("id",1), q->q.bitwiseNot("id",0)); NOT 1 == AND 1 = 0
+ differentFindCount(q->q.bitwiseAnd("id",1, 0), q->q.bitwiseAnd("id",1, 1));
+
+ differentFindCount(q->q.bitwiseAnd("id",2, 0), q->q.bitwiseAnd("id",4, 0));
+ differentFindCount(q->q.bitwiseAnd("id",2, 1), q->q.bitwiseAnd("id",4, 1));
+ // Will produce hash collision
+ differentFindCount(q->q.bitwiseAnd("id",10, 0), q->q.bitwiseAnd("id",0, 928210));
+
+ }
+
+ void differentFindCount(Consumer> q0, Consumer> q1) {
+ LoggedSqlCollector.start();
+
+ ExpressionList el0 = DB.find(EColAB.class).setUseQueryCache(CacheMode.ON).where();
+ q0.accept(el0);
+ el0.findCount();
+
+ ExpressionList el1 = DB.find(EColAB.class).setUseQueryCache(CacheMode.ON).where();
+ q1.accept(el1);
+ el1.findCount();
+
+ List sql = LoggedSqlCollector.stop();
+
+ assertThat(sql).hasSize(2); // different queries
+ }
+
}