Initial add of SpiNamedParam

This commit is contained in:
Robin Bygrave
2016-07-12 20:45:33 +12:00
parent 86faa947af
commit 1a1c432c2a
11 changed files with 153 additions and 36 deletions
@@ -192,6 +192,11 @@ public interface ExpressionFactory {
*/
ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType);
/**
* Create the query by Example expression specifying more options.
*/
Expression like(LikeType likeType, String propertyName, Object value);
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
@@ -0,0 +1,6 @@
package com.avaje.ebeaninternal.api;
public interface SpiNamedParam {
Object getValue();
}
@@ -296,6 +296,8 @@ public interface SpiQuery<T> extends Query<T> {
*/
void setLoadDescription(String loadMode, String loadDescription);
SpiNamedParam createNamedParameter(String parameterName);
/**
* Return the joins required to support predicates on the many properties.
*/
@@ -251,6 +251,12 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new DefaultExampleExpression(checkEntityBean(example), caseInsensitive, likeType);
}
@Override
public Expression like(LikeType likeType, String propertyName, Object value) {
return new LikeExpression(propertyName, value, true, LikeType.RAW);
}
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
@@ -4,28 +4,43 @@ 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 String val;
private final Object value;
private final boolean caseInsensitive;
private final LikeType type;
LikeExpression(String propertyName, String value, boolean caseInsensitive, LikeType type) {
LikeExpression(String propertyName, Object value, boolean caseInsensitive, LikeType type) {
super(propertyName);
this.caseInsensitive = caseInsensitive;
this.type = type;
this.val = value;
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, val(), type, caseInsensitive);
}
@Override
@@ -38,7 +53,7 @@ class LikeExpression extends AbstractExpression {
request.addBindEncryptKey(encryptKey);
}
String bindValue = getValue(val, caseInsensitive, type);
String bindValue = getValue(val(), caseInsensitive, type);
request.addBindValue(bindValue);
}
@@ -74,7 +89,7 @@ class LikeExpression extends AbstractExpression {
@Override
public int queryBindHash() {
return val.hashCode();
return val().hashCode();
}
@Override
@@ -92,7 +107,7 @@ class LikeExpression extends AbstractExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
LikeExpression that = (LikeExpression) other;
return val.equals(that.val);
return val().equals(that.val());
}
private static String getValue(String value, boolean caseInsensitive, LikeType type) {
@@ -5,6 +5,7 @@ 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;
@@ -13,18 +14,28 @@ public class SimpleExpression extends AbstractExpression {
private final Op type;
private final Object value;
private final Object val;
public SimpleExpression(String propertyName, Op type, Object value) {
super(propertyName);
this.type = type;
this.value = value;
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
public Object getIdEqualTo(String idName) {
if (type == Op.EQ && idName.equals(propName)) {
return value;
return value();
}
return null;
}
@@ -37,13 +48,13 @@ public class SimpleExpression extends AbstractExpression {
ExpressionPath prop = context.getExpressionPath(propName);
if (prop != null && prop.isAssocId()) {
String idName = prop.getAssocIdExpression(propName, "");
Object[] ids = prop.getAssocIdValues((EntityBean) value);
Object[] ids = prop.getAssocIdValues((EntityBean) value());
if (ids == null || ids.length != 1) {
throw new IllegalArgumentException("Expecting 1 Id value for " + idName + " but got " + ids);
}
context.writeSimple(type, idName, ids[0]);
} else {
context.writeSimple(type, propName, value);
context.writeSimple(type, propName, value());
}
}
@@ -56,7 +67,7 @@ public class SimpleExpression extends AbstractExpression {
}
public Object getValue() {
return value;
return value();
}
@Override
@@ -65,7 +76,7 @@ public class SimpleExpression extends AbstractExpression {
ElPropertyValue prop = getElProp(request);
if (prop != null) {
if (prop.isAssocId()) {
Object[] ids = prop.getAssocIdValues((EntityBean) value);
Object[] ids = prop.getAssocIdValues((EntityBean) value());
if (ids != null) {
for (int i = 0; i < ids.length; i++) {
request.addBindValue(ids[i]);
@@ -83,7 +94,7 @@ public class SimpleExpression extends AbstractExpression {
// prop.getBeanProperty().getScalarType();
}
request.addBindValue(value);
request.addBindValue(value());
}
@Override
@@ -115,7 +126,7 @@ public class SimpleExpression extends AbstractExpression {
@Override
public int queryBindHash() {
return value.hashCode();
return value().hashCode();
}
@Override
@@ -132,6 +143,6 @@ public class SimpleExpression extends AbstractExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
SimpleExpression that = (SimpleExpression) other;
return value.equals(that.value);
return value().equals(that.value());
}
}
@@ -1,7 +1,10 @@
package com.avaje.ebeaninternal.server.grammer;
import com.avaje.ebean.Expression;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.LikeType;
import com.avaje.ebean.Query;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.grammer.antlr.EQLBaseListener;
import com.avaje.ebeaninternal.server.grammer.antlr.EQLLexer;
import com.avaje.ebeaninternal.server.grammer.antlr.EQLParser;
@@ -14,7 +17,7 @@ class EqlAdapter<T> extends EQLBaseListener {
private static final OperatorMapping operatorMapping = new OperatorMapping();
private final Query<T> query;
private final SpiQuery<T> query;
private final EqlAdapterHelper helper;
@@ -25,7 +28,7 @@ class EqlAdapter<T> extends EQLBaseListener {
private boolean textMode;
public EqlAdapter(Query<T> query) {
this.query = query;
this.query = (SpiQuery<T>)query;
this.helper = new EqlAdapterHelper(this);
}
@@ -60,8 +63,7 @@ class EqlAdapter<T> extends EQLBaseListener {
/**
* Push the expression list onto the appropriate stack.
*/
private void pushExprList(ExpressionList<T> list, String type) {
System.out.println("Push " + type + ">> ");
private void pushExprList(ExpressionList<T> list) {
if (textMode) {
textStack.push(list);
} else {
@@ -72,8 +74,7 @@ class EqlAdapter<T> extends EQLBaseListener {
/**
* End a list of expressions added by 'OR'.
*/
private void popJunction(String type) {
System.out.println("Pop " + type + " >> ");
private void popJunction() {
if (textMode) {
textStack.pop();
} else {
@@ -145,7 +146,7 @@ class EqlAdapter<T> extends EQLBaseListener {
String operator = ctx.getChild(1).getText();
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + op);
throw new IllegalStateException("No operator found for " + operator);
}
// RHS is Path, Literal or Named input parameter
@@ -158,43 +159,50 @@ class EqlAdapter<T> extends EQLBaseListener {
public void enterConditional_term(EQLParser.Conditional_termContext ctx) {
int childCount = ctx.getChildCount();
if (childCount > 1) {
pushExprList(peekExprList().and(), "Conjunction");
pushExprList(peekExprList().and());
}
}
@Override
public void exitConditional_term(EQLParser.Conditional_termContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction("Conjunction");
popJunction();
}
}
@Override
public void enterConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().or(), "Disjunction");
pushExprList(peekExprList().or());
}
}
@Override
public void exitConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction("Disjunction");
popJunction();
}
}
@Override
public void enterConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().not(), "Not");
pushExprList(peekExprList().not());
}
}
@Override
public void exitConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction("Not");
popJunction();
}
}
public Object namedParam(String parameterName) {
return query.createNamedParameter(parameterName);
}
public Expression like(LikeType likeType, String property, Object bindValue) {
return query.getExpressionFactory().like(likeType, property, bindValue);
}
}
@@ -1,6 +1,7 @@
package com.avaje.ebeaninternal.server.grammer;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.LikeType;
import java.math.BigDecimal;
@@ -65,16 +66,16 @@ class EqlAdapterHelper {
peekExprList().le(path, bind(value));
break;
case LIKE:
peekExprList().like(path, bindString(value));
addLike(LikeType.RAW, path, bind(value));
break;
case CONTAINS:
peekExprList().contains(path, bindString(value));
addLike(LikeType.CONTAINS, path, bind(value));
break;
case STARTS_WITH:
peekExprList().startsWith(path, bindString(value));
addLike(LikeType.STARTS_WITH, path, bind(value));
break;
case ENDS_WITH:
peekExprList().endsWith(path, bindString(value));
addLike(LikeType.ENDS_WITH, path, bind(value));
break;
case ILIKE:
peekExprList().ilike(path, bindString(value));
@@ -94,6 +95,10 @@ 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) {
@@ -120,7 +125,7 @@ class EqlAdapterHelper {
case BOOL: return Boolean.parseBoolean(value);
case NUMBER: return new BigDecimal(value);
case STRING: return unquote(value);
case NAMED_PARAM: return new NamedParameter(value.substring(1));
case NAMED_PARAM: return owner.namedParam(value.substring(1));
default:
throw new IllegalArgumentException("Unhandled valueType "+valueType);
}
@@ -16,6 +16,7 @@ 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.SpiNamedParam;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuerySecondary;
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
@@ -28,6 +29,7 @@ import com.avaje.ebeaninternal.server.query.CancelableQuery;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -135,6 +137,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
*/
private Object id;
private Map<String,ONamedParam> namedParams;
/**
* Bind parameters when using the query language.
*/
@@ -1138,6 +1142,15 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
*/
@Override
public DefaultOrmQuery<T> setParameter(String name, Object value) {
if (namedParams != null) {
ONamedParam param = namedParams.get(name);
if (param != null) {
param.setValue(value);
return this;
}
}
if (bindParams == null) {
bindParams = new BindParams();
}
@@ -1375,6 +1388,20 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
this.generatedSql = generatedSql;
}
@Override
public SpiNamedParam createNamedParameter(String name) {
if (namedParams == null) {
namedParams = new HashMap<String, ONamedParam>();
}
ONamedParam param = namedParams.get(name);
if (param == null) {
param = new ONamedParam(name);
namedParams.put(name, param);
}
return param;
}
@Override
public void setDefaultFetchBuffer(int fetchSize) {
if (bufferFetchSizeHint == 0) {
@@ -0,0 +1,22 @@
package com.avaje.ebeaninternal.server.querydefn;
import com.avaje.ebeaninternal.api.SpiNamedParam;
public class ONamedParam implements SpiNamedParam {
private final String name;
private Object value;
public ONamedParam(String name) {
this.name = name;
}
public void setValue(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
}