diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionRequest.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionRequest.java index 3cd185513..913b6ac73 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionRequest.java @@ -14,7 +14,7 @@ public interface SpiExpressionRequest { /** * Return the DB specific JSON expression handler. */ - JsonExpressionHandler getJsonHander(); + JsonExpressionHandler getJsonHandler(); /** * Parse the logical property name to the deployment name. @@ -35,7 +35,12 @@ public interface SpiExpressionRequest { * Append to the expression sql. */ SpiExpressionRequest append(String sql); - + + /** + * Add an encryption key to bind to this request. + */ + void addBindEncryptKey(Object encryptKey); + /** * Add a bind value to this request. */ 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 17963793d..3fd9f08e5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -22,7 +22,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression { if (prop != null && prop.isDbEncrypted()) { // bind the key as well as the value String encryptKey = prop.getBeanProperty().getEncryptKey().getStringValue(); - request.addBindValue(encryptKey); + request.addBindEncryptKey(encryptKey); } request.addBindValue(value); diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/JsonPathExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/JsonPathExpression.java index 03cff50af..5489c0f72 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/JsonPathExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/JsonPathExpression.java @@ -78,7 +78,7 @@ class JsonPathExpression extends AbstractExpression { public void addSql(SpiExpressionRequest request) { // Use DB specific expression handling (Postgres and Oracle supported) - request.getJsonHander().addSql(request, propName, path, operator, value); + request.getJsonHandler().addSql(request, propName, path, operator, value); } @Override 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 827d90af8..9f0d9f475 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/LikeExpression.java @@ -29,7 +29,7 @@ class LikeExpression extends AbstractExpression { if (prop != null && prop.isDbEncrypted()) { // bind the key as well as the value String encryptKey = prop.getBeanProperty().getEncryptKey().getStringValue(); - request.addBindValue(encryptKey); + request.addBindEncryptKey(encryptKey); } String bindValue = getValue(val, caseInsensitive, type); 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 9d2257305..8cde6e3ef 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/SimpleExpression.java @@ -40,7 +40,7 @@ public class SimpleExpression extends AbstractExpression { if (prop.isDbEncrypted()) { // bind the key as well as the value String encryptKey = prop.getBeanProperty().getEncryptKey().getStringValue(); - request.addBindValue(encryptKey); + request.addBindEncryptKey(encryptKey); } //else if (prop.isLocalEncrypted()) { // not supporting this for equals (but probably could) 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 236d3ebd2..e3570defb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java @@ -61,7 +61,7 @@ public class CQueryPredicates { /** * Bind values from the where expressions. */ - private ArrayList filterManyExprBindValues; + private DefaultExpressionRequest filterMany; /** * SQL generated from the where expressions. @@ -71,7 +71,7 @@ public class CQueryPredicates { /** * Bind values from the where expressions. */ - private ArrayList whereExprBindValues; + private DefaultExpressionRequest where; /** * SQL generated from the where expressions. @@ -86,7 +86,7 @@ public class CQueryPredicates { /** * Bind values for having expression. */ - private ArrayList havingExprBindValues; + private DefaultExpressionRequest having; /** * SQL generated from the having expression. @@ -129,16 +129,14 @@ public class CQueryPredicates { public String bind(DataBind dataBind) throws SQLException { - StringBuilder bindLog = new StringBuilder(); - if (query.isVersionsBetween() && binder.isBindAsOfWithFromClause()) { // sql2011 based versions between timestamp syntax Timestamp start = query.getVersionStart(); Timestamp end = query.getVersionEnd(); - bindLog.append("between ").append(start).append(" and ").append(end); + dataBind.append("between ").append(start).append(" and ").append(end); binder.bindObject(dataBind, start); binder.bindObject(dataBind, end); - bindLog.append(", "); + dataBind.append(", "); } List historyTableAlias = query.getAsOfTableAlias(); @@ -146,51 +144,37 @@ public class CQueryPredicates { // bind the asOf value for each table alias as part of the from/join clauses // there is one effective date predicate per table alias Timestamp asOf = query.getAsOf(); - bindLog.append("asOf ").append(asOf); + dataBind.append("asOf ").append(asOf); for (int i = 0; i < historyTableAlias.size() * binder.getAsOfBindCount(); i++) { binder.bindObject(dataBind, asOf); } - bindLog.append(", "); + dataBind.append(", "); } if (idValue != null) { // this is a find by id type query... request.getBeanDescriptor().bindId(dataBind, idValue); - bindLog.append(idValue); + dataBind.append(idValue); } if (bindParams != null) { // bind named and positioned parameters... - binder.bind(bindParams, dataBind, bindLog); + binder.bind(bindParams, dataBind, dataBind.log()); } - if (whereExprBindValues != null) { - for (int i = 0; i < whereExprBindValues.size(); i++) { - Object bindValue = whereExprBindValues.get(i); - bindValue = binder.bindObject(dataBind, bindValue); - if (i > 0 || idValue != null) { - bindLog.append(","); - } - bindLog.append(bindValue); - } + if (where != null) { + where.bind(dataBind); } - if (filterManyExprBindValues != null) { - for (int i = 0; i < filterManyExprBindValues.size(); i++) { - Object bindValue = filterManyExprBindValues.get(i); - bindValue = binder.bindObject(dataBind, bindValue); - if (i > 0 || idValue != null) { - bindLog.append(","); - } - bindLog.append(bindValue); - } + if (filterMany != null) { + filterMany.bind(dataBind); } if (historyTableAlias != null && !binder.isBindAsOfWithFromClause()) { // bind the asOf value for each table alias after all the normal predicates // there is one effective date predicate per table alias Timestamp asOf = query.getAsOf(); - bindLog.append(" asOf ").append(asOf); + dataBind.append(" asOf ").append(asOf); for (int i = 0; i < historyTableAlias.size() * binder.getAsOfBindCount(); i++) { binder.bindObject(dataBind, asOf); } @@ -198,24 +182,14 @@ public class CQueryPredicates { if (havingNamedParams != null) { // bind named parameters in having... - bindLog.append(" havingNamed "); - binder.bind(havingNamedParams.list(), dataBind, bindLog); + binder.bind(havingNamedParams.list(), dataBind, dataBind.log()); } - if (havingExprBindValues != null) { - // bind having expression... - bindLog.append(" having "); - for (int i = 0; i < havingExprBindValues.size(); i++) { - Object bindValue = havingExprBindValues.get(i); - bindValue = binder.bindObject(dataBind, bindValue); - if (i > 0) { - bindLog.append(","); - } - bindLog.append(bindValue); - } + if (having != null) { + having.bind(dataBind); } - return bindLog.toString(); + return dataBind.log().toString(); } private void buildBindHavingRawSql(boolean buildSql, boolean parseRaw, DeployParser deployParser) { @@ -302,22 +276,20 @@ public class CQueryPredicates { SpiExpressionList whereExp = query.getWhereExpressions(); if (whereExp != null) { - DefaultExpressionRequest whereReq = new DefaultExpressionRequest(request, deployParser, binder); - whereExprBindValues = whereExp.buildBindValues(whereReq); + this.where = new DefaultExpressionRequest(request, deployParser, binder, whereExp); if (buildSql) { - whereExprSql = whereExp.buildSql(whereReq); + whereExprSql = where.buildSql(); } } BeanPropertyAssocMany manyProperty = request.getManyProperty(); if (manyProperty != null) { OrmQueryProperties chunk = query.getDetail().getChunk(manyProperty.getName(), false); - SpiExpressionList filterMany = chunk.getFilterMany(); - if (filterMany != null) { - DefaultExpressionRequest filterReq = new DefaultExpressionRequest(request, deployParser, binder); - filterManyExprBindValues = filterMany.buildBindValues(filterReq); + SpiExpressionList filterManyExpr = chunk.getFilterMany(); + if (filterManyExpr != null) { + this.filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr); if (buildSql) { - filterManyExprSql = filterMany.buildSql(filterReq); + filterManyExprSql = filterMany.buildSql(); } } } @@ -325,10 +297,9 @@ public class CQueryPredicates { // having expression SpiExpressionList havingExpr = query.getHavingExpressions(); if (havingExpr != null) { - DefaultExpressionRequest havingReq = new DefaultExpressionRequest(request, deployParser, binder); - havingExprBindValues = havingExpr.buildBindValues(havingReq); + this.having = new DefaultExpressionRequest(request, deployParser, binder, havingExpr); if (buildSql) { - havingExprSql = havingExpr.buildSql(havingReq); + havingExprSql = having.buildSql(); } } @@ -495,7 +466,7 @@ public class CQueryPredicates { * Return the bind values for the where expression. */ public ArrayList getWhereExprBindValues() { - return whereExprBindValues; + return where.getBindValues(); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java b/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java index b39807735..021379201 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java @@ -14,12 +14,31 @@ public class DataBind { private final PreparedStatement pstmt; + private final StringBuilder bindLog = new StringBuilder(); + private int pos; public DataBind(PreparedStatement pstmt) { this.pstmt = pstmt; } + /** + * Append an entry to the bind log. + */ + public StringBuilder append(Object entry) { + return bindLog.append(entry); + } + + /** + * Return the bind log. + */ + public StringBuilder log() { + return bindLog; + } + + /** + * Close the underlying prepared statement. + */ public void close() throws SQLException { pstmt.close(); } diff --git a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionRequest.java b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionRequest.java index 6a62aca31..510d62ba0 100644 --- a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionRequest.java @@ -1,13 +1,16 @@ package com.avaje.ebeaninternal.util; +import java.sql.SQLException; import java.util.ArrayList; +import com.avaje.ebeaninternal.api.SpiExpressionList; import com.avaje.ebeaninternal.api.SpiExpressionRequest; import com.avaje.ebeaninternal.server.core.JsonExpressionHandler; import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.DeployParser; import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.type.DataBind; public class DefaultExpressionRequest implements SpiExpressionRequest { @@ -15,7 +18,7 @@ public class DefaultExpressionRequest implements SpiExpressionRequest { private final BeanDescriptor beanDescriptor; - private final StringBuilder sb = new StringBuilder(); + private final StringBuilder sql = new StringBuilder(); private final ArrayList bindValues = new ArrayList(); @@ -23,13 +26,20 @@ public class DefaultExpressionRequest implements SpiExpressionRequest { private final Binder binder; + private final SpiExpressionList expressionList; + private int paramIndex; - public DefaultExpressionRequest(SpiOrmQueryRequest queryRequest, DeployParser deployParser, Binder binder) { + private StringBuilder bindLog; + + public DefaultExpressionRequest(SpiOrmQueryRequest queryRequest, DeployParser deployParser, Binder binder, SpiExpressionList expressionList) { this.queryRequest = queryRequest; this.beanDescriptor = queryRequest.getBeanDescriptor(); this.deployParser = deployParser; this.binder = binder; + this.expressionList = expressionList; + // immediately build the list of bind values (callback style) + expressionList.buildBindValues(this); } public DefaultExpressionRequest(BeanDescriptor beanDescriptor) { @@ -37,9 +47,30 @@ public class DefaultExpressionRequest implements SpiExpressionRequest { this.queryRequest = null; this.deployParser = null; this.binder = null; + this.expressionList = null; } - public JsonExpressionHandler getJsonHander() { + /** + * Build sql for the underlying expression list. + */ + public String buildSql() { + return expressionList.buildSql(this); + } + + /** + * Bind the values from the underlying expression list. + */ + public void bind(DataBind dataBind) throws SQLException { + for (int i = 0; i < bindValues.size(); i++) { + Object bindValue = bindValues.get(i); + binder.bindObject(dataBind, bindValue); + } + if (bindLog != null) { + dataBind.append(bindLog.toString()); + } + } + + public JsonExpressionHandler getJsonHandler() { return binder.getJsonExpressionHandler(); } @@ -54,9 +85,9 @@ public class DefaultExpressionRequest implements SpiExpressionRequest { */ @Override public void appendLike() { - sb.append(" "); - sb.append(queryRequest.getDBLikeClause()); - sb.append(" "); + sql.append(" "); + sql.append(queryRequest.getDBLikeClause()); + sql.append(" "); } /** @@ -74,17 +105,39 @@ public class DefaultExpressionRequest implements SpiExpressionRequest { return queryRequest; } - public SpiExpressionRequest append(String sql) { - sb.append(sql); + /** + * Append text the underlying sql expression. + */ + public SpiExpressionRequest append(String sqlExpression) { + sql.append(sqlExpression); return this; } + public void addBindEncryptKey(Object bindValue) { + bindValues.add(bindValue); + bindLog("****"); + } + public void addBindValue(Object bindValue) { bindValues.add(bindValue); + bindLog(bindValue); + } + + private void bindLog(Object val) { + if (bindLog == null) { + bindLog = new StringBuilder(); + } else { + bindLog.append(","); + } + bindLog.append(val); + } + + public String getBindLog() { + return bindLog == null ? "" : bindLog.toString(); } public String getSql() { - return sb.toString(); + return sql.toString(); } public ArrayList getBindValues() { diff --git a/src/test/java/com/avaje/tests/basic/encrypt/TestEncrypt.java b/src/test/java/com/avaje/tests/basic/encrypt/TestEncrypt.java index d74652fb9..738a19065 100644 --- a/src/test/java/com/avaje/tests/basic/encrypt/TestEncrypt.java +++ b/src/test/java/com/avaje/tests/basic/encrypt/TestEncrypt.java @@ -3,6 +3,7 @@ package com.avaje.tests.basic.encrypt; import java.sql.Date; import java.util.List; +import org.avaje.ebeantest.LoggedSqlCollector; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; @@ -16,8 +17,24 @@ import com.avaje.ebean.config.dbplatform.DbEncrypt; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.tests.model.basic.EBasicEncrypt; +import static org.assertj.core.api.Assertions.assertThat; + public class TestEncrypt extends BaseTestCase { + + @Test + public void testQueryBind() { + + LoggedSqlCollector.start(); + Ebean.find(EBasicEncrypt.class) + .where().startsWith("description", "Rob") + .findList(); + + List loggedSql = LoggedSqlCollector.stop(); + assertThat(loggedSql).hasSize(1); + assertThat(loggedSql.get(0)).contains("; --bind(****,Rob%)"); + } + @Ignore @Test public void test() {