From 84b37fb0140c0dc1c9046f843298027bd3400bf9 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 12 Jul 2016 21:56:43 +1200 Subject: [PATCH] EQL Named parameter support for Like and ieq --- .../com/avaje/ebean/ExpressionFactory.java | 9 +++-- .../expression/AbstractValueExpression.java | 32 +++++++++++++++++ .../CaseInsensitiveEqualExpression.java | 22 +++++++----- .../expression/DefaultExpressionFactory.java | 12 +++++-- .../server/expression/LikeExpression.java | 30 ++++------------ .../server/expression/NamedParamHelp.java | 28 +++++++++++++++ .../server/expression/SimpleExpression.java | 18 ++-------- .../server/grammer/EqlAdapter.java | 8 +++-- .../server/grammer/EqlAdapterHelper.java | 34 ++++++------------- .../server/grammer/EqlParserTest.java | 18 +++++----- 10 files changed, 123 insertions(+), 88 deletions(-) create mode 100644 src/main/java/com/avaje/ebeaninternal/server/expression/AbstractValueExpression.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/expression/NamedParamHelp.java diff --git a/src/main/java/com/avaje/ebean/ExpressionFactory.java b/src/main/java/com/avaje/ebean/ExpressionFactory.java index a21c2ee20..2fb89e9b2 100644 --- a/src/main/java/com/avaje/ebean/ExpressionFactory.java +++ b/src/main/java/com/avaje/ebean/ExpressionFactory.java @@ -135,6 +135,11 @@ public interface ExpressionFactory { */ Expression ieq(String propertyName, String value); + /** + * Case Insensitive Equal To that allows for named parameter use. + */ + Expression ieqObject(String propertyName, Object value); + /** * Between - property between the two given values. */ @@ -193,9 +198,9 @@ public interface ExpressionFactory { ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType); /** - * Create the query by Example expression specifying more options. + * Like with support for named parameters. */ - Expression like(LikeType likeType, String propertyName, Object value); + Expression like(String propertyName, Object value, boolean caseInsensitive, LikeType likeType); /** * Like - property like value where the value contains the SQL wild card diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractValueExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractValueExpression.java new file mode 100644 index 000000000..fb6756d7e --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractValueExpression.java @@ -0,0 +1,32 @@ +package com.avaje.ebeaninternal.server.expression; + +/** + * Abstract expression that helps with named parameter use. + */ +public abstract class AbstractValueExpression extends AbstractExpression { + + protected final Object bindValue; + + /** + * Construct with property name and potential named parameter. + */ + protected AbstractValueExpression(String propName, Object bindValue) { + super(propName); + this.bindValue = bindValue; + } + + /** + * Return the bind value taking into account named parameters. + */ + protected Object value() { + return NamedParamHelp.value(bindValue); + } + + /** + * Return the String bind value taking into account named parameters. + */ + protected String strValue() { + return NamedParamHelp.valueAsString(bindValue); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index b10c2c57a..cad89dd58 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -7,18 +7,22 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue; import java.io.IOException; -class CaseInsensitiveEqualExpression extends AbstractExpression { +class CaseInsensitiveEqualExpression extends AbstractValueExpression { - private final String value; + CaseInsensitiveEqualExpression(String propertyName, Object value) { + super(propertyName, value); + } - CaseInsensitiveEqualExpression(String propertyName, String value) { - super(propertyName); - this.value = value.toLowerCase(); + /** + * Return the bind value taking into account named parameters. + */ + private String val() { + return strValue().toLowerCase(); } @Override public void writeDocQuery(DocQueryContext context) throws IOException { - context.writeIEqualTo(propName, value); + context.writeIEqualTo(propName, val()); } @Override @@ -31,7 +35,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression { request.addBindEncryptKey(encryptKey); } - request.addBindValue(value); + request.addBindValue(val()); } @Override @@ -54,7 +58,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression { @Override public int queryBindHash() { - return value.hashCode(); + return val().hashCode(); } @Override @@ -70,6 +74,6 @@ class CaseInsensitiveEqualExpression extends AbstractExpression { @Override public boolean isSameByBind(SpiExpression other) { CaseInsensitiveEqualExpression that = (CaseInsensitiveEqualExpression) other; - return value.equals(that.value); + return val().equals(that.val()); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionFactory.java b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionFactory.java index e1cd4c1fc..5115a8ae3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionFactory.java @@ -157,6 +157,13 @@ public class DefaultExpressionFactory implements SpiExpressionFactory { return new CaseInsensitiveEqualExpression(propertyName, value); } + /** + * Create for named parameter use (and without support for equalsWithNullAsNoop). + */ + public Expression ieqObject(String propertyName, Object value) { + return new CaseInsensitiveEqualExpression(propertyName, value); + } + /** * Between - property between the two given values. */ @@ -252,9 +259,8 @@ public class DefaultExpressionFactory implements SpiExpressionFactory { } @Override - public Expression like(LikeType likeType, String propertyName, Object value) { - - return new LikeExpression(propertyName, value, true, LikeType.RAW); + public Expression like(String propertyName, Object value, boolean caseInsensitive, LikeType likeType) { + return new LikeExpression(propertyName, value, caseInsensitive, likeType); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java index 5d14a5d67..7e4b330e3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java @@ -4,43 +4,25 @@ import com.avaje.ebean.LikeType; import com.avaje.ebeaninternal.api.HashQueryPlanBuilder; import com.avaje.ebeaninternal.api.SpiExpression; import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.api.SpiNamedParam; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import java.io.IOException; -class LikeExpression extends AbstractExpression { - - private final Object value; +class LikeExpression extends AbstractValueExpression { private final boolean caseInsensitive; private final LikeType type; LikeExpression(String propertyName, Object value, boolean caseInsensitive, LikeType type) { - super(propertyName); + super(propertyName, value); this.caseInsensitive = caseInsensitive; this.type = type; - this.value = value; - } - - /** - * Return the bind value taking into account named parameters. - */ - private Object value() { - if (value instanceof SpiNamedParam) { - return ((SpiNamedParam)value).getValue(); - } - return value; - } - - private String val() { - return value().toString(); } @Override public void writeDocQuery(DocQueryContext context) throws IOException { - context.writeLike(propName, val(), type, caseInsensitive); + context.writeLike(propName, strValue(), type, caseInsensitive); } @Override @@ -53,7 +35,7 @@ class LikeExpression extends AbstractExpression { request.addBindEncryptKey(encryptKey); } - String bindValue = getValue(val(), caseInsensitive, type); + String bindValue = getValue(strValue(), caseInsensitive, type); request.addBindValue(bindValue); } @@ -89,7 +71,7 @@ class LikeExpression extends AbstractExpression { @Override public int queryBindHash() { - return val().hashCode(); + return strValue().hashCode(); } @Override @@ -107,7 +89,7 @@ class LikeExpression extends AbstractExpression { @Override public boolean isSameByBind(SpiExpression other) { LikeExpression that = (LikeExpression) other; - return val().equals(that.val()); + return strValue().equals(that.strValue()); } private static String getValue(String value, boolean caseInsensitive, LikeType type) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/NamedParamHelp.java b/src/main/java/com/avaje/ebeaninternal/server/expression/NamedParamHelp.java new file mode 100644 index 000000000..97df6e828 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/NamedParamHelp.java @@ -0,0 +1,28 @@ +package com.avaje.ebeaninternal.server.expression; + +import com.avaje.ebeaninternal.api.SpiNamedParam; + +/** + * Helper for evaluating named parameters. + */ +class NamedParamHelp { + + /** + * Return the bind value taking into account named parameters. + */ + static Object value(Object val) { + if (val instanceof SpiNamedParam) { + return ((SpiNamedParam) val).getValue(); + } + return val; + } + + /** + * Return the value as a string. + */ + static String valueAsString(Object val) { + Object value = value(val); + return (value == null) ? null : value.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java index bc4804eba..c50681faa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java @@ -5,31 +5,17 @@ import com.avaje.ebean.plugin.ExpressionPath; import com.avaje.ebeaninternal.api.HashQueryPlanBuilder; import com.avaje.ebeaninternal.api.SpiExpression; import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.api.SpiNamedParam; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import java.io.IOException; -public class SimpleExpression extends AbstractExpression { +public class SimpleExpression extends AbstractValueExpression { private final Op type; - private final Object val; - public SimpleExpression(String propertyName, Op type, Object value) { - super(propertyName); + super(propertyName, value); this.type = type; - this.val = value; - } - - /** - * Return the bind value taking into account named parameters. - */ - private Object value() { - if (val instanceof SpiNamedParam) { - return ((SpiNamedParam)val).getValue(); - } - return val; } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapter.java index 7cd369a72..706cbabb3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapter.java @@ -202,7 +202,11 @@ class EqlAdapter extends EQLBaseListener { return query.createNamedParameter(parameterName); } - public Expression like(LikeType likeType, String property, Object bindValue) { - return query.getExpressionFactory().like(likeType, property, bindValue); + public Expression like(boolean caseInsensitive, LikeType likeType, String property, Object bindValue) { + return query.getExpressionFactory().like(property, bindValue, caseInsensitive, likeType); + } + + public Expression ieq(String property, Object bindValue) { + return query.getExpressionFactory().ieqObject(property, bindValue); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapterHelper.java b/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapterHelper.java index d3585d973..203233b3f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapterHelper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/grammer/EqlAdapterHelper.java @@ -48,7 +48,7 @@ class EqlAdapterHelper { peekExprList().eq(path, bind(value)); break; case IEQ: - peekExprList().ieq(path, bindString(value)); + peekExprList().add(owner.ieq(path, bind(value))); break; case NE: peekExprList().ne(path, bind(value)); @@ -66,28 +66,28 @@ class EqlAdapterHelper { peekExprList().le(path, bind(value)); break; case LIKE: - addLike(LikeType.RAW, path, bind(value)); + addLike(false, LikeType.RAW, path, bind(value)); break; case CONTAINS: - addLike(LikeType.CONTAINS, path, bind(value)); + addLike(false, LikeType.CONTAINS, path, bind(value)); break; case STARTS_WITH: - addLike(LikeType.STARTS_WITH, path, bind(value)); + addLike(false, LikeType.STARTS_WITH, path, bind(value)); break; case ENDS_WITH: - addLike(LikeType.ENDS_WITH, path, bind(value)); + addLike(false, LikeType.ENDS_WITH, path, bind(value)); break; case ILIKE: - peekExprList().ilike(path, bindString(value)); + addLike(true, LikeType.RAW, path, bind(value)); break; case ICONTAINS: - peekExprList().icontains(path, bindString(value)); + addLike(true, LikeType.CONTAINS, path, bind(value)); break; case ISTARTS_WITH: - peekExprList().istartsWith(path, bindString(value)); + addLike(true, LikeType.STARTS_WITH, path, bind(value)); break; case IENDS_WITH: - peekExprList().iendsWith(path, bindString(value)); + addLike(true, LikeType.ENDS_WITH, path, bind(value)); break; default: throw new IllegalStateException("Unhandled operator " + op); @@ -95,20 +95,8 @@ class EqlAdapterHelper { } - private void addLike(LikeType likeType, String path, Object bindValue) { - peekExprList().add(owner.like(likeType, path, bindValue)); - } - - private String bindString(String value) { - ValueType valueType = getValueType(value); - switch (valueType) { - case NAMED_PARAM: - return NamedParameter.PREFIX + value; - case STRING: - return unquote(value); - default: - throw new IllegalArgumentException("Only STRING or NAMED PARAMETER argument allowed but got " + valueType); - } + private void addLike(boolean caseInsensitive, LikeType likeType, String path, Object bindValue) { + peekExprList().add(owner.like(caseInsensitive, likeType, path, bindValue)); } private Object bind(String value) { diff --git a/src/test/java/com/avaje/ebeaninternal/server/grammer/EqlParserTest.java b/src/test/java/com/avaje/ebeaninternal/server/grammer/EqlParserTest.java index 92a72c97b..11e9c7d8d 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/grammer/EqlParserTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/grammer/EqlParserTest.java @@ -47,15 +47,15 @@ public class EqlParserTest { assertThat(query.getGeneratedSql()).contains("where t0.name = ?"); } -// @Test -// public void where_namedParam_startsWith() throws Exception { -// -// Query query = parse("where name startsWith :name"); -// query.setParameter("name", "Rob"); -// query.findList(); -// -// assertThat(query.getGeneratedSql()).contains("where t0.name like ?"); -// } + @Test + public void where_namedParam_startsWith() throws Exception { + + Query query = parse("where name startsWith :name"); + query.setParameter("name", "Rob"); + query.findList(); + + assertThat(query.getGeneratedSql()).contains("where t0.name like ?"); + } @Test public void where_or1() throws Exception {