diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java
index 49ca50f0a..ab08d30ab 100644
--- a/src/main/java/com/avaje/ebean/EbeanServer.java
+++ b/src/main/java/com/avaje/ebean/EbeanServer.java
@@ -1937,4 +1937,12 @@ public interface EbeanServer {
*/
List draftRestore(Query query);
+ /**
+ * Returns the set of properties/paths that are unknown (do not map to known properties or paths).
+ *
+ * Validate the query checking the where and orderBy expression paths to confirm if
+ * they represent valid properties/path for the given bean type.
+ *
+ */
+ Set validateQuery(Query query);
}
diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java
index fee79aafa..00367523e 100644
--- a/src/main/java/com/avaje/ebean/Query.java
+++ b/src/main/java/com/avaje/ebean/Query.java
@@ -1318,4 +1318,13 @@ public interface Query extends Serializable {
*
*/
Query setDisableLazyLoading(boolean disableLazyLoading);
+
+ /**
+ * Returns the set of properties or paths that are unknown (do not map to known properties or paths).
+ *
+ * Validate the query checking the where and orderBy expression paths to confirm if
+ * they represent valid properties or paths for the given bean type.
+ *
+ */
+ Set validate();
}
diff --git a/src/main/java/com/avaje/ebean/plugin/SpiBeanType.java b/src/main/java/com/avaje/ebean/plugin/SpiBeanType.java
index 11f68f06d..ac97d7237 100644
--- a/src/main/java/com/avaje/ebean/plugin/SpiBeanType.java
+++ b/src/main/java/com/avaje/ebean/plugin/SpiBeanType.java
@@ -17,8 +17,13 @@ public interface SpiBeanType {
Class getBeanType();
/**
- * Return the base table this bean type maps to.
+ * Return true if the property is a valid known property or path for the given bean type.
*/
+ boolean isValidExpression(String property);
+
+ /**
+ * Return the base table this bean type maps to.
+ */
String getBaseTable();
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java
index 152890772..28cdf61c5 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpression.java
@@ -66,4 +66,9 @@ public interface SpiExpression extends Expression {
* the associated request.
*/
void addBindValues(SpiExpressionRequest request);
+
+ /**
+ * Validate all the properties/paths associated with this expression.
+ */
+ void validate(SpiExpressionValidation validation);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
index 87121f0fa..1cacf0a45 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
@@ -67,4 +67,8 @@ public interface SpiExpressionList extends ExpressionList {
*/
void queryPlanHash(BeanQueryRequest> request, HashQueryPlanBuilder builder);
+ /**
+ * Validate all the properties/paths used in this expression list.
+ */
+ void validate(SpiExpressionValidation validation);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionValidation.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionValidation.java
new file mode 100644
index 000000000..fdc1e93cc
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionValidation.java
@@ -0,0 +1,37 @@
+package com.avaje.ebeaninternal.api;
+
+import com.avaje.ebean.plugin.SpiBeanType;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Property expression validation request for a given root bean type.
+ */
+public class SpiExpressionValidation {
+
+ private final SpiBeanType> desc;
+
+ private final LinkedHashSet unknown = new LinkedHashSet();
+
+ public SpiExpressionValidation(SpiBeanType> desc) {
+ this.desc = desc;
+ }
+
+ /**
+ * Validate that the property expression (path) is valid.
+ */
+ public void validate(String propertyName) {
+ if (!desc.isValidExpression(propertyName)) {
+ unknown.add(propertyName);
+ }
+ }
+
+ /**
+ * Return the set of properties considered as having unknown paths.
+ */
+ public Set getUnknownProperties() {
+ return unknown;
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
index 565c0a800..05b1a7136 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
@@ -11,6 +11,7 @@ import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebean.event.readaudit.ReadEvent;
+import com.avaje.ebean.plugin.SpiBeanType;
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -22,6 +23,7 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.sql.Timestamp;
import java.util.List;
+import java.util.Set;
/**
* Object Relational query - Internal extension to Query object.
@@ -694,4 +696,10 @@ public interface SpiQuery extends Query {
* Return root table alias set by {@link #alias(String)} command.
*/
String getAlias();
+
+ /**
+ * Validate the query returning the set of properties with unknown paths.
+ */
+ Set validate(SpiBeanType desc);
+
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index c6e25515c..d9ec8164c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -908,8 +908,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
Class beanType = (Class) list.get(0).getClass();
BeanDescriptor beanDescriptor = getBeanDescriptor(beanType);
if (beanDescriptor == null) {
- String m = "BeanDescriptor not found, is [" + beanType + "] an entity bean?";
- throw new PersistenceException(m);
+ throw new PersistenceException("BeanDescriptor not found, is [" + beanType + "] an entity bean?");
}
beanDescriptor.sort(list, sortByClause);
}
@@ -933,6 +932,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new DefaultOrmQuery(beanType, this, expressionFactory, deployQuery);
}
+ @Override
+ public Set validateQuery(Query query) {
+
+ BeanDescriptor beanDescriptor = getBeanDescriptor(query.getBeanType());
+ if (beanDescriptor == null) {
+ throw new PersistenceException("BeanDescriptor not found, is [" + query.getBeanType() + "] an entity bean?");
+ }
+ return ((SpiQuery)query).validate(beanDescriptor);
+ }
+
public Filter filter(Class beanType) {
BeanDescriptor desc = getBeanDescriptor(beanType);
if (desc == 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 0c8149a6a..3f0320b30 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -1602,6 +1602,15 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType {
return new ElComparatorProperty(elGetValue, sortProp.isAscending(), nullsHigh);
}
+ @Override
+ public boolean isValidExpression(String propertyName) {
+ try {
+ return (getElGetValue(propertyName) != null);
+ } catch (PersistenceException e) {
+ return false;
+ }
+ }
+
/**
* Get an Expression language Value object.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java
index 99fe203a6..fd4e8c926 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
@@ -42,6 +43,11 @@ public abstract class AbstractExpression implements SpiExpression {
}
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ validation.validate(getPropertyName());
+ }
+
protected ElPropertyValue getElProp(SpiExpressionRequest request) {
String propertyName = getPropertyName();
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 5098f94c9..8f4edfdca 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/AllEqualsExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/AllEqualsExpression.java
@@ -8,6 +8,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
@@ -36,6 +37,13 @@ class AllEqualsExpression implements SpiExpression {
}
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ for (String propName: propMap.keySet()) {
+ validation.validate(propName);
+ }
+ }
+
public void addBindValues(SpiExpressionRequest request) {
if (propMap.isEmpty()) {
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 5102cbcc9..c8f8193e1 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
@@ -44,6 +45,12 @@ class BetweenPropertyExpression implements SpiExpression {
}
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ validation.validate(lowProperty);
+ validation.validate(highProperty);
+ }
+
public void addBindValues(SpiExpressionRequest request) {
request.addBindValue(value);
}
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 b357e44da..47b2415d7 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExampleExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExampleExpression.java
@@ -10,6 +10,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -121,6 +122,13 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
return this;
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ for (int i = 0; i < list.size(); i++) {
+ list.get(i).validate(validation);
+ }
+ }
+
/**
* Adds bind values to the request.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/ExistsExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/ExistsExpression.java
index 026f78306..d0b65a6ed 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/ExistsExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/ExistsExpression.java
@@ -8,6 +8,7 @@ import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.query.CQuery;
@@ -85,4 +86,9 @@ public class ExistsExpression implements SpiExpression {
public void containsMany(BeanDescriptor> desc, ManyWhereJoins whereManyJoins) {
// Nothing to do for exists expression
}
+
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ // Nothing to do for exists expression
+ }
}
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 af62ed471..ce122251b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/IdExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/IdExpression.java
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
@@ -28,6 +29,11 @@ class IdExpression implements SpiExpression {
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ // always valid
+ }
+
public void addBindValues(SpiExpressionRequest request) {
// 'flatten' EmbeddedId and multiple Id cases
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 4d965cd7c..ee3fa0bad 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/IdInExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/IdInExpression.java
@@ -7,6 +7,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
@@ -27,6 +28,11 @@ public class IdInExpression implements SpiExpression {
public void containsMany(BeanDescriptor> desc, ManyWhereJoins manyWhereJoin) {
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ // always valid
+ }
+
public void addBindValues(SpiExpressionRequest request) {
// Bind the Id values including EmbeddedId and multiple Id
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 845b4aba2..c0ba4f9f2 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
@@ -13,6 +13,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.util.DefaultExpressionList;
@@ -81,6 +82,11 @@ abstract class JunctionExpression implements Junction, SpiExpression, Expr
}
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ exprList.validate(validation);
+ }
+
@Override
public Junction add(Expression item) {
SpiExpression i = (SpiExpression) item;
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 782af06ba..981e6b563 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/LogicExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/LogicExpression.java
@@ -6,6 +6,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
@@ -53,6 +54,12 @@ abstract class LogicExpression implements SpiExpression {
expTwo.containsMany(desc, manyWhereJoin);
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ expOne.validate(validation);
+ expTwo.validate(validation);
+ }
+
public void addBindValues(SpiExpressionRequest request) {
expOne.addBindValues(request);
expTwo.addBindValues(request);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/NoopExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/NoopExpression.java
index 789f470d1..549ecb60d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/NoopExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/NoopExpression.java
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
@@ -19,6 +20,11 @@ class NoopExpression implements SpiExpression {
// nothing to do
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ // always valid
+ }
+
@Override
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
builder.add(NoopExpression.class);
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 122ff6658..a34fc1bbe 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/NotExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/NotExpression.java
@@ -6,6 +6,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
final class NotExpression implements SpiExpression {
@@ -24,6 +25,11 @@ final class NotExpression implements SpiExpression {
exp.containsMany(desc, manyWhereJoin);
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ exp.validate(validation);
+ }
+
public void addBindValues(SpiExpressionRequest request) {
exp.addBindValues(request);
}
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 754e07586..e078b87c9 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/RawExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/RawExpression.java
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
class RawExpression implements SpiExpression {
@@ -24,6 +25,11 @@ class RawExpression implements SpiExpression {
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ // always ignored
+ }
+
public void addBindValues(SpiExpressionRequest request) {
if (values != null) {
for (int i = 0; i < values.length; i++) {
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 fee0b4223..7b2dbaec4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -9,6 +9,7 @@ import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebean.event.readaudit.ReadEvent;
+import com.avaje.ebean.plugin.SpiBeanType;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.HashQuery;
@@ -17,6 +18,7 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionList;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -1413,4 +1415,28 @@ public class DefaultOrmQuery implements SpiQuery {
}
}
+ @Override
+ public Set validate() {
+ return server.validateQuery(this);
+ }
+
+ /**
+ * Validate all the expression properties/paths given the bean descriptor.
+ */
+ public Set validate(SpiBeanType desc) {
+
+ SpiExpressionValidation validation = new SpiExpressionValidation(desc);
+ if (whereExpressions != null) {
+ whereExpressions.validate(validation);
+ }
+ if (havingExpressions != null) {
+ havingExpressions.validate(validation);
+ }
+ if (orderBy != null) {
+ for (Property property : orderBy.getProperties()) {
+ validation.validate(property.getProperty());
+ }
+ }
+ return validation.getUnknownProperties();
+ }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
index 9474f6001..f7cb6043f 100644
--- a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
@@ -15,6 +15,7 @@ import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
+import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
@@ -98,6 +99,13 @@ public class DefaultExpressionList implements SpiExpressionList {
}
}
+ @Override
+ public void validate(SpiExpressionValidation validation) {
+ for (int i = 0; i < list.size(); i++) {
+ list.get(i).validate(validation);
+ }
+ }
+
@Override
public ExpressionList endJunction() {
return parentExprList == null ? this : parentExprList;
diff --git a/src/test/java/com/avaje/ebean/plugin/SpiServerTest.java b/src/test/java/com/avaje/ebean/plugin/SpiServerTest.java
index 1f752217c..21fddfdb8 100644
--- a/src/test/java/com/avaje/ebean/plugin/SpiServerTest.java
+++ b/src/test/java/com/avaje/ebean/plugin/SpiServerTest.java
@@ -26,6 +26,13 @@ public class SpiServerTest {
assertNull(beanType.getPersistListener());
assertNull(beanType.getQueryAdapter());
+ assertTrue(beanType.isValidExpression("name"));
+ assertTrue(beanType.isValidExpression("contacts.firstName"));
+ assertTrue(beanType.isValidExpression("contacts.group.name"));
+ assertFalse(beanType.isValidExpression("junk"));
+ assertFalse(beanType.isValidExpression("Name"));
+ assertFalse(beanType.isValidExpression("contacts.name"));
+
Customer customer = new Customer();
customer.setId(42);
diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
index dd9750044..467d416b0 100644
--- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
+++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
@@ -292,6 +292,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return null;
}
+ @Override
+ public Set validateQuery(Query query) {
+ return null;
+ }
+
@Override
public Object nextId(Class> beanType) {
return null;
diff --git a/src/test/java/com/avaje/tests/query/orderby/TestOrderByWithDistinct.java b/src/test/java/com/avaje/tests/query/orderby/TestOrderByWithDistinct.java
index cb03fd16e..1a69c2eff 100644
--- a/src/test/java/com/avaje/tests/query/orderby/TestOrderByWithDistinct.java
+++ b/src/test/java/com/avaje/tests/query/orderby/TestOrderByWithDistinct.java
@@ -1,7 +1,10 @@
package com.avaje.tests.query.orderby;
import java.util.List;
+import java.util.Set;
+import com.avaje.tests.model.basic.Customer;
+import com.avaje.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
@@ -12,9 +15,29 @@ import com.avaje.tests.model.basic.MRole;
import com.avaje.tests.model.basic.MUser;
import com.avaje.tests.model.basic.MUserType;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class TestOrderByWithDistinct extends BaseTestCase {
-
+
+ @Test
+ public void testOrderByValidation() {
+
+ ResetBasicData.reset();
+
+ Query query = Ebean.find(Customer.class)
+ .where()
+ .eq("junk","blah")
+ .eq("name","jim")
+ .orderBy("id desc,path.that.does.not.exist,contacts.group.name asc");
+
+ Set unknownProperties = query.validate();
+ assertThat(unknownProperties).isNotEmpty();
+ assertThat(unknownProperties).hasSize(2);
+ assertThat(unknownProperties).contains("junk","path.that.does.not.exist");
+
+ }
+
@Test
public void test() {
/*