From 769e4bb488b509bc069231ce7ac474b80e9f3fb5 Mon Sep 17 00:00:00 2001
From: Robin Bygrave
* That is, if the hash changes then the query will be considered different * from an AutoTune perspective and get different tuning. *
*/ - void queryAutoTuneHash(HashQueryPlanBuilder builder); - - /** - * Calculate a hash value for the expression. - * This includes the expression type and property but should exclude - * the bind values. - *- * This is used where queries are the same except for the bind values, in which - * case the query execution plan can be reused. - *
- */ - void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder); - + void queryPlanHash(HashQueryPlanBuilder builder); + /** * Return the hash value for the values that will be bound. */ int queryBindHash(); - + /** * Add some sql to the query. *
@@ -61,7 +55,7 @@ public interface SpiExpression extends Expression {
/**
* Add the parameter values to be set against query. For each ? place holder
* there should be a corresponding value that is added to the bindList.
- *
+ *
* @param request
* the associated request.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
index 1cacf0a45..98c7ee1fd 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
@@ -1,13 +1,12 @@
package com.avaje.ebeaninternal.api;
-import java.util.ArrayList;
-import java.util.List;
-
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import java.util.List;
+
/**
* Internal extension of ExpressionList.
*/
@@ -59,13 +58,18 @@ public interface SpiExpressionList
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 e8d0cc46b..4140c607e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -1,5 +1,14 @@ package com.avaje.ebeaninternal.server.querydefn; +import com.avaje.ebean.FetchConfig; +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; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.query.SplitName; + +import javax.persistence.PersistenceException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -11,17 +20,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.FetchConfig; -import com.avaje.ebean.event.BeanQueryRequest; -import com.avaje.ebeaninternal.api.HashQueryPlanBuilder; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; -import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.query.SplitName; - /** * Represents the internal structure of an Object Relational query. *
@@ -65,17 +63,17 @@ public class OrmQueryDetail implements Serializable {
/**
* Calculate the hash for the query plan.
*/
- public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ public void queryPlanHash(HashQueryPlanBuilder builder) {
if (baseProps == null) {
builder.add(false);
} else {
builder.add(true);
- baseProps.queryPlanHash(request, builder);
+ baseProps.queryPlanHash(builder);
}
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
- p.queryPlanHash(request, builder);
+ p.queryPlanHash(builder);
}
}
}
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 21dba9a65..75171d86e 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -1,18 +1,9 @@
package com.avaje.ebeaninternal.server.querydefn;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
-import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
@@ -23,6 +14,14 @@ import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.util.FilterExpressionList;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* Represents the Properties of an Object Relational query.
*/
@@ -334,7 +333,7 @@ public class OrmQueryProperties implements Serializable {
* Calculate the query plan hash.
*/
@SuppressWarnings("unchecked")
- public void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder) {
+ public void queryPlanHash(HashQueryPlanBuilder builder) {
builder.add(path);
if (properties != null) {
@@ -344,7 +343,7 @@ public class OrmQueryProperties implements Serializable {
}
builder.add(filterMany != null);
if (filterMany != null) {
- filterMany.queryPlanHash(request, builder);
+ filterMany.queryPlanHash(builder);
}
builder.add(lazyFetchBatch);
builder.add(queryFetchBatch);
diff --git a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
index 7715da72c..313c31288 100644
--- a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
@@ -349,15 +349,10 @@ public class DefaultExpressionList
- * Excludes bind values and occurs prior to AutoTune potentially
- * tuning/modifying the query.
- *
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 f21eae234..7f3dea20d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -759,13 +759,10 @@ public class DefaultOrmQuery
@@ -818,7 +806,7 @@ public class DefaultOrmQuery
@@ -65,4 +75,10 @@ public interface SpiExpression extends Expression {
* Validate all the properties/paths associated with this expression.
*/
void validate(SpiExpressionValidation validation);
+
+ /**
+ * Return a copy of the expression for use in the query plan key.
+ */
+ SpiExpression copyForPlanKey();
+
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
index 98c7ee1fd..de075203c 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
@@ -1,16 +1,13 @@
package com.avaje.ebeaninternal.api;
-import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.ExpressionList;
-import com.avaje.ebean.event.BeanQueryRequest;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.List;
/**
* Internal extension of ExpressionList.
*/
-public interface SpiExpressionList
- * Predicates on Many properties require an extra independent join clause.
- *
- * The list of expressions are evaluated in order building a sql statement
- * with bind parameters.
- *
- * Expressions are evaluated in order and all the resulting bind values are
- * returned as a List.
- *
- * This is used AFTER AutoTune has potentially tuned the query. This is
- * used to identify and reused query plans (the final SQL string and
- * associated SqlTree object).
- *
- * Excludes the actual bind values (as they don't effect the query plan).
+ * The query plan excludes actual bind values (as they don't effect the query plan).
*
- * After deserialisation so that it can be further modified.
- *
@@ -88,6 +81,14 @@ public class DefaultExpressionList
@@ -45,7 +44,7 @@ public class CQueryPlan {
private final boolean autoTuned;
- private final HashQueryPlan hash;
+ private final CQueryPlanKey planKey;
private final boolean rawSql;
@@ -79,7 +78,7 @@ public class CQueryPlan {
this.server = request.getServer();
this.beanType = request.getBeanDescriptor().getBeanType();
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
- this.hash = request.getQueryPlanHash();
+ this.planKey = request.getQueryPlanKey();
this.autoTuned = request.getQuery().isAutoTuned();
if (sqlRes != null) {
this.sql = sqlRes.getSql();
@@ -103,7 +102,7 @@ public class CQueryPlan {
this.server = request.getServer();
this.beanType = request.getBeanDescriptor().getBeanType();
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
- this.hash = buildHash(sql, rawSql, rowNumberIncluded, logWhereSql);
+ this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
this.autoTuned = false;
this.sql = sql;
this.sqlTree = sqlTree;
@@ -114,15 +113,13 @@ public class CQueryPlan {
}
- private HashQueryPlan buildHash(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
- HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
- builder.add(sql).add(rawSql).add(rowNumberIncluded).add(logWhereSql);
- builder.addRawSql(sql);
- return builder.build();
+ private CQueryPlanKey buildPlanKey(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
+
+ return new RawSqlQueryPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
}
public String toString() {
- return beanType + " hash:" + hash;
+ return beanType + " hash:" + planKey;
}
public Class> getBeanType() {
@@ -147,8 +144,8 @@ public class CQueryPlan {
return autoTuned;
}
- public HashQueryPlan getHash() {
- return hash;
+ public CQueryPlanKey getPlanKey() {
+ return planKey;
}
/**
@@ -164,7 +161,7 @@ public class CQueryPlan {
private String calcAuditQueryKey() {
// rawSql needs to include the MD5 hash of the sql
- return rawSql ? hash.getPartialKey() + "_" + getSqlMd5Hash() : hash.getPartialKey();
+ return rawSql ? planKey.getPartialKey() + "_" + getSqlMd5Hash() : planKey.getPartialKey();
}
/**
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 31636a05e..6196867ac 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanStats.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanStats.java
@@ -233,7 +233,7 @@ public final class CQueryPlanStats {
@Override
public String getQueryPlanHash() {
- return queryPlan.getHash().toString();
+ return queryPlan.getPlanKey().toString();
}
@Override
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java
index 152ac6712..c42ec239f 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java
@@ -13,7 +13,7 @@ import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
-import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
+import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlQueryPlanKey.java b/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlQueryPlanKey.java
new file mode 100644
index 000000000..f26301ba3
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlQueryPlanKey.java
@@ -0,0 +1,56 @@
+package com.avaje.ebeaninternal.server.query;
+
+import com.avaje.ebeaninternal.api.CQueryPlanKey;
+
+/**
+ * QueryPlanKey for RawSql queries.
+ */
+public class RawSqlQueryPlanKey implements CQueryPlanKey {
+
+ private final String sql;
+ private final boolean rawSql;
+ private final boolean rowNumberIncluded;
+ private final String logWhereSql;
+
+ public RawSqlQueryPlanKey(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
+ this.sql = sql;
+ this.rawSql = rawSql;
+ this.rowNumberIncluded = rowNumberIncluded;
+ this.logWhereSql = logWhereSql;
+ }
+
+ public String toString() {
+ return getPartialKey() + ":r";
+ }
+
+ /**
+ * Return as a partial key. For rawSql hash the sql is part of the key and as such
+ * needs to be included in order to have a complete key. Typically the MD5 of the sql
+ * can be used as a short form proxy for the actual sql.
+ */
+ public String getPartialKey() {
+ return hashCode() + "_0";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RawSqlQueryPlanKey that = (RawSqlQueryPlanKey) o;
+
+ if (rawSql != that.rawSql) return false;
+ if (rowNumberIncluded != that.rowNumberIncluded) return false;
+ if (!sql.equals(that.sql)) return false;
+ return logWhereSql != null ? logWhereSql.equals(that.logWhereSql) : that.logWhereSql == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = sql.hashCode();
+ result = 31 * result + (rawSql ? 1 : 0);
+ result = 31 * result + (rowNumberIncluded ? 1 : 0);
+ result = 31 * result + logWhereSql.hashCode();
+ return result;
+ }
+}
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 7f3dea20d..0c07ea385 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -13,9 +13,8 @@ import com.avaje.ebean.plugin.SpiBeanType;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.HashQuery;
-import com.avaje.ebeaninternal.api.HashQueryPlan;
-import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
+import com.avaje.ebeaninternal.api.CQueryPlanKey;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
@@ -28,7 +27,7 @@ import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.expression.SimpleExpression;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
-import com.avaje.ebeaninternal.util.DefaultExpressionList;
+import com.avaje.ebeaninternal.server.expression.DefaultExpressionList;
import javax.persistence.PersistenceException;
import java.sql.Timestamp;
@@ -234,7 +233,7 @@ public class DefaultOrmQuery
- * This can used to enable the caching and reuse of a 'query plan'.
- *
- * This is calculated AFTER AutoTune query tuning has occurred.
- *
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
index c9def4a75..02f48ae67 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
@@ -294,6 +294,11 @@ public interface SpiQuery