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() { /*