From 7e265f7bde97120cf66e59e43abf044e0b047764 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 13 Jul 2021 09:09:30 +0200 Subject: [PATCH 01/22] FIX: SubQuery with fetch-path does not work correctly --- .../server/query/SqlTreeBuilder.java | 2 +- .../server/querydefn/DefaultOrmQuery.java | 4 +- .../java/org/tests/query/TestSubQuery.java | 71 +++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 239e8a58e..e4c936e45 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -311,7 +311,7 @@ public final class SqlTreeBuilder { } else { // do not read Id on child beans (e.g. when used with fetch()) - boolean withId = isNotSingleAttribute(); + boolean withId = isNotSingleAttribute() && !subQuery; return new SqlTreeNodeBean(prefix, prop, props, myList, withId, temporalMode, disableLazyLoad); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 96a923637..3a81183e9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -616,7 +616,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { * Limit the number of fetch joins to Many properties, mark as query joins as needed. */ private void markQueryJoins() { - detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type != Type.ATTRIBUTE); + detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type != Type.ATTRIBUTE && type != Type.SUBQUERY); } private boolean isAllowOneManyFetch() { @@ -629,7 +629,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { @Override public void setDefaultSelectClause() { - if (type != Type.ATTRIBUTE) { + if (type != Type.ATTRIBUTE && type != Type.SUBQUERY) { detail.setDefaultSelectClause(beanDescriptor); } else if (!detail.hasSelectClause()) { // explicit empty select when single attribute query on non-root fetch path diff --git a/ebean-core/src/test/java/org/tests/query/TestSubQuery.java b/ebean-core/src/test/java/org/tests/query/TestSubQuery.java index db9eb7cbd..61ffacf56 100644 --- a/ebean-core/src/test/java/org/tests/query/TestSubQuery.java +++ b/ebean-core/src/test/java/org/tests/query/TestSubQuery.java @@ -6,6 +6,8 @@ import io.ebean.Query; import org.junit.Test; import org.tests.model.basic.CKeyParent; import org.tests.model.basic.Order; +import org.tests.model.basic.OrderDetail; +import org.tests.model.basic.OrderShipment; import org.tests.model.basic.ResetBasicData; import org.tests.model.basic.Vehicle; import org.tests.model.basic.VehicleDriver; @@ -47,6 +49,75 @@ public class TestSubQuery extends BaseTestCase { DB.find(Order.class).where().isIn("id", sq).findList(); } + /** + * Testcase, that discovered, that DefaultOrmQuery.setDefaultSelectClause is set on subQueries with fetch path. + * Also checks, that SqlTreeBuilder does not read id on Many2One props. + */ + @Test + public void test_IsInWithFetchSubQuery1() { + + List productIds = new ArrayList<>(); + productIds.add(3); + + Query sq = DB.createQuery(OrderDetail.class).fetch("order", "id").where() + .isIn("product.id", productIds).query(); + + // execute the subQuery as copy (generatedSQL must be part of original query) + Query debugSq = sq.copy(); + debugSq.findSingleAttribute(); + assertThat(debugSq.getGeneratedSql()).isEqualTo( + "select t1.id from o_order_detail t0 join o_order t1 on t1.id = t0.order_id where t0.product_id in (?)"); + + Query query = DB.find(Order.class).select("shipDate").where().isIn("id", sq).query(); + query.findSingleAttribute(); + + assertThat(query.getGeneratedSql()) + .isEqualTo("select t0.ship_date from o_order t0 where (t0.id) in (" + debugSq.getGeneratedSql() + ")"); + } + + /** + * Test checks, that DefaultOrmQuery.markQueryJoins handles subQuery correct. + */ + @Test + public void test_IsInWithFetchSubQuery2() { + + Query sq = DB.createQuery(OrderDetail.class).fetch("order.customer", "anniversary").where() + .eq("order.customer.name", "Roland") + .query().setDistinct(true); + + // execute the subQuery as copy (generatedSQL must be part of original query) + Query debugSq = sq.copy(); + debugSq.findSingleAttribute(); + + Query query = DB.find(Order.class).select("status").where().isIn("shipDate", sq).query(); + query.findSingleAttribute(); + assertThat(query.getGeneratedSql()) + .isEqualTo("select t0.status from o_order t0 where (t0.ship_date) in (" + debugSq.getGeneratedSql() + ")"); + } + + /** + * Checks, that SqlTreeBuilder does not read id on One2Many props. + */ + @Test + public void test_IsInWithFetchSubQuery3() { + + List productIds = new ArrayList<>(); + productIds.add(3); + + Query sq = DB.createQuery(OrderDetail.class).fetch("order.shipments", "id").where() + .isIn("product.id", productIds).query(); + + // execute the subQuery as copy (generatedSQL must be part of original query) + Query debugSq = sq.copy(); + debugSq.findSingleAttribute(); + + Query query = DB.find(OrderShipment.class).select("shipTime").where().isIn("id", sq).query(); + query.findSingleAttribute(); + + assertThat(query.getGeneratedSql()) + .isEqualTo("select t0.ship_time from or_order_ship t0 where (t0.id) in (" + debugSq.getGeneratedSql() + ")"); + } + public void testCompositeKey() { ResetBasicData.reset(); From 181dc73d39f5285fb7d02ffbc923d01ff794c4d9 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 9 Aug 2021 09:19:53 +0200 Subject: [PATCH 02/22] Use MD5 for QueryBindHash --- .../java/io/ebeaninternal/api/BindHash.java | 37 +++++ .../java/io/ebeaninternal/api/BindParams.java | 11 +- .../java/io/ebeaninternal/api/HashQuery.java | 8 +- .../io/ebeaninternal/api/SpiExpression.java | 2 +- .../java/io/ebeaninternal/api/SpiQuery.java | 2 +- .../expression/AbstractTextExpression.java | 7 +- .../expression/AllEqualsExpression.java | 8 +- .../expression/ArrayContainsExpression.java | 10 +- .../expression/ArrayIsEmptyExpression.java | 5 +- .../server/expression/BetweenExpression.java | 7 +- .../expression/BetweenPropertyExpression.java | 5 +- .../server/expression/BitwiseExpression.java | 5 +- .../CaseInsensitiveEqualExpression.java | 5 +- .../expression/DefaultExampleExpression.java | 8 +- .../expression/DefaultExpressionList.java | 8 +- .../expression/ExistsQueryExpression.java | 5 +- .../server/expression/IdExpression.java | 5 +- .../server/expression/IdInExpression.java | 8 +- .../server/expression/InExpression.java | 8 +- .../server/expression/InPairsExpression.java | 8 +- .../server/expression/InQueryExpression.java | 5 +- .../server/expression/InRangeExpression.java | 7 +- .../server/expression/IsEmptyExpression.java | 5 +- .../server/expression/JsonPathExpression.java | 7 +- .../server/expression/JunctionExpression.java | 6 +- .../server/expression/LikeExpression.java | 5 +- .../server/expression/LogicExpression.java | 7 +- .../expression/NativeILikeExpression.java | 5 +- .../NestedPathWrapperExpression.java | 5 +- .../server/expression/NoopExpression.java | 4 +- .../server/expression/NotExpression.java | 5 +- .../server/expression/NullExpression.java | 5 +- .../server/expression/RawExpression.java | 8 +- .../server/expression/SimpleExpression.java | 5 +- .../server/querydefn/DefaultOrmQuery.java | 26 ++-- .../server/querydefn/HashCodeBindHash.java | 56 +++++++ .../server/querydefn/MdBindHash.java | 138 ++++++++++++++++++ .../server/expression/RawExpressionTest.java | 12 +- .../server/querydefn/DefaultOrmQueryTest.java | 14 +- .../java/org/tests/cache/TestQueryCache.java | 86 +++++++---- 40 files changed, 436 insertions(+), 137 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java new file mode 100644 index 000000000..d70c5f09d --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java @@ -0,0 +1,37 @@ +package io.ebeaninternal.api; + +/** + * BindHash implementation. + * + * @author Roland Praml, FOCONIS AG + * + */ +public interface BindHash { + + /** + * Update with boolean value. + */ + BindHash update(boolean boolValue); + + /** + * Update with int value. + */ + BindHash update(int intValue); + + /** + * Update with long value. + */ + BindHash update(long longValue); + + /** + * Update with object value. + */ + BindHash update(Object value); + + /** + * finishes the hash. May be used to compute internal state. After finish, no + * update method must be called + */ + void finish(); + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index afb1e44b8..2817c4ba1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -54,12 +54,11 @@ public class BindParams implements Serializable { positionedParameters.clear(); } - public int queryBindHash() { - int hc = namedParameters.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(positionedParameters.size()); for (Param positionedParameter : positionedParameters) { - hc = hc * 92821 + positionedParameter.hashCode(); + positionedParameter.queryBindHash(hash); } - return hc; } /** @@ -422,6 +421,10 @@ public class BindParams implements Serializable { return hc; } + void queryBindHash(BindHash hash) { + hash.update(isInParam).update(isOutParam).update(type).update(inValue); + } + @Override public boolean equals(Object o) { return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java index c14cb88d7..87241aedc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java @@ -7,12 +7,12 @@ public class HashQuery { private final CQueryPlanKey planHash; - private final int bindHash; + private final BindHash bindHash; /** * Create the HashQuery. */ - public HashQuery(CQueryPlanKey planHash, int bindHash) { + public HashQuery(CQueryPlanKey planHash, BindHash bindHash) { this.planHash = planHash; this.bindHash = bindHash; } @@ -25,7 +25,7 @@ public class HashQuery { @Override public int hashCode() { int hc = 92821 * planHash.hashCode(); - hc = 92821 * hc + bindHash; + hc = 92821 * hc + bindHash.hashCode(); return hc; } @@ -39,6 +39,6 @@ public class HashQuery { } HashQuery e = (HashQuery) obj; - return e.bindHash == bindHash && e.planHash.equals(planHash); + return e.bindHash.equals(bindHash) && e.planHash.equals(planHash); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java index 007e9f29f..32c3a3518 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java @@ -56,7 +56,7 @@ public interface SpiExpression extends Expression { /** * Return the hash value for the values that will be bound. */ - int queryBindHash(); + void queryBindHash(BindHash hash); /** * Return true if the expression is the same with respect to bind values. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 12942e7d6..d8bae7503 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -635,7 +635,7 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod * query). *

*/ - int queryBindHash(); + void queryBindHash(BindHash hash); /** * Identifies queries that are exactly the same including bind variables. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java index d30649149..d377e48dd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -37,9 +38,9 @@ public abstract class AbstractTextExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return 0; - } + public void queryBindHash(BindHash hash) { + // do nothing, only execute against document store + }; @Override public boolean isSameByBind(SpiExpression other) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java index 5a931ec5a..df9e6095b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -122,14 +123,13 @@ class AllEqualsExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { + public void queryBindHash(BindHash hash) { - int hc = 92821; + hash.update(propMap.size()); for (Object value : propMap.values()) { - hc = hc * 92821 + (value == null ? 0 : value.hashCode()); + hash.update(value); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index 8c12bec60..ac6d27d0e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -49,12 +50,11 @@ public class ArrayContainsExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = values[0].hashCode(); - for (int i = 1; i < values.length; i++) { - hc = hc * 92821 + values[i].hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(values.length); + for (int i = 0; i < values.length; i++) { + hash.update(values[i]); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java index 815223dfe..0249acabb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -33,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return empty ? 0 : 92821; + public void queryBindHash(BindHash hash) { + hash.update(empty); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java index bfdfa1d52..37a3aab8f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -49,10 +50,8 @@ class BetweenExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = low().hashCode(); - hc = hc * 92821 + high().hashCode(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(low()).update(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java index a50ec888f..440dd5f1f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -95,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { - return val().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java index 073ee51df..db1fa686d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -39,8 +40,8 @@ class BitwiseExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return Long.hashCode(flags); + public void queryBindHash(BindHash hash) { + hash.update(flags).update(match); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index 52e39fbfa..485d1b04f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -69,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression { } @Override - public int queryBindHash() { - return val().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index aade99f5a..9149923e0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -5,6 +5,7 @@ import io.ebean.LikeType; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; import io.ebean.util.SplitName; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -241,12 +242,11 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio * Return a hash for the actual bind values used. */ @Override - public int queryBindHash() { - int hc = DefaultExampleExpression.class.getName().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(list.size()); for (SpiExpression aList : list) { - hc = hc * 92821 + aList.queryBindHash(); + aList.queryBindHash(hash); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index ab80ae9e5..b5f9c6ec1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -26,6 +26,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -678,12 +679,11 @@ public class DefaultExpressionList implements SpiExpressionList { * Calculate a hash based on the expressions. */ @Override - public int queryBindHash() { - int hash = DefaultExpressionList.class.getName().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(list.size()); for (SpiExpression aList : list) { - hash = hash * 92821 + aList.queryBindHash(); + aList.queryBindHash(hash); } - return hash; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index 32ae2bccc..b3fcf8ce0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiEbeanServer; @@ -91,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress } @Override - public int queryBindHash() { - return subQuery.queryBindHash(); + public void queryBindHash(BindHash hash) { + subQuery.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java index b5280b868..904fbe9be 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -77,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression { } @Override - public int queryBindHash() { - return value.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java index 4c08b24d2..390d0bebb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -133,8 +134,11 @@ public class IdInExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { - return idCollection.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(idCollection.size()); + for (Object elem : idCollection) { + hash.update(elem); + } } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index 7009a8b44..a5719f71a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -176,12 +177,11 @@ class InExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = 92821; + public void queryBindHash(BindHash hash) { + hash.update(bindValues.size()); for (Object bindValue : bindValues) { - hc = 92821 * hc + bindValue.hashCode(); + hash.update(bindValue); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java index 96cc7c182..a7079ae55 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Pairs; import io.ebean.Pairs.Entry; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -124,12 +125,11 @@ class InPairsExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = 92821; + public void queryBindHash(BindHash hash) { + hash.update(entries.size()); for (Pairs.Entry entry : entries) { - hc = 92821 * hc + entry.hashCode(); + hash.update(entry.getA()).update(entry.getB()); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 83507af4b..619f7b715 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -72,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor } @Override - public int queryBindHash() { - return subQuery.queryBindHash(); + public void queryBindHash(BindHash hash) { + subQuery.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java index e1d170b43..abe0b7338 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -47,10 +48,8 @@ class InRangeExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = low().hashCode(); - hc = hc * 92821 + high().hashCode(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(low()).update(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java index d3a3a1da6..bb65962b3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -103,8 +104,8 @@ class IsEmptyExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return 1; + public void queryBindHash(BindHash hash) { + // no bind values } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java index f60b125cb..18e0aa924 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -83,10 +84,8 @@ class JsonPathExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = (value == null) ? 0 : value.hashCode(); - hc = (upperValue == null) ? hc : hc * 92821 + upperValue.hashCode(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(value).update(upperValue); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 0aae30886..51eba8f39 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -25,6 +25,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -225,13 +226,12 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public int queryBindHash() { + public void queryBindHash(BindHash hash) { int hc = JunctionExpression.class.getName().hashCode(); List list = exprList.internalList(); for (SpiExpression aList : list) { - hc = hc * 92821 + aList.queryBindHash(); + aList.queryBindHash(hash); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java index 6886203c4..587e6064c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -70,8 +71,8 @@ class LikeExpression extends AbstractValueExpression { } @Override - public int queryBindHash() { - return strValue().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(strValue()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java index 482aac6e8..cbd487b3e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.Junction; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -168,10 +169,8 @@ abstract class LogicExpression implements SpiExpression { } @Override - public int queryBindHash() { - int hc = expOne.queryBindHash(); - hc = hc * 92821 + expTwo.queryBindHash(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(expOne).update(expTwo); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java index c3376f656..8c51d2f5d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -54,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return val.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(val); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java index 77f46828b..f3ed1badd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -79,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression { } @Override - public int queryBindHash() { - return delegate.queryBindHash(); + public void queryBindHash(BindHash hash) { + delegate.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java index 28c9e3c78..92180cea1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -74,9 +75,8 @@ class NoopExpression implements SpiExpression { } @Override - public int queryBindHash() { + public void queryBindHash(BindHash hash) { // no bind values - return 0; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java index 60ade47b0..e9df6d856 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -99,8 +100,8 @@ final class NotExpression implements SpiExpression { } @Override - public int queryBindHash() { - return exp.queryBindHash(); + public void queryBindHash(BindHash hash) { + exp.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index 90894dbde..daded43cb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -94,7 +95,7 @@ class NullExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return (notNull ? 1 : 0); + public void queryBindHash(BindHash hash) { + hash.update(notNull); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index 295ddc75b..721d69b25 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -73,12 +74,11 @@ class RawExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { - int hc = sql.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(values.length); for (Object value : values) { - hc = hc * 92821 + value.hashCode(); + hash.update(value); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java index 0f8de90ec..e50d9856c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java @@ -5,6 +5,7 @@ import io.ebean.plugin.ExpressionPath; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.NaturalKeyQueryData; import java.io.IOException; @@ -121,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression { } @Override - public int queryBindHash() { - return value().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(value()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 96a923637..6f3334ede 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -32,6 +32,8 @@ import io.ebean.bean.PersistenceContext; import io.ebean.event.BeanQueryRequest; import io.ebean.event.readaudit.ReadEvent; import io.ebean.plugin.BeanType; +import io.ebean.plugin.LoadErrorHandler; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.BindParams; import io.ebeaninternal.api.CQueryPlanKey; import io.ebeaninternal.api.CacheIdLookup; @@ -283,6 +285,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { private boolean orderById; + private final String bindHashAlgorithm; + private ProfileLocation profileLocation; public DefaultOrmQuery(BeanDescriptor desc, SpiEbeanServer server, ExpressionFactory expressionFactory) { @@ -291,6 +295,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { this.beanType = desc.getBeanType(); this.server = server; this.orderById = server.getServerConfig().isDefaultOrderById(); + this.bindHashAlgorithm = "MD5"; // TODO: server.getServerConfig().isUseMd5BindHash(); this.disableLazyLoading = server.getServerConfig().isDisableLazyLoading(); this.expressionFactory = expressionFactory; this.detail = new OrmQueryDetail(); @@ -1276,15 +1281,12 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { *

*/ @Override - public int queryBindHash() { - int hc = (id == null ? 0 : id.hashCode()); - hc = hc * 92821 + (whereExpressions == null ? 0 : whereExpressions.queryBindHash()); - hc = hc * 92821 + (havingExpressions == null ? 0 : havingExpressions.queryBindHash()); - hc = hc * 92821 + (bindParams == null ? 0 : bindParams.queryBindHash()); - hc = hc * 92821 + (asOf == null ? 0 : asOf.hashCode()); - hc = hc * 92821 + (versionsStart == null ? 0 : versionsStart.hashCode()); - hc = hc * 92821 + (versionsEnd == null ? 0 : versionsEnd.hashCode()); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(id); + if (whereExpressions != null) whereExpressions.queryBindHash(hash); + if (havingExpressions != null) havingExpressions.queryBindHash(hash); + if (bindParams != null) bindParams.queryBindHash(hash); + hash.update(asOf).update(versionsStart).update(versionsEnd); } /** @@ -1298,8 +1300,10 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - int hc = queryBindHash(); - return new HashQuery(queryPlanKey, hc); + BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); + queryBindHash(hash); + hash.finish(); + return new HashQuery(queryPlanKey, hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java new file mode 100644 index 000000000..cc6cd5f13 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java @@ -0,0 +1,56 @@ +package io.ebeaninternal.server.querydefn; + +import java.util.Objects; + +import io.ebeaninternal.api.BindHash; + +/** + * HashCode builder that uses Object.hashCode for computing bind-hashes. + * This is a fast and lightweight implementation, but may produce collisions. + * + * @author Roland Praml, FOCONIS AG + */ +public class HashCodeBindHash implements BindHash { + + int hashCode; + + @Override + public BindHash update(int intValue) { + hashCode = hashCode * 92821 + intValue; + return this; + } + + @Override + public BindHash update(long longValue) { + hashCode = hashCode * 92821 + Long.hashCode(longValue); + return this; + } + + @Override + public BindHash update(boolean boolValue) { + hashCode = hashCode * 92821 + Boolean.hashCode(boolValue); + return this; + } + + @Override + public BindHash update(Object value) { + hashCode = hashCode * 92821 + Objects.hashCode(value); + return this; + } + + @Override + public void finish() { + // nothing to do + } + + @Override + public boolean equals(Object obj) { + return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).hashCode == hashCode; + } + + @Override + public int hashCode() { + return hashCode; + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java new file mode 100644 index 000000000..4791a9c07 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java @@ -0,0 +1,138 @@ +/* + * Licensed Materials - Property of FOCONIS AG + * (C) Copyright FOCONIS AG. + */ + +package io.ebeaninternal.server.querydefn; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.Date; +import java.util.UUID; + +import io.ebeaninternal.api.BindHash; + +/** + * Bind hash that uses a MessageDigest to compute a collision resistent hash. + * + * @author Roland Praml, FOCONIS AG + * + */ +public class MdBindHash implements BindHash { + private MessageDigest md; + private byte[] buffer; + private int hashCode; + + public MdBindHash(String algorithm) { + try { + md = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError(algorithm + " not supported", nsae); + } + } + + @Override + public BindHash update(int v) { + md.update((byte) (v >>> 24)); + md.update((byte) (v >>> 16)); + md.update((byte) (v >>> 8)); + md.update((byte) (v >>> 0)); + return this; + } + + @Override + public BindHash update(long v) { + md.update((byte) (v >>> 56)); + md.update((byte) (v >>> 48)); + md.update((byte) (v >>> 40)); + md.update((byte) (v >>> 32)); + md.update((byte) (v >>> 24)); + md.update((byte) (v >>> 16)); + md.update((byte) (v >>> 8)); + md.update((byte) (v >>> 0)); + return this; + } + + @Override + public BindHash update(boolean boolValue) { + md.update(boolValue ? (byte) 1 : (byte) 0); + return this; + } + + @Override + public BindHash update(Object value) { + if (value == null) { + md.update((byte) 0); + + // do some special handling for known object types + } else if (value instanceof String) { + md.update(((String) value).getBytes()); + + } else if (value instanceof Long) { + update(((Long) value).longValue()); + + } else if (value instanceof Double) { + double d = ((Double) value).doubleValue(); + update(Double.doubleToLongBits(d)); + + } else if (value instanceof UUID) { + UUID uuid = (UUID) value; + update(uuid.getLeastSignificantBits()); + update(uuid.getMostSignificantBits()); + + } else if (value instanceof Date) { + update(((Date) value).getTime()); + + } else if (value instanceof Instant) { + update(((Instant) value).getEpochSecond()); + update(((Instant) value).getNano()); + + } else if (value instanceof LocalDate) { + update(((LocalDate) value).toEpochDay()); + + } else if (value instanceof LocalTime) { + update(((LocalTime) value).toSecondOfDay()); + update(((LocalTime) value).toNanoOfDay()); + + } else if (value instanceof LocalDateTime) { + update(((LocalDateTime) value).toLocalDate().toEpochDay()); + update(((LocalDateTime) value).toLocalTime().toSecondOfDay()); + update(((LocalDateTime) value).toLocalTime().toNanoOfDay()); + + } else { + // Fall back to hashCode for all other types + updateOther(value); + } + return this; + } + + /** + * Update all other object. May be overridden to handle joda dates. + */ + protected void updateOther(Object value) { + // Fall back to hashCode for all other types + update(value.hashCode()); + } + + @Override + public void finish() { + buffer = md.digest(); + hashCode = Arrays.hashCode(buffer); + md = null; // clear memory + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MdBindHash && Arrays.equals(buffer, ((MdBindHash) obj).buffer); + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 1953f2959..6c608472f 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -3,6 +3,8 @@ package io.ebeaninternal.server.expression; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; +import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.server.querydefn.HashCodeBindHash; public class RawExpressionTest extends BaseExpressionTest { @@ -61,11 +63,17 @@ public class RawExpressionTest extends BaseExpressionTest { } public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) { - assertThat(exp0.queryBindHash()).isNotEqualTo(exp1.queryBindHash()); + assertThat(getHash(exp0)).isNotEqualTo(getHash(exp1)); } public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) { - assertThat(exp0.queryBindHash()).isEqualTo(exp1.queryBindHash()); + assertThat(getHash(exp0)).isEqualTo(getHash(exp1)); } + private int getHash(RawExpression query) { + BindHash hash = new HashCodeBindHash(); + query.queryBindHash(hash); + hash.finish(); + return hash.hashCode(); + } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index d9e621221..cb1738ba8 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -4,6 +4,7 @@ package io.ebeaninternal.server.querydefn; import io.ebean.BaseTestCase; import io.ebean.CacheMode; import io.ebean.Ebean; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.core.OrmQueryRequest; import org.junit.Test; @@ -62,7 +63,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isNotEqualTo(q2.createQueryPlanKey()); - assertThat(q1.queryBindHash()).isNotEqualTo(q2.queryBindHash()); + assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); } @Test @@ -73,7 +74,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(q1.queryBindHash()).isNotEqualTo(q2.queryBindHash()); + assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); } @Test @@ -84,7 +85,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(q1.queryBindHash()).isEqualTo(q2.queryBindHash()); + assertThat(getHash(q1)).isEqualTo(getHash(q2)); } @Test @@ -110,4 +111,11 @@ public class DefaultOrmQueryTest extends BaseTestCase { OrmQueryRequest r2 = createQueryRequest(SpiQuery.Type.LIST, q2, null); q2.prepare(r2); } + + private int getHash(DefaultOrmQuery query) { + BindHash hash = new HashCodeBindHash(); + query.queryBindHash(hash); + hash.finish(); + return hash.hashCode(); + } } diff --git a/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java b/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java index f9e732220..4c4d56109 100644 --- a/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java +++ b/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java @@ -3,7 +3,7 @@ package org.tests.cache; import io.ebean.BaseTestCase; import io.ebean.CacheMode; import io.ebean.DB; -import io.ebean.Ebean; +import io.ebean.ExpressionList; import io.ebean.bean.BeanCollection; import io.ebean.cache.ServerCache; import org.ebeantest.LoggedSqlCollector; @@ -14,6 +14,7 @@ import org.tests.model.basic.ResetBasicData; import org.tests.model.cache.EColAB; import java.util.List; +import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; @@ -26,8 +27,7 @@ public class TestQueryCache extends BaseTestCase { new EColAB("02", "10").save(); List list1 = - Ebean.getServer(null) - .find(EColAB.class) + DB.find(EColAB.class) .setUseQueryCache(true) .where() .eq("columnA", "01") @@ -35,8 +35,7 @@ public class TestQueryCache extends BaseTestCase { .findList(); List list2 = - Ebean.getServer(null) - .find(EColAB.class) + DB.find(EColAB.class) .setUseQueryCache(true) .where() .eq("columnA", "02") @@ -57,7 +56,7 @@ public class TestQueryCache extends BaseTestCase { new EColAB("03", "SingleAttribute").save(); new EColAB("03", "SingleAttribute").save(); - List colA_first = Ebean.getServer(null) + List colA_first = DB .find(EColAB.class) .setUseQueryCache(true) .setDistinct(true) @@ -66,7 +65,7 @@ public class TestQueryCache extends BaseTestCase { .eq("columnB", "SingleAttribute") .findSingleAttributeList(); - List colA_Second = Ebean.getServer(null) + List colA_Second = DB .find(EColAB.class) .setUseQueryCache(true) .setDistinct(true) @@ -77,7 +76,7 @@ public class TestQueryCache extends BaseTestCase { assertThat(colA_Second).isSameAs(colA_first); - List colA_NotDistinct = Ebean.getServer(null) + List colA_NotDistinct = DB .find(EColAB.class) .setUseQueryCache(true) .select("columnA") @@ -89,7 +88,7 @@ public class TestQueryCache extends BaseTestCase { // ensure that findCount & findSingleAttribute use different // slots in cache. If not a "Cannot cast List to int" should happen. - int count = Ebean.getServer(null) + int count = DB .find(EColAB.class) .setUseQueryCache(true) .select("columnA") @@ -107,13 +106,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "count") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "count") @@ -126,7 +125,7 @@ public class TestQueryCache extends BaseTestCase { // and now, ensure that we hit the database LoggedSqlCollector.start(); - int count2 = Ebean.find(EColAB.class) + int count2 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.OFF) .where() .eq("columnB", "count") @@ -142,13 +141,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "abc") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "def") @@ -167,13 +166,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "uvw") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.PUT) .where() .eq("columnB", "uvw") @@ -193,13 +192,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.PUT) .where() .eq("columnB", "xyz") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "xyz") @@ -214,26 +213,26 @@ public class TestQueryCache extends BaseTestCase { @Test @SuppressWarnings("unchecked") - public void test() { + public void testReadOnlyFind() { ResetBasicData.reset(); - ServerCache customerCache = Ebean.getServerCacheManager().getQueryCache(Customer.class); + ServerCache customerCache = DB.getServerCacheManager().getQueryCache(Customer.class); customerCache.clear(); - List list = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() + List list = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() .ilike("name", "Rob").findList(); BeanCollection bc = (BeanCollection) list; Assert.assertTrue(bc.isReadOnly()); Assert.assertFalse(bc.isEmpty()); Assert.assertTrue(!list.isEmpty()); - Assert.assertTrue(Ebean.getBeanState(list.get(0)).isReadOnly()); + Assert.assertTrue(DB.getBeanState(list.get(0)).isReadOnly()); - List list2 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() + List list2 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() .ilike("name", "Rob").findList(); - List list2B = Ebean.find(Customer.class).setUseQueryCache(true) + List list2B = DB.find(Customer.class).setUseQueryCache(true) // .setReadOnly(true) .where().ilike("name", "Rob").findList(); @@ -245,7 +244,7 @@ public class TestQueryCache extends BaseTestCase { - List list3 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where() + List list3 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where() .ilike("name", "Rob").findList(); Assert.assertNotSame(list, list3); @@ -269,13 +268,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - List colA_first = Ebean.find(EColAB.class) + List colA_first = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "someId") .findIds(); - List colA_second = Ebean.find(EColAB.class) + List colA_second = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "someId") @@ -289,7 +288,7 @@ public class TestQueryCache extends BaseTestCase { // and now, ensure that we hit the database LoggedSqlCollector.start(); - colA_second = Ebean.find(EColAB.class) + colA_second = DB.find(EColAB.class) .setUseQueryCache(CacheMode.PUT) .where() .eq("columnB", "someId") @@ -299,4 +298,35 @@ public class TestQueryCache extends BaseTestCase { assertThat(sql).hasSize(1); } + @Test + public void findCountDifferentQueriesBit() { + DB.getDefault().getPluginApi().getServerCacheManager().clearAll(); + differentFindCount(q->q.bitwiseAny("id",1), q->q.bitwiseAny("id",0)); + differentFindCount(q->q.bitwiseAll("id",1), q->q.bitwiseAll("id",0)); + // differentFindCount(q->q.bitwiseNot("id",1), q->q.bitwiseNot("id",0)); NOT 1 == AND 1 = 0 + differentFindCount(q->q.bitwiseAnd("id",1, 0), q->q.bitwiseAnd("id",1, 1)); + + differentFindCount(q->q.bitwiseAnd("id",2, 0), q->q.bitwiseAnd("id",4, 0)); + differentFindCount(q->q.bitwiseAnd("id",2, 1), q->q.bitwiseAnd("id",4, 1)); + // Will produce hash collision + differentFindCount(q->q.bitwiseAnd("id",10, 0), q->q.bitwiseAnd("id",0, 928210)); + + } + + void differentFindCount(Consumer> q0, Consumer> q1) { + LoggedSqlCollector.start(); + + ExpressionList el0 = DB.find(EColAB.class).setUseQueryCache(CacheMode.ON).where(); + q0.accept(el0); + el0.findCount(); + + ExpressionList el1 = DB.find(EColAB.class).setUseQueryCache(CacheMode.ON).where(); + q1.accept(el1); + el1.findCount(); + + List sql = LoggedSqlCollector.stop(); + + assertThat(sql).hasSize(2); // different queries + } + } From e18c190905fa8a2a2051e6bba3447ad1a942408d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 9 Aug 2021 09:46:49 +0200 Subject: [PATCH 03/22] Fix the test to run with java 8 --- .../server/profile/BasicProfileLocationTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java index 84a53442a..9d99b17ce 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java @@ -14,7 +14,11 @@ public class BasicProfileLocationTest { assertThat(loc.obtain()).isTrue(); assertThat(loc.fullLocation()).endsWith(":12)"); - assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0"); + if (System.getProperty("java.version").startsWith("1.8")) { + assertThat(loc.location()).isEqualTo("sun.reflect.NativeMethodAccessorImpl.invoke0"); + } else { + assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0"); + } assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0"); } From c8ae3c5ef65da0ad043f9a0c302720abd298fda9 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 10 Aug 2021 09:30:25 +0200 Subject: [PATCH 04/22] FIX: Changelog-oldValue did not work for Json mutable properties --- .../io/ebean/bean/EntityBeanIntercept.java | 13 ++++++--- .../org/tests/changelog/TestChangeLog.java | 27 +++++++++++++++++++ .../tests/model/basic/EBasicChangeLog.java | 19 ++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 78d8e5f8c..527860200 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -395,6 +395,15 @@ public final class EntityBeanIntercept implements Serializable { this.owner._ebean_setEmbeddedLoaded(); this.lazyLoadProperty = -1; this.origValues = null; + // after save, transfer the mutable next values back to mutable info + if (mutableNext != null) { + for (int i = 0; i < mutableNext.length; i++) { + MutableValueNext next = mutableNext[i]; + if (next != null) { + mutableInfo(i, next.info()); + } + } + } this.mutableNext = null; for (int i = 0; i < flags.length; i++) { flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET); @@ -1223,9 +1232,7 @@ public final class EntityBeanIntercept implements Serializable { if (mutableNext == null) { return null; } - final MutableValueNext next = mutableNext[propertyIndex]; - mutableInfo(propertyIndex, next.info()); - return next.content(); + return mutableNext[propertyIndex].content(); } } diff --git a/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java b/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java index 2adbbfc4b..a10a41276 100644 --- a/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java +++ b/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java @@ -16,10 +16,13 @@ import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.changelog.ChangeSet; import io.ebean.event.changelog.ChangeType; import io.ebean.event.changelog.TxnState; +import io.ebeantest.LoggedSql; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.tests.model.basic.EBasicChangeLog; +import org.tests.model.json.PlainBean; import java.util.ArrayList; import java.util.List; @@ -130,7 +133,31 @@ public class TestChangeLog extends BaseTestCase { assertThat(change.getEvent()).isEqualTo(ChangeType.DELETE); assertThat(change.getData()).isNull(); } + + @Test + public void testWithJsonMutationDetection() { + EBasicChangeLog bean = new EBasicChangeLog(); + bean.setName(null); + bean.setShortDescription("hello"); + PlainBean jsonBean = new PlainBean(); + bean.setPlainBean(jsonBean); + jsonBean.setName("A"); + server.save(bean); + + BeanChange change = firstChange(); + assertThat(change.getEvent()).isEqualTo(ChangeType.INSERT); + + jsonBean.setName("B"); + LoggedSql.start(); + server.save(bean); + assertThat(LoggedSql.stop()).isNotEmpty(); + + change = firstChange(); + assertThat(change.getEvent()).isEqualTo(ChangeType.UPDATE); + assertThat(change.getData()).contains("\"plainBean\":{\"name\":\"B\""); + assertThat(change.getOldData()).contains("\"plainBean\":{\"name\":\"A\""); + } private Database createServer() { DatabaseConfig config = new DatabaseConfig(); diff --git a/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java b/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java index 88e3e4f10..0ac02e749 100644 --- a/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java +++ b/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java @@ -2,6 +2,7 @@ package org.tests.model.basic; import io.ebean.annotation.Cache; import io.ebean.annotation.ChangeLog; +import io.ebean.annotation.DbJson; import io.ebean.annotation.ReadAudit; import io.ebean.annotation.WhenCreated; import io.ebean.annotation.WhenModified; @@ -12,11 +13,16 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; import javax.validation.constraints.Size; + +import org.tests.model.json.PlainBean; + +import static io.ebean.annotation.MutationDetection.SOURCE; + import java.sql.Timestamp; @Cache(enableQueryCache = true) @ReadAudit -@ChangeLog(updatesThatInclude = {"name", "shortDescription"}) +@ChangeLog(updatesThatInclude = {"name", "shortDescription", "plainBean"}) @Entity public class EBasicChangeLog { @@ -46,6 +52,9 @@ public class EBasicChangeLog { @Version Long version; + + @DbJson(length = 500, mutationDetection = SOURCE) // such that we can rebuild old values + PlainBean plainBean; public Long getId() { return id; @@ -118,4 +127,12 @@ public class EBasicChangeLog { public void setVersion(Long version) { this.version = version; } + + public PlainBean getPlainBean() { + return plainBean; + } + + public void setPlainBean(PlainBean plainBean) { + this.plainBean = plainBean; + } } From 3e8f76cd6d054cbc9bb88392c4e3048d8857841b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 21:49:31 +1200 Subject: [PATCH 05/22] Update HashCodeBindHash to use values for equals() --- .../server/querydefn/DefaultOrmQuery.java | 3 ++- .../server/querydefn/HashCodeBindHash.java | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 6f3334ede..f29889a88 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1300,7 +1300,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); + //BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); + BindHash hash = new HashCodeBindHash();// : new MdBindHash(bindHashAlgorithm); queryBindHash(hash); hash.finish(); return new HashQuery(queryPlanKey, hash); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java index cc6cd5f13..9bc9c60fc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java @@ -1,5 +1,7 @@ package io.ebeaninternal.server.querydefn; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import io.ebeaninternal.api.BindHash; @@ -12,28 +14,33 @@ import io.ebeaninternal.api.BindHash; */ public class HashCodeBindHash implements BindHash { - int hashCode; + private final List values = new ArrayList<>(); + private int hashCode; @Override public BindHash update(int intValue) { + values.add(intValue); hashCode = hashCode * 92821 + intValue; return this; } @Override public BindHash update(long longValue) { + values.add(longValue); hashCode = hashCode * 92821 + Long.hashCode(longValue); return this; } @Override public BindHash update(boolean boolValue) { + values.add(boolValue); hashCode = hashCode * 92821 + Boolean.hashCode(boolValue); return this; } @Override public BindHash update(Object value) { + values.add(value); hashCode = hashCode * 92821 + Objects.hashCode(value); return this; } @@ -45,7 +52,7 @@ public class HashCodeBindHash implements BindHash { @Override public boolean equals(Object obj) { - return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).hashCode == hashCode; + return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).values.equals(values); } @Override From ede1ad0a4c0d067bfa2823d0ec24530c7e88254e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 21:58:41 +1200 Subject: [PATCH 06/22] Fix BindParams equals() and hashCode() --- .../java/io/ebeaninternal/api/BindParams.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index 2817c4ba1..3a81f2f0a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -4,11 +4,7 @@ import io.ebeaninternal.server.persist.MultiValueWrapper; import io.ebeaninternal.server.querydefn.NaturalKeyBindParam; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; /** @@ -414,6 +410,7 @@ public class BindParams implements Serializable { @Override public int hashCode() { int hc = getClass().hashCode(); + hc = hc * 92821 + (encryptionKey ? 0 : 1); hc = hc * 92821 + (isInParam ? 0 : 1); hc = hc * 92821 + (isOutParam ? 0 : 1); hc = hc * 92821 + (type); @@ -421,13 +418,17 @@ public class BindParams implements Serializable { return hc; } - void queryBindHash(BindHash hash) { - hash.update(isInParam).update(isOutParam).update(type).update(inValue); - } - @Override public boolean equals(Object o) { - return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode()); + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Param param = (Param) o; + return encryptionKey == param.encryptionKey && isInParam == param.isInParam && isOutParam == param.isOutParam + && type == param.type && Objects.equals(inValue, param.inValue); + } + + void queryBindHash(BindHash hash) { + hash.update(isInParam).update(isOutParam).update(type).update(inValue); } /** From 2dc48cf08d4519e66bc8bc81b9f31a791407e47d Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:00:46 +1200 Subject: [PATCH 07/22] ArrayContainsExpression equals() to use enhanced for loop --- .../server/expression/ArrayContainsExpression.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index ac6d27d0e..3c200c046 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -52,8 +52,8 @@ public class ArrayContainsExpression extends AbstractExpression { @Override public void queryBindHash(BindHash hash) { hash.update(values.length); - for (int i = 0; i < values.length; i++) { - hash.update(values[i]); + for (Object value : values) { + hash.update(value); } } From ac3edbedf41ef627f5d99f77507dbfac4fc51dd5 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:07:48 +1200 Subject: [PATCH 08/22] Remove MdBindHash, tidy DefaultOrmQuery --- .../server/querydefn/DefaultOrmQuery.java | 67 +-------- .../server/querydefn/MdBindHash.java | 138 ------------------ 2 files changed, 5 insertions(+), 200 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index f29889a88..e4735e32b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1,30 +1,7 @@ package io.ebeaninternal.server.querydefn; -import io.ebean.CacheMode; -import io.ebean.CountDistinctOrder; -import io.ebean.Database; -import io.ebean.DtoQuery; -import io.ebean.Expression; -import io.ebean.ExpressionFactory; -import io.ebean.ExpressionList; -import io.ebean.FetchConfig; -import io.ebean.FetchGroup; -import io.ebean.FetchPath; -import io.ebean.FutureIds; -import io.ebean.FutureList; -import io.ebean.FutureRowCount; -import io.ebean.OrderBy; +import io.ebean.*; import io.ebean.OrderBy.Property; -import io.ebean.PagedList; -import io.ebean.PersistenceContextScope; -import io.ebean.ProfileLocation; -import io.ebean.Query; -import io.ebean.QueryIterator; -import io.ebean.QueryType; -import io.ebean.RawSql; -import io.ebean.Transaction; -import io.ebean.UpdateQuery; -import io.ebean.Version; import io.ebean.bean.CallOrigin; import io.ebean.bean.ObjectGraphNode; import io.ebean.bean.ObjectGraphOrigin; @@ -32,31 +9,10 @@ import io.ebean.bean.PersistenceContext; import io.ebean.event.BeanQueryRequest; import io.ebean.event.readaudit.ReadEvent; import io.ebean.plugin.BeanType; -import io.ebean.plugin.LoadErrorHandler; -import io.ebeaninternal.api.BindHash; -import io.ebeaninternal.api.BindParams; -import io.ebeaninternal.api.CQueryPlanKey; -import io.ebeaninternal.api.CacheIdLookup; -import io.ebeaninternal.api.CacheIdLookupMany; -import io.ebeaninternal.api.CacheIdLookupSingle; -import io.ebeaninternal.api.HashQuery; -import io.ebeaninternal.api.ManyWhereJoins; -import io.ebeaninternal.api.NaturalKeyQueryData; -import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.api.SpiExpression; -import io.ebeaninternal.api.SpiExpressionList; -import io.ebeaninternal.api.SpiExpressionValidation; -import io.ebeaninternal.api.SpiNamedParam; -import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.api.SpiQuerySecondary; -import io.ebeaninternal.api.SpiTransaction; +import io.ebeaninternal.api.*; import io.ebeaninternal.server.autotune.ProfilingListener; import io.ebeaninternal.server.core.SpiOrmQueryRequest; -import io.ebeaninternal.server.deploy.BeanDescriptor; -import io.ebeaninternal.server.deploy.BeanNaturalKey; -import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import io.ebeaninternal.server.deploy.InheritInfo; -import io.ebeaninternal.server.deploy.TableJoin; +import io.ebeaninternal.server.deploy.*; import io.ebeaninternal.server.el.ElPropertyDeploy; import io.ebeaninternal.server.expression.DefaultExpressionList; import io.ebeaninternal.server.expression.IdInExpression; @@ -68,14 +24,7 @@ import io.ebeaninternal.server.transaction.ExternalJdbcTransaction; import javax.persistence.PersistenceException; import java.sql.Connection; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.locks.ReentrantLock; +import java.util.*; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -93,8 +42,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy(); - private final ReentrantLock lock = new ReentrantLock(); - private final Class beanType; private final ExpressionFactory expressionFactory; @@ -285,8 +232,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { private boolean orderById; - private final String bindHashAlgorithm; - private ProfileLocation profileLocation; public DefaultOrmQuery(BeanDescriptor desc, SpiEbeanServer server, ExpressionFactory expressionFactory) { @@ -295,7 +240,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { this.beanType = desc.getBeanType(); this.server = server; this.orderById = server.getServerConfig().isDefaultOrderById(); - this.bindHashAlgorithm = "MD5"; // TODO: server.getServerConfig().isUseMd5BindHash(); this.disableLazyLoading = server.getServerConfig().isDisableLazyLoading(); this.expressionFactory = expressionFactory; this.detail = new OrmQueryDetail(); @@ -1300,8 +1244,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - //BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); - BindHash hash = new HashCodeBindHash();// : new MdBindHash(bindHashAlgorithm); + BindHash hash = new HashCodeBindHash(); queryBindHash(hash); hash.finish(); return new HashQuery(queryPlanKey, hash); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java deleted file mode 100644 index 4791a9c07..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed Materials - Property of FOCONIS AG - * (C) Copyright FOCONIS AG. - */ - -package io.ebeaninternal.server.querydefn; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.Arrays; -import java.util.Date; -import java.util.UUID; - -import io.ebeaninternal.api.BindHash; - -/** - * Bind hash that uses a MessageDigest to compute a collision resistent hash. - * - * @author Roland Praml, FOCONIS AG - * - */ -public class MdBindHash implements BindHash { - private MessageDigest md; - private byte[] buffer; - private int hashCode; - - public MdBindHash(String algorithm) { - try { - md = MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException nsae) { - throw new InternalError(algorithm + " not supported", nsae); - } - } - - @Override - public BindHash update(int v) { - md.update((byte) (v >>> 24)); - md.update((byte) (v >>> 16)); - md.update((byte) (v >>> 8)); - md.update((byte) (v >>> 0)); - return this; - } - - @Override - public BindHash update(long v) { - md.update((byte) (v >>> 56)); - md.update((byte) (v >>> 48)); - md.update((byte) (v >>> 40)); - md.update((byte) (v >>> 32)); - md.update((byte) (v >>> 24)); - md.update((byte) (v >>> 16)); - md.update((byte) (v >>> 8)); - md.update((byte) (v >>> 0)); - return this; - } - - @Override - public BindHash update(boolean boolValue) { - md.update(boolValue ? (byte) 1 : (byte) 0); - return this; - } - - @Override - public BindHash update(Object value) { - if (value == null) { - md.update((byte) 0); - - // do some special handling for known object types - } else if (value instanceof String) { - md.update(((String) value).getBytes()); - - } else if (value instanceof Long) { - update(((Long) value).longValue()); - - } else if (value instanceof Double) { - double d = ((Double) value).doubleValue(); - update(Double.doubleToLongBits(d)); - - } else if (value instanceof UUID) { - UUID uuid = (UUID) value; - update(uuid.getLeastSignificantBits()); - update(uuid.getMostSignificantBits()); - - } else if (value instanceof Date) { - update(((Date) value).getTime()); - - } else if (value instanceof Instant) { - update(((Instant) value).getEpochSecond()); - update(((Instant) value).getNano()); - - } else if (value instanceof LocalDate) { - update(((LocalDate) value).toEpochDay()); - - } else if (value instanceof LocalTime) { - update(((LocalTime) value).toSecondOfDay()); - update(((LocalTime) value).toNanoOfDay()); - - } else if (value instanceof LocalDateTime) { - update(((LocalDateTime) value).toLocalDate().toEpochDay()); - update(((LocalDateTime) value).toLocalTime().toSecondOfDay()); - update(((LocalDateTime) value).toLocalTime().toNanoOfDay()); - - } else { - // Fall back to hashCode for all other types - updateOther(value); - } - return this; - } - - /** - * Update all other object. May be overridden to handle joda dates. - */ - protected void updateOther(Object value) { - // Fall back to hashCode for all other types - update(value.hashCode()); - } - - @Override - public void finish() { - buffer = md.digest(); - hashCode = Arrays.hashCode(buffer); - md = null; // clear memory - } - - @Override - public boolean equals(Object obj) { - return obj instanceof MdBindHash && Arrays.equals(buffer, ((MdBindHash) obj).buffer); - } - - @Override - public int hashCode() { - return hashCode; - } -} From e1ad210bb088aa7da3a6ee15fe6775742becf784 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:18:33 +1200 Subject: [PATCH 09/22] Tidy JunctionExpression --- .../server/expression/JunctionExpression.java | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 51eba8f39..88614873d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -114,9 +114,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void writeDocQuery(DocQueryContext context) throws IOException { context.startBool(type); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.writeDocQuery(context); + for (SpiExpression expr : exprList.internalList()) { + expr.writeDocQuery(context); } context.endBool(); } @@ -124,9 +123,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void writeDocQueryJunction(DocQueryContext context) throws IOException { context.startBoolGroupList(type); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.writeDocQuery(context); + for (SpiExpression expr : exprList.internalList()) { + expr.writeDocQuery(context); } context.endBoolGroupList(); } @@ -139,18 +137,15 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { - List list = exprList.internalList(); - // get the current state for 'require outer joins' boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins(); if (type == Type.OR) { // turn on outer joins required for disjunction expressions manyWhereJoin.setRequireOuterJoins(true); } - - for (SpiExpression aList : list) { - aList.containsMany(desc, manyWhereJoin); + for (SpiExpression expr : list) { + expr.containsMany(desc, manyWhereJoin); } if (type == Type.OR && !parentOuterJoins) { // restore state to not forcing outer joins @@ -177,18 +172,14 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void addBindValues(SpiExpressionRequest request) { - - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.addBindValues(request); + for (SpiExpression expr : exprList.internalList()) { + expr.addBindValues(request); } } @Override public void addSql(SpiExpressionRequest request) { - List list = exprList.internalList(); - if (!list.isEmpty()) { request.append(type.prefix()); request.append("("); @@ -205,9 +196,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void prepareExpression(BeanQueryRequest request) { - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.prepareExpression(request); + for (SpiExpression expr : exprList.internalList()) { + expr.prepareExpression(request); } } @@ -227,10 +217,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void queryBindHash(BindHash hash) { - int hc = JunctionExpression.class.getName().hashCode(); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.queryBindHash(hash); + for (SpiExpression expr : exprList.internalList()) { + expr.queryBindHash(hash); } } @@ -275,7 +263,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression return exprList.textCommonTerms(search, options); } - @Override public ExpressionList allEq(Map propertyMap) { return exprList.allEq(propertyMap); @@ -1025,7 +1012,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public String nestedPath(BeanDescriptor desc) { - PrepareDocNested.prepare(exprList, desc, type); String nestedPath = exprList.allDocNestedPath; if (nestedPath != null) { From e47b1737a82382093714d07bc3f20f787c63c7f9 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:28:31 +1200 Subject: [PATCH 10/22] Add HashCodeBindHashTest --- .../querydefn/HashCodeBindHashTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java new file mode 100644 index 000000000..dea0a2e0c --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java @@ -0,0 +1,37 @@ +package io.ebeaninternal.server.querydefn; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashCodeBindHashTest { + + @Test + public void update_with_null() { + + HashCodeBindHash hash = new HashCodeBindHash(); + hash.update(1).update(null).update("hello"); + + HashCodeBindHash hash2 = new HashCodeBindHash(); + hash2.update(1).update(null).update("hello"); + + assertThat(hash).isEqualTo(hash2); + } + + @Test + public void notEqual() { + + HashCodeBindHash hash = new HashCodeBindHash(); + hash.update(1).update(null).update("hello"); + + HashCodeBindHash hash2 = new HashCodeBindHash(); + hash2.update(1).update("hello"); + + HashCodeBindHash hash3 = new HashCodeBindHash(); + hash2.update(1).update(null); + + assertThat(hash).isNotEqualTo(hash2); + assertThat(hash).isNotEqualTo(hash3); + assertThat(hash2).isNotEqualTo(hash3); + } +} From 75fa7cb7fddda9d5699354e8c0a298abfc6888bc Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 10 Aug 2021 13:24:13 +0200 Subject: [PATCH 11/22] PushJson/PopJson did not work correctly, if null beans were involved --- .../type/ScalarTypeJsonObjectMapper.java | 4 +- .../org/tests/json/TestDbJson_Jackson3.java | 16 ++++ .../org/tests/model/json/EBasicJsonMulti.java | 81 +++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 7ae24420f..41cb28d49 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -80,11 +80,11 @@ class ScalarTypeJsonObjectMapper { @Override public Object read(DataReader reader) throws SQLException { String json = reader.getString(); + // pushJson such that we MD5 and store on EntityBeanIntercept later + reader.pushJson(json); if (json == null || json.isEmpty()) { return null; } - // pushJson such that we MD5 and store on EntityBeanIntercept later - reader.pushJson(json); try { return objectReader.readValue(json, deserType); } catch (IOException e) { diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index c1cc3c40d..5b58b3108 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -10,6 +10,7 @@ import io.ebeantest.LoggedSql; import org.junit.Test; import org.tests.model.json.EBasicJsonJackson3; import org.tests.model.json.EBasicJsonList; +import org.tests.model.json.EBasicJsonMulti; import org.tests.model.json.PlainBean; import org.tests.model.json.PlainBeanDirtyAware; @@ -221,6 +222,21 @@ public class TestDbJson_Jackson3 extends BaseTestCase { LoggedSql.stop(); } + + @Test + public void push_pop_test() { + + EBasicJsonMulti bean = new EBasicJsonMulti(); + bean.setPlainValue2(new PlainBeanDirtyAware("x", 42)); + bean.save(); + + bean = DB.find(EBasicJsonMulti.class, bean.getId()); + bean.setPlainValue1(null); // already null + bean.setPlainValue2(null); + bean.setPlainValue3(null); // already null + BeanState state = DB.getBeanState(bean); + assertThat(state.getDirtyValues()).hasSize(1).containsKey("plainValue2"); + } private void expectedSql(int i, String s) { assertThat(LoggedSql.collect().get(i)).contains(s); diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java new file mode 100644 index 000000000..23dcd33d9 --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java @@ -0,0 +1,81 @@ +package org.tests.model.json; + +import io.ebean.Model; +import io.ebean.annotation.DbJson; +import io.ebean.annotation.MutationDetection; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; + +import static io.ebean.annotation.MutationDetection.NONE; +import static io.ebean.annotation.MutationDetection.SOURCE; + +@Entity +public class EBasicJsonMulti extends Model { + + @Id + Long id; + + String name; + + @DbJson(length = 500, mutationDetection = SOURCE) + PlainBeanDirtyAware plainValue1; + + @DbJson(length = 500, mutationDetection = SOURCE) + PlainBeanDirtyAware plainValue2; + + @DbJson(length = 500, mutationDetection = SOURCE) + PlainBeanDirtyAware plainValue3; + + @Version + long version; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PlainBeanDirtyAware getPlainValue1() { + return plainValue1; + } + + public void setPlainValue1(PlainBeanDirtyAware plainValue1) { + this.plainValue1 = plainValue1; + } + + public PlainBeanDirtyAware getPlainValue2() { + return plainValue2; + } + + public void setPlainValue2(PlainBeanDirtyAware plainValue2) { + this.plainValue2 = plainValue2; + } + + public PlainBeanDirtyAware getPlainValue3() { + return plainValue3; + } + + public void setPlainValue3(PlainBeanDirtyAware plainValue3) { + this.plainValue3 = plainValue3; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } +} From 60dfaab22ce4f10539c2c570a8d014d616321efd Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 10 Aug 2021 15:29:10 +0200 Subject: [PATCH 12/22] Enables select + exists for dynamic formula properties --- .../server/deploy/BeanDescriptor.java | 2 +- .../server/query/SqlTreeBuilder.java | 8 ++--- .../java/org/tests/query/TestQueryAlias.java | 29 +++++++++++++++---- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 8e8b02b6f..055f2605a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2519,7 +2519,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { if (propName.indexOf('(') > -1) { return findSqlTreeFormula(propName, path); } - return _findBeanProperty(propName); + return findProperty(propName); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 239e8a58e..7e165ef57 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -365,11 +365,11 @@ public final class SqlTreeBuilder { * This means it can included individual properties of an embedded bean. *

*/ - private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName) { - STreeProperty p = desc.findProperty(propName); + private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName, String path) { + STreeProperty p = desc.findPropertyWithDynamic(propName, path); if (p == null) { logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it."); - + return; } else if (p instanceof STreePropertyAssoc && p.isEmbedded()) { // if the property is embedded we need to lookup the real column name int pos = propName.indexOf('.'); @@ -383,7 +383,7 @@ public final class SqlTreeBuilder { private void addProperty(SqlTreeProperties selectProps, STreeType desc, OrmQueryProperties queryProps, String propName) { if (subQuery) { - addPropertyToSubQuery(selectProps, desc, propName); + addPropertyToSubQuery(selectProps, desc, propName, queryProps.getPath()); return; } diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java b/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java index de378586c..4e77d49d6 100644 --- a/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java +++ b/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java @@ -1,7 +1,7 @@ package org.tests.query; import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import io.ebean.DB; import io.ebean.Query; import org.junit.Test; import org.tests.model.basic.CKeyParent; @@ -16,11 +16,11 @@ public class TestQueryAlias extends BaseTestCase { ResetBasicData.reset(); - Query sq = Ebean.createQuery(CKeyParent.class) + Query sq = DB.createQuery(CKeyParent.class) .select("id.oneKey").alias("st0") .setAutoTune(false).where().query(); - Query pq = Ebean.find(CKeyParent.class).alias("myt0").where().in("id.oneKey", sq).query(); + Query pq = DB.find(CKeyParent.class).alias("myt0").where().in("id.oneKey", sq).query(); pq.findList(); @@ -36,17 +36,36 @@ public class TestQueryAlias extends BaseTestCase { assertThat(sql).contains("ckey_parent myt0"); assertThat(sql).contains("(myt0.one_key) in (select st0.one_key from ckey_parent st0)"); } + + @Test + public void testExistsWithConcat() { + + ResetBasicData.reset(); + + Query sq = DB.createQuery(CKeyParent.class) + .select("concat(id.oneKey,id.twoKey)").alias("st0") + .setAutoTune(false).where().query(); + + Query pq = DB.find(CKeyParent.class).alias("myt0").where().in("concat(id.oneKey,id.twoKey)", sq).query(); + + pq.findList(); + + String sql = pq.getGeneratedSql(); + + assertThat(sql).contains("ckey_parent myt0"); + assertThat(sql).contains("(concat(myt0.one_key,myt0.two_key)) in (select concat(st0.one_key,st0.two_key) from ckey_parent st0)"); + } @Test public void testNotExists() { ResetBasicData.reset(); - Query sq = Ebean.createQuery(CKeyParent.class) + Query sq = DB.createQuery(CKeyParent.class) .select("id.oneKey").alias("st0") .setAutoTune(false).where().query(); - Query pq = Ebean.find(CKeyParent.class).alias("myt0").where().notIn("id.oneKey", sq).query(); + Query pq = DB.find(CKeyParent.class).alias("myt0").where().notIn("id.oneKey", sq).query(); pq.findList(); From d08f7af1e0e03a3773eee88b82b1698e30ed43d8 Mon Sep 17 00:00:00 2001 From: Thomas Fellner Date: Tue, 10 Aug 2021 18:31:02 +0200 Subject: [PATCH 13/22] update ebean-ddl-generator version in ebean-core --- ebean-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 11aac552a..b99bf98fb 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -72,7 +72,7 @@ io.ebean ebean-ddl-generator - 12.9.4-RC1 + 12.11.0 test From 11e8e3696f7a0c17cefe2a467625f1cb2447d045 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 12:24:36 +1200 Subject: [PATCH 14/22] BindParams does not need encryptionKey in equals/hashCode --- .../src/main/java/io/ebeaninternal/api/BindParams.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index 3a81f2f0a..b3da53833 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -410,8 +410,7 @@ public class BindParams implements Serializable { @Override public int hashCode() { int hc = getClass().hashCode(); - hc = hc * 92821 + (encryptionKey ? 0 : 1); - hc = hc * 92821 + (isInParam ? 0 : 1); +`` hc = hc * 92821 + (isInParam ? 0 : 1); hc = hc * 92821 + (isOutParam ? 0 : 1); hc = hc * 92821 + (type); hc = hc * 92821 + (inValue == null ? 0 : inValue.hashCode()); @@ -423,8 +422,7 @@ public class BindParams implements Serializable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Param param = (Param) o; - return encryptionKey == param.encryptionKey && isInParam == param.isInParam && isOutParam == param.isOutParam - && type == param.type && Objects.equals(inValue, param.inValue); + return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue); } void queryBindHash(BindHash hash) { From fd2e542c0b90433140c41b3434cd0e84873f0643 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 12:43:58 +1200 Subject: [PATCH 15/22] Change BindHash from interface to implementation, Delete unused server persist BindValues --- .../java/io/ebeaninternal/api/BindHash.java | 42 ++++------ .../java/io/ebeaninternal/api/BindParams.java | 2 +- .../server/persist/BindValues.java | 81 ------------------- .../ebeaninternal/server/persist/Binder.java | 27 ------- .../server/querydefn/DefaultOrmQuery.java | 3 +- .../server/querydefn/HashCodeBindHash.java | 63 --------------- .../server/expression/RawExpressionTest.java | 4 +- ...odeBindHashTest.java => BindHashTest.java} | 13 +-- .../server/querydefn/DefaultOrmQueryTest.java | 3 +- 9 files changed, 28 insertions(+), 210 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java rename ebean-core/src/test/java/io/ebeaninternal/server/querydefn/{HashCodeBindHashTest.java => BindHashTest.java} (67%) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java index d70c5f09d..b3ad8e49b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java @@ -1,37 +1,29 @@ package io.ebeaninternal.api; +import java.util.ArrayList; +import java.util.List; + /** * BindHash implementation. - * - * @author Roland Praml, FOCONIS AG - * */ -public interface BindHash { +public class BindHash { - /** - * Update with boolean value. - */ - BindHash update(boolean boolValue); + private final List values = new ArrayList<>(); - /** - * Update with int value. - */ - BindHash update(int intValue); + public BindHash update(Object value) { + values.add(value); + return this; + } - /** - * Update with long value. - */ - BindHash update(long longValue); + @Override + public boolean equals(Object obj) { + return obj instanceof BindHash && ((BindHash) obj).values.equals(values); + } - /** - * Update with object value. - */ - BindHash update(Object value); + @Override + public int hashCode() { + return values.hashCode(); + } - /** - * finishes the hash. May be used to compute internal state. After finish, no - * update method must be called - */ - void finish(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index b3da53833..74e7dfa23 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -410,7 +410,7 @@ public class BindParams implements Serializable { @Override public int hashCode() { int hc = getClass().hashCode(); -`` hc = hc * 92821 + (isInParam ? 0 : 1); + hc = hc * 92821 + (isInParam ? 0 : 1); hc = hc * 92821 + (isOutParam ? 0 : 1); hc = hc * 92821 + (type); hc = hc * 92821 + (inValue == null ? 0 : inValue.hashCode()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java deleted file mode 100644 index 6d71e8d79..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.ebeaninternal.server.persist; - -import java.util.ArrayList; - -/** - * Holds a list of bind values for binding to a PreparedStatement. - */ -class BindValues { - - private final ArrayList list = new ArrayList<>(); - - /** - * Create with a Binder. - */ - public BindValues() { - } - - /** - * Add a bind value with its JDBC datatype. - * - * @param value the bind value - * @param dbType the type as per java.sql.Types - */ - public void add(Object value, int dbType, String name) { - list.add(new Value(value, dbType, name)); - } - - /** - * List of bind values. - */ - public ArrayList values() { - return list; - } - - /** - * A Value has additionally the JDBC data type. - */ - public static class Value { - - private final Object value; - - private final int dbType; - - private final String name; - - /** - * Create the value. - */ - Value(Object value, int dbType, String name) { - this.value = value; - this.dbType = dbType; - this.name = name; - } - - /** - * Return the type as per java.sql.Types. - */ - public int getDbType() { - return dbType; - } - - /** - * Return the value. - */ - public Object getValue() { - return value; - } - - /** - * Return the property name. - */ - public String getName() { - return name; - } - - @Override - public String toString() { - return String.valueOf(value); - } - } -} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java index 1d94d7a81..600a7e21a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java @@ -77,33 +77,6 @@ public class Binder { return asOfStandardsBased; } - /** - * Bind the values to the Prepared Statement. - */ - public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf) throws SQLException { - String logPrefix = ""; - ArrayList list = bindValues.values(); - for (BindValues.Value bindValue : list) { - Object val = bindValue.getValue(); - int dt = bindValue.getDbType(); - bindObject(dataBind, val, dt); - - if (bindBuf != null) { - bindBuf.append(logPrefix); - if (logPrefix.isEmpty()) { - logPrefix = ", "; - } - bindBuf.append(bindValue.getName()); - bindBuf.append("="); - if (isLob(dt)) { - bindBuf.append("[LOB]"); - } else { - bindBuf.append(val); - } - } - } - } - /** * Bind the parameters to the preparedStatement returning the bind log. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index e4735e32b..865b3184b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1244,9 +1244,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - BindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); queryBindHash(hash); - hash.finish(); return new HashQuery(queryPlanKey, hash); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java deleted file mode 100644 index 9bc9c60fc..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.ebeaninternal.server.querydefn; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import io.ebeaninternal.api.BindHash; - -/** - * HashCode builder that uses Object.hashCode for computing bind-hashes. - * This is a fast and lightweight implementation, but may produce collisions. - * - * @author Roland Praml, FOCONIS AG - */ -public class HashCodeBindHash implements BindHash { - - private final List values = new ArrayList<>(); - private int hashCode; - - @Override - public BindHash update(int intValue) { - values.add(intValue); - hashCode = hashCode * 92821 + intValue; - return this; - } - - @Override - public BindHash update(long longValue) { - values.add(longValue); - hashCode = hashCode * 92821 + Long.hashCode(longValue); - return this; - } - - @Override - public BindHash update(boolean boolValue) { - values.add(boolValue); - hashCode = hashCode * 92821 + Boolean.hashCode(boolValue); - return this; - } - - @Override - public BindHash update(Object value) { - values.add(value); - hashCode = hashCode * 92821 + Objects.hashCode(value); - return this; - } - - @Override - public void finish() { - // nothing to do - } - - @Override - public boolean equals(Object obj) { - return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).values.equals(values); - } - - @Override - public int hashCode() { - return hashCode; - } - -} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 6c608472f..99c7d7eec 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -4,7 +4,6 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import io.ebeaninternal.api.BindHash; -import io.ebeaninternal.server.querydefn.HashCodeBindHash; public class RawExpressionTest extends BaseExpressionTest { @@ -71,9 +70,8 @@ public class RawExpressionTest extends BaseExpressionTest { } private int getHash(RawExpression query) { - BindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); query.queryBindHash(hash); - hash.finish(); return hash.hashCode(); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java similarity index 67% rename from ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java rename to ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java index dea0a2e0c..a74f4432f 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java @@ -1,18 +1,19 @@ package io.ebeaninternal.server.querydefn; +import io.ebeaninternal.api.BindHash; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class HashCodeBindHashTest { +public class BindHashTest { @Test public void update_with_null() { - HashCodeBindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); hash.update(1).update(null).update("hello"); - HashCodeBindHash hash2 = new HashCodeBindHash(); + BindHash hash2 = new BindHash(); hash2.update(1).update(null).update("hello"); assertThat(hash).isEqualTo(hash2); @@ -21,13 +22,13 @@ public class HashCodeBindHashTest { @Test public void notEqual() { - HashCodeBindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); hash.update(1).update(null).update("hello"); - HashCodeBindHash hash2 = new HashCodeBindHash(); + BindHash hash2 = new BindHash(); hash2.update(1).update("hello"); - HashCodeBindHash hash3 = new HashCodeBindHash(); + BindHash hash3 = new BindHash(); hash2.update(1).update(null); assertThat(hash).isNotEqualTo(hash2); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index cb1738ba8..999459892 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -113,9 +113,8 @@ public class DefaultOrmQueryTest extends BaseTestCase { } private int getHash(DefaultOrmQuery query) { - BindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); query.queryBindHash(hash); - hash.finish(); return hash.hashCode(); } } From ec7c7048db86a677e85d4d066cd76f77cd1cc23a Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 13:19:44 +1200 Subject: [PATCH 16/22] Refactor rename BindHash to BindValuesKey --- .../java/io/ebeaninternal/api/BindHash.java | 29 -------------- .../java/io/ebeaninternal/api/BindParams.java | 12 +++--- .../io/ebeaninternal/api/BindValuesKey.java | 35 +++++++++++++++++ .../java/io/ebeaninternal/api/HashQuery.java | 12 +++--- .../io/ebeaninternal/api/SpiExpression.java | 2 +- .../java/io/ebeaninternal/api/SpiQuery.java | 2 +- .../expression/AbstractTextExpression.java | 4 +- .../expression/AllEqualsExpression.java | 10 ++--- .../expression/ArrayContainsExpression.java | 8 ++-- .../expression/ArrayIsEmptyExpression.java | 6 +-- .../server/expression/BetweenExpression.java | 6 +-- .../expression/BetweenPropertyExpression.java | 6 +-- .../server/expression/BitwiseExpression.java | 6 +-- .../CaseInsensitiveEqualExpression.java | 6 +-- .../expression/DefaultExampleExpression.java | 10 ++--- .../expression/DefaultExpressionList.java | 10 ++--- .../expression/ExistsQueryExpression.java | 6 +-- .../server/expression/IdExpression.java | 6 +-- .../server/expression/IdInExpression.java | 8 ++-- .../server/expression/InExpression.java | 8 ++-- .../server/expression/InPairsExpression.java | 8 ++-- .../server/expression/InQueryExpression.java | 6 +-- .../server/expression/InRangeExpression.java | 6 +-- .../server/expression/IsEmptyExpression.java | 4 +- .../server/expression/JsonPathExpression.java | 6 +-- .../server/expression/JunctionExpression.java | 6 +-- .../server/expression/LikeExpression.java | 6 +-- .../server/expression/LogicExpression.java | 6 +-- .../expression/NativeILikeExpression.java | 6 +-- .../NestedPathWrapperExpression.java | 6 +-- .../server/expression/NoopExpression.java | 4 +- .../server/expression/NotExpression.java | 6 +-- .../server/expression/NullExpression.java | 6 +-- .../server/expression/RawExpression.java | 8 ++-- .../server/expression/SimpleExpression.java | 6 +-- .../server/querydefn/DefaultOrmQuery.java | 18 ++++----- .../server/expression/RawExpressionTest.java | 14 +++---- .../server/querydefn/BindHashTest.java | 38 ------------------- .../server/querydefn/BindValuesKeyTest.java | 38 +++++++++++++++++++ .../server/querydefn/DefaultOrmQueryTest.java | 16 ++++---- 40 files changed, 204 insertions(+), 202 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java delete mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java deleted file mode 100644 index b3ad8e49b..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.ebeaninternal.api; - -import java.util.ArrayList; -import java.util.List; - -/** - * BindHash implementation. - */ -public class BindHash { - - private final List values = new ArrayList<>(); - - public BindHash update(Object value) { - values.add(value); - return this; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof BindHash && ((BindHash) obj).values.equals(values); - } - - @Override - public int hashCode() { - return values.hashCode(); - } - - -} diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index 74e7dfa23..64a0a0287 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -50,10 +50,10 @@ public class BindParams implements Serializable { positionedParameters.clear(); } - public void queryBindHash(BindHash hash) { - hash.update(positionedParameters.size()); - for (Param positionedParameter : positionedParameters) { - positionedParameter.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + key.add(positionedParameters.size()); + for (Param param : positionedParameters) { + param.queryBindHash(key); } } @@ -425,8 +425,8 @@ public class BindParams implements Serializable { return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue); } - void queryBindHash(BindHash hash) { - hash.update(isInParam).update(isOutParam).update(type).update(inValue); + void queryBindHash(BindValuesKey key) { + key.add(isInParam).add(isOutParam).add(type).add(inValue); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java new file mode 100644 index 000000000..4a6eac324 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java @@ -0,0 +1,35 @@ +package io.ebeaninternal.api; + +import java.util.ArrayList; +import java.util.List; + +/** + * BindValues used for L2 query cache key matching. + *

+ * The equals/hashCode implementation must meet the requirement that the query bind values + * match for L2 query cache hit (given the query plan hash is already a match). + */ +public class BindValuesKey { + + private final List values = new ArrayList<>(); + + /** + * Add a bind value. + */ + public BindValuesKey add(Object value) { + values.add(value); + return this; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof BindValuesKey && ((BindValuesKey) obj).values.equals(values); + } + + @Override + public int hashCode() { + return values.hashCode(); + } + + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java index 87241aedc..faddb7bee 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java @@ -6,15 +6,14 @@ package io.ebeaninternal.api; public class HashQuery { private final CQueryPlanKey planHash; - - private final BindHash bindHash; + private final BindValuesKey bindValuesKey; /** * Create the HashQuery. */ - public HashQuery(CQueryPlanKey planHash, BindHash bindHash) { + public HashQuery(CQueryPlanKey planHash, BindValuesKey bindValuesKey) { this.planHash = planHash; - this.bindHash = bindHash; + this.bindValuesKey = bindValuesKey; } @Override @@ -25,7 +24,7 @@ public class HashQuery { @Override public int hashCode() { int hc = 92821 * planHash.hashCode(); - hc = 92821 * hc + bindHash.hashCode(); + hc = 92821 * hc + bindValuesKey.hashCode(); return hc; } @@ -37,8 +36,7 @@ public class HashQuery { if (!(obj instanceof HashQuery)) { return false; } - HashQuery e = (HashQuery) obj; - return e.bindHash.equals(bindHash) && e.planHash.equals(planHash); + return e.bindValuesKey.equals(bindValuesKey) && e.planHash.equals(planHash); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java index 32c3a3518..00879e15d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java @@ -56,7 +56,7 @@ public interface SpiExpression extends Expression { /** * Return the hash value for the values that will be bound. */ - void queryBindHash(BindHash hash); + void queryBindHash(BindValuesKey key); /** * Return true if the expression is the same with respect to bind values. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index d8bae7503..2eae4506b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -635,7 +635,7 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod * query). *

*/ - void queryBindHash(BindHash hash); + void queryBindHash(BindValuesKey key); /** * Identifies queries that are exactly the same including bind variables. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java index d377e48dd..2a411675e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -38,7 +38,7 @@ public abstract class AbstractTextExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { // do nothing, only execute against document store }; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java index df9e6095b..be0791a12 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -123,13 +123,11 @@ class AllEqualsExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - - hash.update(propMap.size()); + public void queryBindHash(BindValuesKey key) { + key.add(propMap.size()); for (Object value : propMap.values()) { - hash.update(value); + key.add(value); } - } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index 3c200c046..1ca15f8f7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -50,10 +50,10 @@ public class ArrayContainsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(values.length); + public void queryBindHash(BindValuesKey key) { + key.add(values.length); for (Object value : values) { - hash.update(value); + key.add(value); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java index 0249acabb..5b58be0b4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -34,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(empty); + public void queryBindHash(BindValuesKey key) { + key.add(empty); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java index 37a3aab8f..27e7a5909 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -50,8 +50,8 @@ class BetweenExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(low()).update(high()); + public void queryBindHash(BindValuesKey key) { + key.add(low()).add(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java index 440dd5f1f..cdc199d70 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -96,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(val()); + public void queryBindHash(BindValuesKey key) { + key.add(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java index db1fa686d..20119b370 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -40,8 +40,8 @@ class BitwiseExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(flags).update(match); + public void queryBindHash(BindValuesKey key) { + key.add(flags).add(match); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index 485d1b04f..ecefbe08a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -70,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(val()); + public void queryBindHash(BindValuesKey key) { + key.add(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index 9149923e0..3c0e268b6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -5,7 +5,7 @@ import io.ebean.LikeType; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; import io.ebean.util.SplitName; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -242,10 +242,10 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio * Return a hash for the actual bind values used. */ @Override - public void queryBindHash(BindHash hash) { - hash.update(list.size()); - for (SpiExpression aList : list) { - aList.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + key.add(list.size()); + for (SpiExpression expr : list) { + expr.queryBindHash(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index b5f9c6ec1..6c47467f2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -26,7 +26,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -679,10 +679,10 @@ public class DefaultExpressionList implements SpiExpressionList { * Calculate a hash based on the expressions. */ @Override - public void queryBindHash(BindHash hash) { - hash.update(list.size()); - for (SpiExpression aList : list) { - aList.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + key.add(list.size()); + for (SpiExpression expr : list) { + expr.queryBindHash(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index b3fcf8ce0..9f1d1830d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiEbeanServer; @@ -92,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress } @Override - public void queryBindHash(BindHash hash) { - subQuery.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + subQuery.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java index 904fbe9be..5c20e66b3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -78,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(value); + public void queryBindHash(BindValuesKey key) { + key.add(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java index 390d0bebb..8dbceb419 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -134,10 +134,10 @@ public class IdInExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(idCollection.size()); + public void queryBindHash(BindValuesKey key) { + key.add(idCollection.size()); for (Object elem : idCollection) { - hash.update(elem); + key.add(elem); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index a5719f71a..bbc2bf7eb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -177,10 +177,10 @@ class InExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(bindValues.size()); + public void queryBindHash(BindValuesKey key) { + key.add(bindValues.size()); for (Object bindValue : bindValues) { - hash.update(bindValue); + key.add(bindValue); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java index a7079ae55..6f2aae853 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Pairs; import io.ebean.Pairs.Entry; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -125,10 +125,10 @@ class InPairsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(entries.size()); + public void queryBindHash(BindValuesKey key) { + key.add(entries.size()); for (Pairs.Entry entry : entries) { - hash.update(entry.getA()).update(entry.getB()); + key.add(entry.getA()).add(entry.getB()); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 619f7b715..769f83b84 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -73,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor } @Override - public void queryBindHash(BindHash hash) { - subQuery.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + subQuery.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java index abe0b7338..c8a8dc96f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -48,8 +48,8 @@ class InRangeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(low()).update(high()); + public void queryBindHash(BindValuesKey key) { + key.add(low()).add(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java index bb65962b3..4ca943619 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -104,7 +104,7 @@ class IsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java index 18e0aa924..d501068d9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -84,8 +84,8 @@ class JsonPathExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(value).update(upperValue); + public void queryBindHash(BindValuesKey key) { + key.add(value).add(upperValue); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 88614873d..75c17ba19 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -25,7 +25,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -216,9 +216,9 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { for (SpiExpression expr : exprList.internalList()) { - expr.queryBindHash(hash); + expr.queryBindHash(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java index 587e6064c..355cdfa3c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -71,8 +71,8 @@ class LikeExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(strValue()); + public void queryBindHash(BindValuesKey key) { + key.add(strValue()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java index cbd487b3e..d6f7894e0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.Junction; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -169,8 +169,8 @@ abstract class LogicExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(expOne).update(expTwo); + public void queryBindHash(BindValuesKey key) { + key.add(expOne).add(expTwo); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java index 8c51d2f5d..efb77d37a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -55,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(val); + public void queryBindHash(BindValuesKey key) { + key.add(val); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java index f3ed1badd..0931486af 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -80,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - delegate.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + delegate.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java index 92180cea1..0a8a67f24 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -75,7 +75,7 @@ class NoopExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java index e9df6d856..4d6a4aba1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -100,8 +100,8 @@ final class NotExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - exp.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + exp.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index daded43cb..1ec123fda 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -95,7 +95,7 @@ class NullExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(notNull); + public void queryBindHash(BindValuesKey key) { + key.add(notNull); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index 721d69b25..080f541b7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -74,10 +74,10 @@ class RawExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(values.length); + public void queryBindHash(BindValuesKey key) { + key.add(values.length); for (Object value : values) { - hash.update(value); + key.add(value); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java index e50d9856c..3e0db8707 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java @@ -5,7 +5,7 @@ import io.ebean.plugin.ExpressionPath; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.NaturalKeyQueryData; import java.io.IOException; @@ -122,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(value()); + public void queryBindHash(BindValuesKey key) { + key.add(value()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 865b3184b..4508458b8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1225,12 +1225,12 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { *

*/ @Override - public void queryBindHash(BindHash hash) { - hash.update(id); - if (whereExpressions != null) whereExpressions.queryBindHash(hash); - if (havingExpressions != null) havingExpressions.queryBindHash(hash); - if (bindParams != null) bindParams.queryBindHash(hash); - hash.update(asOf).update(versionsStart).update(versionsEnd); + public void queryBindHash(BindValuesKey key) { + key.add(id); + if (whereExpressions != null) whereExpressions.queryBindHash(key); + if (havingExpressions != null) havingExpressions.queryBindHash(key); + if (bindParams != null) bindParams.queryBindHash(key); + key.add(asOf).add(versionsStart).add(versionsEnd); } /** @@ -1244,9 +1244,9 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - BindHash hash = new BindHash(); - queryBindHash(hash); - return new HashQuery(queryPlanKey, hash); + BindValuesKey bindKey = new BindValuesKey(); + queryBindHash(bindKey); + return new HashQuery(queryPlanKey, bindKey); } @Override diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 99c7d7eec..3568a3bf6 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.expression; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; public class RawExpressionTest extends BaseExpressionTest { @@ -62,16 +62,16 @@ public class RawExpressionTest extends BaseExpressionTest { } public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) { - assertThat(getHash(exp0)).isNotEqualTo(getHash(exp1)); + assertThat(bindKey(exp0)).isNotEqualTo(bindKey(exp1)); } public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) { - assertThat(getHash(exp0)).isEqualTo(getHash(exp1)); + assertThat(bindKey(exp0)).isEqualTo(bindKey(exp1)); } - private int getHash(RawExpression query) { - BindHash hash = new BindHash(); - query.queryBindHash(hash); - return hash.hashCode(); + private BindValuesKey bindKey(RawExpression query) { + BindValuesKey bindValuesKey = new BindValuesKey(); + query.queryBindHash(bindValuesKey); + return bindValuesKey; } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java deleted file mode 100644 index a74f4432f..000000000 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.ebeaninternal.server.querydefn; - -import io.ebeaninternal.api.BindHash; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class BindHashTest { - - @Test - public void update_with_null() { - - BindHash hash = new BindHash(); - hash.update(1).update(null).update("hello"); - - BindHash hash2 = new BindHash(); - hash2.update(1).update(null).update("hello"); - - assertThat(hash).isEqualTo(hash2); - } - - @Test - public void notEqual() { - - BindHash hash = new BindHash(); - hash.update(1).update(null).update("hello"); - - BindHash hash2 = new BindHash(); - hash2.update(1).update("hello"); - - BindHash hash3 = new BindHash(); - hash2.update(1).update(null); - - assertThat(hash).isNotEqualTo(hash2); - assertThat(hash).isNotEqualTo(hash3); - assertThat(hash2).isNotEqualTo(hash3); - } -} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java new file mode 100644 index 000000000..1a7d3b39f --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java @@ -0,0 +1,38 @@ +package io.ebeaninternal.server.querydefn; + +import io.ebeaninternal.api.BindValuesKey; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BindValuesKeyTest { + + @Test + public void update_with_null() { + + BindValuesKey hash = new BindValuesKey(); + hash.add(1).add(null).add("hello"); + + BindValuesKey hash2 = new BindValuesKey(); + hash2.add(1).add(null).add("hello"); + + assertThat(hash).isEqualTo(hash2); + } + + @Test + public void notEqual() { + + BindValuesKey hash = new BindValuesKey(); + hash.add(1).add(null).add("hello"); + + BindValuesKey hash2 = new BindValuesKey(); + hash2.add(1).add("hello"); + + BindValuesKey hash3 = new BindValuesKey(); + hash2.add(1).add(null); + + assertThat(hash).isNotEqualTo(hash2); + assertThat(hash).isNotEqualTo(hash3); + assertThat(hash2).isNotEqualTo(hash3); + } +} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index 999459892..b3b657c4a 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -4,7 +4,7 @@ package io.ebeaninternal.server.querydefn; import io.ebean.BaseTestCase; import io.ebean.CacheMode; import io.ebean.Ebean; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.core.OrmQueryRequest; import org.junit.Test; @@ -63,7 +63,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isNotEqualTo(q2.createQueryPlanKey()); - assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); + assertThat(bindKey(q1)).isNotEqualTo(bindKey(q2)); } @Test @@ -74,7 +74,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); + assertThat(bindKey(q1)).isNotEqualTo(bindKey(q2)); } @Test @@ -85,7 +85,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(getHash(q1)).isEqualTo(getHash(q2)); + assertThat(bindKey(q1)).isEqualTo(bindKey(q2)); } @Test @@ -112,9 +112,9 @@ public class DefaultOrmQueryTest extends BaseTestCase { q2.prepare(r2); } - private int getHash(DefaultOrmQuery query) { - BindHash hash = new BindHash(); - query.queryBindHash(hash); - return hash.hashCode(); + private BindValuesKey bindKey(DefaultOrmQuery query) { + BindValuesKey key = new BindValuesKey(); + query.queryBindHash(key); + return key; } } From 144a3b54bb51d804712a086a0e1921eceb1faf86 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 13:25:04 +1200 Subject: [PATCH 17/22] Refactor rename method queryBindHash() to queryBindKey() --- .../java/io/ebeaninternal/api/SpiExpression.java | 4 ++-- .../main/java/io/ebeaninternal/api/SpiQuery.java | 8 +++----- .../server/expression/AbstractTextExpression.java | 2 +- .../server/expression/AllEqualsExpression.java | 2 +- .../server/expression/ArrayContainsExpression.java | 2 +- .../server/expression/ArrayIsEmptyExpression.java | 2 +- .../server/expression/BetweenExpression.java | 2 +- .../expression/BetweenPropertyExpression.java | 2 +- .../server/expression/BitwiseExpression.java | 2 +- .../expression/CaseInsensitiveEqualExpression.java | 2 +- .../expression/DefaultExampleExpression.java | 7 ++----- .../server/expression/DefaultExpressionList.java | 7 ++----- .../server/expression/ExistsQueryExpression.java | 4 ++-- .../server/expression/IdExpression.java | 2 +- .../server/expression/IdInExpression.java | 2 +- .../server/expression/InExpression.java | 2 +- .../server/expression/InPairsExpression.java | 2 +- .../server/expression/InQueryExpression.java | 4 ++-- .../server/expression/InRangeExpression.java | 2 +- .../server/expression/IsEmptyExpression.java | 2 +- .../server/expression/JsonPathExpression.java | 2 +- .../server/expression/JunctionExpression.java | 4 ++-- .../server/expression/LikeExpression.java | 2 +- .../server/expression/LogicExpression.java | 2 +- .../server/expression/NativeILikeExpression.java | 2 +- .../expression/NestedPathWrapperExpression.java | 4 ++-- .../server/expression/NoopExpression.java | 2 +- .../server/expression/NotExpression.java | 4 ++-- .../server/expression/NullExpression.java | 2 +- .../server/expression/RawExpression.java | 2 +- .../server/expression/SimpleExpression.java | 2 +- .../server/querydefn/DefaultOrmQuery.java | 14 ++++---------- .../server/expression/RawExpressionTest.java | 2 +- .../server/querydefn/DefaultOrmQueryTest.java | 2 +- 34 files changed, 47 insertions(+), 61 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java index 00879e15d..8fe986259 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java @@ -54,9 +54,9 @@ public interface SpiExpression extends Expression { void queryPlanHash(StringBuilder builder); /** - * Return the hash value for the values that will be bound. + * Build the key for bind values of the query. */ - void queryBindHash(BindValuesKey key); + void queryBindKey(BindValuesKey key); /** * Return true if the expression is the same with respect to bind values. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 2eae4506b..5ba0b6eeb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -629,13 +629,11 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod CQueryPlanKey prepare(SpiOrmQueryRequest request); /** - * Calculate a hash based on the bind values used in the query. + * Build the key for the bind values used in the query (for l2 query cache). *

- * Combined with queryPlanHash() to return getQueryHash (a unique hash for a - * query). - *

+ * Combined with queryPlanHash() to return queryHash (a unique key for a query). */ - void queryBindHash(BindValuesKey key); + void queryBindKey(BindValuesKey key); /** * Identifies queries that are exactly the same including bind variables. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java index 2a411675e..363bcb08d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java @@ -38,7 +38,7 @@ public abstract class AbstractTextExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { // do nothing, only execute against document store }; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java index be0791a12..56761dcf2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java @@ -123,7 +123,7 @@ class AllEqualsExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(propMap.size()); for (Object value : propMap.values()) { key.add(value); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index 1ca15f8f7..7a9e903f6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -50,7 +50,7 @@ public class ArrayContainsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(values.length); for (Object value : values) { key.add(value); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java index 5b58be0b4..f9cd530c8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java @@ -34,7 +34,7 @@ public class ArrayIsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(empty); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java index 27e7a5909..ed3d58d8b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java @@ -50,7 +50,7 @@ class BetweenExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(low()).add(high()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java index cdc199d70..55b08380d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -96,7 +96,7 @@ class BetweenPropertyExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(val()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java index 20119b370..460af37ec 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java @@ -40,7 +40,7 @@ class BitwiseExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(flags).add(match); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index ecefbe08a..3a6224d9c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -70,7 +70,7 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(val()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index 3c0e268b6..e6353edd0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -238,14 +238,11 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio builder.append("]"); } - /** - * Return a hash for the actual bind values used. - */ @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(list.size()); for (SpiExpression expr : list) { - expr.queryBindHash(key); + expr.queryBindKey(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 6c47467f2..97899f091 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -675,14 +675,11 @@ public class DefaultExpressionList implements SpiExpressionList { builder.append("]"); } - /** - * Calculate a hash based on the expressions. - */ @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(list.size()); for (SpiExpression expr : list) { - expr.queryBindHash(key); + expr.queryBindKey(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index 9f1d1830d..4ba939066 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -92,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress } @Override - public void queryBindHash(BindValuesKey key) { - subQuery.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + subQuery.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java index 5c20e66b3..94cab6de7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java @@ -78,7 +78,7 @@ class IdExpression extends NonPrepareExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java index 8dbceb419..edf56b443 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java @@ -134,7 +134,7 @@ public class IdInExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(idCollection.size()); for (Object elem : idCollection) { key.add(elem); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index bbc2bf7eb..0bc5a4588 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -177,7 +177,7 @@ class InExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(bindValues.size()); for (Object bindValue : bindValues) { key.add(bindValue); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java index 6f2aae853..e5bd27413 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java @@ -125,7 +125,7 @@ class InPairsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(entries.size()); for (Pairs.Entry entry : entries) { key.add(entry.getA()).add(entry.getB()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 769f83b84..48647d0c2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -73,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor } @Override - public void queryBindHash(BindValuesKey key) { - subQuery.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + subQuery.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java index c8a8dc96f..04b242ba8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java @@ -48,7 +48,7 @@ class InRangeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(low()).add(high()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java index 4ca943619..d6b878bfa 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java @@ -104,7 +104,7 @@ class IsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java index d501068d9..1d954dcc1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java @@ -84,7 +84,7 @@ class JsonPathExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(value).add(upperValue); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 75c17ba19..a253bbb2b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -216,9 +216,9 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { for (SpiExpression expr : exprList.internalList()) { - expr.queryBindHash(key); + expr.queryBindKey(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java index 355cdfa3c..f40501929 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java @@ -71,7 +71,7 @@ class LikeExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(strValue()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java index d6f7894e0..a66e2e88a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java @@ -169,7 +169,7 @@ abstract class LogicExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(expOne).add(expTwo); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java index efb77d37a..f64933df5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java @@ -55,7 +55,7 @@ class NativeILikeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(val); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java index 0931486af..b76bfef4d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java @@ -80,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { - delegate.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + delegate.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java index 0a8a67f24..6c8d709e7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java @@ -75,7 +75,7 @@ class NoopExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java index 4d6a4aba1..462e60d9e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java @@ -100,8 +100,8 @@ final class NotExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { - exp.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + exp.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index 1ec123fda..2808acce7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -95,7 +95,7 @@ class NullExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(notNull); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index 080f541b7..9e1c3e8dc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -74,7 +74,7 @@ class RawExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(values.length); for (Object value : values) { key.add(value); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java index 3e0db8707..bc23b84f2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java @@ -122,7 +122,7 @@ public class SimpleExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(value()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 4508458b8..834abb856 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1218,17 +1218,11 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { } } - /** - * Calculate a hash based on the bind values used in the query. - *

- * Used with queryPlanHash() to get a unique hash for a query. - *

- */ @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(id); - if (whereExpressions != null) whereExpressions.queryBindHash(key); - if (havingExpressions != null) havingExpressions.queryBindHash(key); + if (whereExpressions != null) whereExpressions.queryBindKey(key); + if (havingExpressions != null) havingExpressions.queryBindKey(key); if (bindParams != null) bindParams.queryBindHash(key); key.add(asOf).add(versionsStart).add(versionsEnd); } @@ -1245,7 +1239,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called BindValuesKey bindKey = new BindValuesKey(); - queryBindHash(bindKey); + queryBindKey(bindKey); return new HashQuery(queryPlanKey, bindKey); } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 3568a3bf6..eb12ff460 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -71,7 +71,7 @@ public class RawExpressionTest extends BaseExpressionTest { private BindValuesKey bindKey(RawExpression query) { BindValuesKey bindValuesKey = new BindValuesKey(); - query.queryBindHash(bindValuesKey); + query.queryBindKey(bindValuesKey); return bindValuesKey; } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index b3b657c4a..b2b4902ad 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -114,7 +114,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { private BindValuesKey bindKey(DefaultOrmQuery query) { BindValuesKey key = new BindValuesKey(); - query.queryBindHash(key); + query.queryBindKey(key); return key; } } From c267366578b7877313f87569b503e77f331a6d8b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 15:32:54 +1200 Subject: [PATCH 18/22] Refactor tidy internals - aList -> expr in for loops etc. No functional change. --- .../io/ebeaninternal/api/LoadBeanRequest.java | 13 +------ .../server/core/DefaultServer.java | 12 +++--- .../deploy/meta/DeployBeanDescriptor.java | 6 +-- .../expression/DefaultExampleExpression.java | 16 +++----- .../expression/DefaultExpressionList.java | 38 ++++++++----------- .../server/expression/JunctionExpression.java | 5 +-- 6 files changed, 33 insertions(+), 57 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java b/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java index fef666a63..9d72463a5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java @@ -92,9 +92,7 @@ public class LoadBeanRequest extends LoadRequest { * Return the list of Id values for the beans in the lazy load buffer. */ public List getIdList() { - List idList = new ArrayList<>(); - BeanDescriptor desc = loadBuffer.getBeanDescriptor(); for (EntityBeanIntercept ebi : batch) { idList.add(desc.getId(ebi.getOwner())); @@ -106,10 +104,8 @@ public class LoadBeanRequest extends LoadRequest { * Configure the query for lazy loading execution. */ public void configureQuery(SpiQuery query, List idList) { - query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN); query.setPersistenceContext(loadBuffer.getPersistenceContext()); - String mode = isLazy() ? "+lazy" : "+query"; query.setLoadDescription(mode, getDescription()); @@ -117,9 +113,7 @@ public class LoadBeanRequest extends LoadRequest { // cascade the batch size (if set) for further lazy loading query.setLazyLoadBatchSize(getBatchSize()); } - loadBuffer.configureQuery(query, lazyLoadProperty); - if (idList.size() == 1) { query.where().idEq(idList.get(0)); } else { @@ -131,19 +125,16 @@ public class LoadBeanRequest extends LoadRequest { * Load the beans into the L2 cache if that is requested and check for load failures due to deletes. */ public void postLoad(List list) { - Set loadedIds = new HashSet<>(); - BeanDescriptor desc = loadBuffer.getBeanDescriptor(); // collect Ids and maybe load bean cache - for (Object aList : list) { - EntityBean loadedBean = (EntityBean) aList; + for (Object bean : list) { + EntityBean loadedBean = (EntityBean) bean; loadedIds.add(desc.getId(loadedBean)); } if (isLoadCache()) { desc.cacheBeanPutAll(list); } - if (lazyLoadProperty != null) { for (EntityBeanIntercept ebi : batch) { // check if the underlying row in DB was deleted. Mark the bean as 'failed' if diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 2040e3f78..4f0eed00b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -2055,17 +2055,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return transactionManager; } - public void register(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (BeanDescriptor aList : list) { - aList.register(c); + public void register(BeanPersistController controller) { + for (BeanDescriptor desc : beanDescriptorManager.getBeanDescriptorList()) { + desc.register(controller); } } public void deregister(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (BeanDescriptor aList : list) { - aList.deregister(c); + for (BeanDescriptor desc : beanDescriptorManager.getBeanDescriptorList()) { + desc.deregister(c); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 79ca0beb1..f0184948e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -702,13 +702,11 @@ public class DeployBeanDescriptor { } public void sortProperties() { - ArrayList list = new ArrayList<>(propMap.values()); list.sort(PROP_ORDER); - propMap = new LinkedHashMap<>(list.size()); - for (DeployBeanProperty aList : list) { - addBeanProperty(aList); + for (DeployBeanProperty property : list) { + addBeanProperty(property); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index e6353edd0..cd8a59857 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -137,10 +137,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio @Override public void containsMany(BeanDescriptor desc, ManyWhereJoins whereManyJoins) { list = buildExpressions(desc); - if (list != null) { - for (SpiExpression aList : list) { - aList.containsMany(desc, whereManyJoins); - } + for (SpiExpression expr : list) { + expr.containsMany(desc, whereManyJoins); } } @@ -187,8 +185,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio @Override public void validate(SpiExpressionValidation validation) { - for (SpiExpression aList : list) { - aList.validate(validation); + for (SpiExpression expr : list) { + expr.validate(validation); } } @@ -229,10 +227,9 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio */ @Override public void queryPlanHash(StringBuilder builder) { - builder.append("Example["); - for (SpiExpression aList : list) { - aList.queryPlanHash(builder); + for (SpiExpression expr : list) { + expr.queryPlanHash(builder); builder.append(","); } builder.append("]"); @@ -264,7 +261,6 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio * Build the List of expressions. */ private ArrayList buildExpressions(BeanDescriptor beanDescriptor) { - ArrayList list = new ArrayList<>(); addExpressions(list, beanDescriptor, entity, null); return list; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 97899f091..ed3f57e14 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -110,10 +110,8 @@ public class DefaultExpressionList implements SpiExpressionList { * @return A single SpiExpression that has the nestedPath set */ SpiExpression wrap(List list, String nestedPath, Junction.Type type) { - DefaultExpressionList wrapper = new DefaultExpressionList<>(query, expr, null, list, false); wrapper.setAllDocNested(nestedPath); - if (type != null) { return new JunctionExpression<>(type, wrapper); } else { @@ -122,15 +120,15 @@ public class DefaultExpressionList implements SpiExpressionList { } void simplifyEntries() { - for (SpiExpression element : list) { - element.simplify(); + for (SpiExpression expr : list) { + expr.simplify(); } } @Override public void prefixProperty(String path) { - for (SpiExpression exp : list) { - exp.prefixProperty(path); + for (SpiExpression expr : list) { + expr.prefixProperty(path); } } @@ -175,7 +173,6 @@ public class DefaultExpressionList implements SpiExpressionList { context.startNested(allDocNestedPath); } int size = list.size(); - SpiExpression first = list.get(0); boolean explicitBool = first instanceof SpiJunction; boolean implicitBool = !explicitBool && size > 1; @@ -211,7 +208,6 @@ public class DefaultExpressionList implements SpiExpressionList { @Override public void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException { - if (allDocNestedPath != null) { context.startNested(allDocNestedPath); } @@ -228,8 +224,8 @@ public class DefaultExpressionList implements SpiExpressionList { if (idEquals != null) { idEquals.writeDocQuery(context); } - for (SpiExpression aList : list) { - aList.writeDocQuery(context); + for (SpiExpression expr : list) { + expr.writeDocQuery(context); } context.endBool(); } @@ -279,16 +275,15 @@ public class DefaultExpressionList implements SpiExpressionList { */ @Override public void containsMany(BeanDescriptor desc, ManyWhereJoins whereManyJoins) { - - for (SpiExpression aList : list) { - aList.containsMany(desc, whereManyJoins); + for (SpiExpression expr : list) { + expr.containsMany(desc, whereManyJoins); } } @Override public void validate(SpiExpressionValidation validation) { - for (SpiExpression aList : list) { - aList.validate(validation); + for (SpiExpression expr : list) { + expr.validate(validation); } } @@ -631,7 +626,6 @@ public class DefaultExpressionList implements SpiExpressionList { @Override public void addSql(SpiExpressionRequest request) { - for (int i = 0, size = list.size(); i < size; i++) { SpiExpression expression = list.get(i); if (i > 0) { @@ -643,15 +637,15 @@ public class DefaultExpressionList implements SpiExpressionList { @Override public void addBindValues(SpiExpressionRequest request) { - for (SpiExpression aList : list) { - aList.addBindValues(request); + for (SpiExpression expr : list) { + expr.addBindValues(request); } } @Override public void prepareExpression(BeanQueryRequest request) { - for (SpiExpression aList : list) { - aList.prepareExpression(request); + for (SpiExpression expr : list) { + expr.prepareExpression(request); } } @@ -668,8 +662,8 @@ public class DefaultExpressionList implements SpiExpressionList { if (allDocNestedPath != null) { builder.append("path:").append(allDocNestedPath).append(" "); } - for (SpiExpression aList : list) { - aList.queryPlanHash(builder); + for (SpiExpression expr : list) { + expr.queryPlanHash(builder); builder.append(","); } builder.append("]"); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index a253bbb2b..5637fba7f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -207,9 +207,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void queryPlanHash(StringBuilder builder) { builder.append(type).append("["); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.queryPlanHash(builder); + for (SpiExpression expr : exprList.internalList()) { + expr.queryPlanHash(builder); builder.append(","); } builder.append("]"); From f50f0192d155bd1cb71b1a5b369a481d75fffe32 Mon Sep 17 00:00:00 2001 From: Thomas Fellner Date: Wed, 11 Aug 2021 08:47:49 +0200 Subject: [PATCH 19/22] Switch DB2 driver to the publicly available JCC driver --- ebean-core/pom.xml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index b99bf98fb..5324aec82 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -20,22 +20,13 @@ - db2 + - com.ibm.jdbc - db2jcc4 - 4.23.42 + com.ibm.db2 + jcc + 11.5.5.0 test From 1a2aba3f56df9c4d58e3e18f1498693492115b66 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 11 Aug 2021 08:54:40 +0200 Subject: [PATCH 20/22] Distinguish between exists and in subquery --- .../server/autotune/service/BaseQueryTuner.java | 3 ++- .../main/java/io/ebeaninternal/api/SpiEbeanServer.java | 3 ++- .../src/main/java/io/ebeaninternal/api/SpiQuery.java | 9 +++++++-- .../io/ebeaninternal/server/core/DefaultServer.java | 4 ++-- .../server/expression/ExistsQueryExpression.java | 3 ++- .../server/expression/InQueryExpression.java | 3 ++- .../java/io/ebeaninternal/server/query/SqlTree.java | 1 + .../io/ebeaninternal/server/query/SqlTreeBuilder.java | 10 +++++++++- .../server/querydefn/DefaultOrmQuery.java | 5 +++-- .../java/io/ebeaninternal/api/TDSpiEbeanServer.java | 6 +++--- .../org/tests/query/cancel/SqlQueryCancelTest.java | 8 ++++---- 11 files changed, 37 insertions(+), 18 deletions(-) diff --git a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java index af969fd53..b93be5cc8 100644 --- a/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java +++ b/ebean-autotune/src/main/java/io/ebeaninternal/server/autotune/service/BaseQueryTuner.java @@ -146,7 +146,8 @@ public class BaseQueryTuner { case ID_LIST: case UPDATE: case DELETE: - case SUBQUERY: + case SQ_EXISTS: + case SQ_IN: return false; default: // not using autoTune when explicitly loading the l2 bean cache diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java index daf6418d0..fae60f49d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java @@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.meta.MetricVisitor; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.core.SpiResultSet; import io.ebeaninternal.server.core.timezone.DataTimeZone; import io.ebeaninternal.server.deploy.BeanDescriptor; @@ -149,7 +150,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect /** * Compile a query. */ - CQuery compileQuery(Query query, Transaction t); + CQuery compileQuery(Type type, Query query, Transaction t); /** * Execute the findId's query but without copying the query. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 5ba0b6eeb..12a25fed8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -92,9 +92,14 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod COUNT(FIND_COUNT, "findCount"), /** - * A subquery used as part of a where clause. + * A subquery used as part of an exists where clause. */ - SUBQUERY(FIND_SUBQUERY, "subquery"), + SQ_EXISTS(FIND_SUBQUERY, "sqExists"), + + /** + * A subquery used as part of an in where clause. + */ + SQ_IN(FIND_SUBQUERY, "sqIn"), /** * Delete query. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 4f0eed00b..c52befdcc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -526,8 +526,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { * Compile a query. Only valid for ORM queries. */ @Override - public CQuery compileQuery(Query query, Transaction t) { - SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t); + public CQuery compileQuery(Type type, Query query, Transaction t) { + SpiOrmQueryRequest qr = createQueryRequest(type, query, t); OrmQueryRequest orm = (OrmQueryRequest) qr; return cqueryEngine.buildQuery(orm); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index 4ba939066..71ca665fe 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -9,6 +9,7 @@ import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiExpressionValidation; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.query.CQuery; @@ -82,7 +83,7 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress */ protected CQuery compileSubQuery(BeanQueryRequest queryRequest) { SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer(); - return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction()); + return ebeanServer.compileQuery(Type.SQ_EXISTS, subQuery, queryRequest.getTransaction()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 48647d0c2..a2d6d787c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -6,6 +6,7 @@ import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.query.CQuery; import java.io.IOException; @@ -69,7 +70,7 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor private CQuery compileSubQuery(BeanQueryRequest queryRequest) { SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer(); - return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction()); + return ebeanServer.compileQuery(Type.SQ_IN, subQuery, queryRequest.getTransaction()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java index 1befa650b..5f548fd59 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java @@ -101,6 +101,7 @@ class SqlTree { * Return the String for the actual SQL. */ String getSelectSql() { + assert selectSql != null : "selectSql was null"; return selectSql; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 06465355c..a344244c4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -102,7 +102,11 @@ public final class SqlTreeBuilder { this.query = request.getQuery(); this.temporalMode = SpiQuery.TemporalMode.of(query); this.disableLazyLoad = query.isDisableLazyLoading(); - this.subQuery = Type.SUBQUERY == query.getType() || Type.ID_LIST == query.getType() || Type.DELETE == query.getType() || query.isCountDistinct(); + this.subQuery = Type.SQ_EXISTS == query.getType() + || Type.SQ_IN == query.getType() + || Type.ID_LIST == query.getType() + || Type.DELETE == query.getType() + || query.isCountDistinct(); this.includeJoin = query.getM2mIncludeJoin(); this.manyWhereJoins = query.getManyWhereJoins(); this.queryDetail = query.getDetail(); @@ -146,6 +150,10 @@ public final class SqlTreeBuilder { if (rawSql) { return "Not Used"; } + if (query.getType() ==Type.SQ_EXISTS) { + // effective query is "where exists (select 1 from ...)" + return "1"; + } rootNode.appendSelect(ctx, subQuery); return trimComma(ctx.getContent()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index db3a605ea..dea84fb80 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -565,7 +565,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { * Limit the number of fetch joins to Many properties, mark as query joins as needed. */ private void markQueryJoins() { - detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type != Type.ATTRIBUTE && type != Type.SUBQUERY); + detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), + type != Type.ATTRIBUTE && type != Type.SQ_EXISTS && type != Type.SQ_IN); } private boolean isAllowOneManyFetch() { @@ -578,7 +579,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { @Override public void setDefaultSelectClause() { - if (type != Type.ATTRIBUTE && type != Type.SUBQUERY) { + if (type != Type.ATTRIBUTE && type != Type.SQ_EXISTS && type != Type.SQ_IN) { detail.setDefaultSelectClause(beanDescriptor); } else if (!detail.hasSelectClause()) { // explicit empty select when single attribute query on non-root fetch path diff --git a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java index b09a0f54e..cb7e613a3 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java +++ b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java @@ -45,6 +45,7 @@ import io.ebean.plugin.Property; import io.ebean.plugin.SpiServer; import io.ebean.text.csv.CsvReader; import io.ebean.text.json.JsonContext; +import io.ebeaninternal.api.SpiQuery.Type; import io.ebeaninternal.server.core.SpiResultSet; import io.ebeaninternal.server.core.timezone.DataTimeZone; import io.ebeaninternal.server.deploy.BeanDescriptor; @@ -54,7 +55,6 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent; import javax.persistence.OptimisticLockException; import javax.persistence.PersistenceException; import javax.sql.DataSource; -import java.lang.reflect.Type; import java.time.Clock; import java.util.Collection; import java.util.Collections; @@ -256,7 +256,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public CQuery compileQuery(Query query, Transaction t) { + public CQuery compileQuery(Type type, Query query, Transaction t) { return null; } @@ -316,7 +316,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public boolean isSupportedType(Type genericType) { + public boolean isSupportedType(java.lang.reflect.Type genericType) { return false; } diff --git a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java index 9fc3d6b57..5fedc53e0 100644 --- a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java +++ b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java @@ -113,8 +113,8 @@ public class SqlQueryCancelTest extends BaseTestCase { doCancelOrmAtBegin(Query::findOne); doCancelOrmAtBegin(q -> q.setMaxRows(1000).findPagedList().getList()); // untested doCancelOrmAtBegin(Query::findSet); - doCancelOrmAtBegin(Query::findSingleAttribute); - doCancelOrmAtBegin(Query::findSingleAttributeList); + doCancelOrmAtBegin(q -> q.select("name").findSingleAttribute()); + doCancelOrmAtBegin(q -> q.select("name").findSingleAttributeList()); doCancelOrmAtBegin(Query::findStream); // testDuringRun(Query::findVersions); // EBasic has no history support, but it should work if @History is added @@ -138,8 +138,8 @@ public class SqlQueryCancelTest extends BaseTestCase { // findOne cannot be tested, as H2 does the cancel check every 128 rows only doCancelOrmDuringRun(q -> q.setMaxRows(1000).findPagedList().getList()); // untested doCancelOrmDuringRun(Query::findSet); - doCancelOrmDuringRun(Query::findSingleAttribute); - doCancelOrmDuringRun(Query::findSingleAttributeList); + doCancelOrmDuringRun(q -> q.select("name").findSingleAttribute()); + doCancelOrmDuringRun(q -> q.select("name").findSingleAttributeList()); doCancelOrmDuringRun(Query::findStream); // testDuringRun(Query::findVersions); // EBasic has no history support, but it should work if @History is added From 2d240202022ab1e00033aebdd0f278d401aee4a0 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 11 Aug 2021 09:32:21 +0200 Subject: [PATCH 21/22] Added / enhanced test case --- .../server/query/SqlTreeBuilder.java | 2 +- .../java/org/tests/query/TestQueryExists.java | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index a344244c4..65fa4f7f8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -150,7 +150,7 @@ public final class SqlTreeBuilder { if (rawSql) { return "Not Used"; } - if (query.getType() ==Type.SQ_EXISTS) { + if (query.getType() == Type.SQ_EXISTS) { // effective query is "where exists (select 1 from ...)" return "1"; } diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryExists.java b/ebean-core/src/test/java/org/tests/query/TestQueryExists.java index 93155bf9a..f885852b7 100644 --- a/ebean-core/src/test/java/org/tests/query/TestQueryExists.java +++ b/ebean-core/src/test/java/org/tests/query/TestQueryExists.java @@ -1,7 +1,7 @@ package org.tests.query; import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import io.ebean.DB; import io.ebean.Query; import io.ebeantest.LoggedSql; @@ -21,7 +21,7 @@ public class TestQueryExists extends BaseTestCase { ResetBasicData.reset(); - Query query = Ebean.find(Order.class) + Query query = DB.find(Order.class) .where().gt("id", 1) .query(); @@ -34,8 +34,8 @@ public class TestQueryExists extends BaseTestCase { assertThat(sql).contains("select t0.id from o_order t0 where t0.id > ? limit 1"); } - assertThat(Ebean.find(Order.class).where().gt("id", 1).exists()).isTrue(); - assertThat(Ebean.find(Order.class).where().or().gt("id", 1).isNull("shipDate").exists()).isTrue(); + assertThat(DB.find(Order.class).where().gt("id", 1).exists()).isTrue(); + assertThat(DB.find(Order.class).where().or().gt("id", 1).isNull("shipDate").exists()).isTrue(); } @Test @@ -43,13 +43,13 @@ public class TestQueryExists extends BaseTestCase { ResetBasicData.reset(); - Query query = Ebean.find(Order.class) + Query query = DB.find(Order.class) .where().raw("exists (select 1 from o_order_detail where order_id = t0.id)") .query(); List ordersThatHave = query.findList(); - Query query2 = Ebean.find(Order.class) + Query query2 = DB.find(Order.class) .where().raw("not exists (select 1 from o_order_detail where order_id = t0.id)") .query(); @@ -67,13 +67,13 @@ public class TestQueryExists extends BaseTestCase { ResetBasicData.reset(); - Query query = Ebean.find(Customer.class) + Query query = DB.find(Customer.class) .where().raw("exists (select 1 from contact where customer_id = t0.id)") .query(); List customersWithContacts = query.findList(); - Query query2 = Ebean.find(Customer.class) + Query query2 = DB.find(Customer.class) .where().raw("not exists (select 1 FROM contact where customer_id = t0.id)") .query(); @@ -89,26 +89,26 @@ public class TestQueryExists extends BaseTestCase { public void testExists() { ResetBasicData.reset(); - Query subQuery = Ebean.find(Order.class).alias("sq").select("id").where().raw("sq.kcustomer_id = qt.id").query(); + Query subQuery = DB.find(Order.class).alias("sq").select("id").where().raw("sq.kcustomer_id = qt.id").query(); - Query query = Ebean.find(Customer.class).alias("qt").where().exists(subQuery).query(); + Query query = DB.find(Customer.class).alias("qt").where().exists(subQuery).query(); query.findList(); String sql = query.getGeneratedSql(); - assertThat(sql).contains("exists ("); + assertThat(sql).contains("exists (select 1 from"); } @Test public void testNotExists() { ResetBasicData.reset(); - Query subQuery = Ebean.find(Order.class).alias("sq").select("id").where().raw("sq.kcustomer_id = qt.id").query(); - Query query = Ebean.find(Customer.class).alias("qt").where().notExists(subQuery).query(); + Query subQuery = DB.find(Order.class).alias("sq").where().raw("sq.kcustomer_id = qt.id").query(); + Query query = DB.find(Customer.class).alias("qt").where().notExists(subQuery).query(); query.findList(); String sql = query.getGeneratedSql(); - assertThat(sql).contains("not exists ("); + assertThat(sql).contains("not exists (select 1 from"); } } From b676476cd257b70f74f60419a093bf3d1972956b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 20:51:46 +1200 Subject: [PATCH 22/22] Add SpiQuery.Type.defaultSelect() for ATTRIBUTE and sub-query type queries --- .../java/io/ebeaninternal/api/SpiQuery.java | 21 +++++++++---- .../server/query/CQueryBuilder.java | 30 ------------------- .../server/querydefn/DefaultOrmQuery.java | 5 ++-- 3 files changed, 18 insertions(+), 38 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 12a25fed8..5a6ae2217 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -84,7 +84,7 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod /** * Find single attribute. */ - ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"), + ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute", false, false), /** * Find rowCount. @@ -94,12 +94,12 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod /** * A subquery used as part of an exists where clause. */ - SQ_EXISTS(FIND_SUBQUERY, "sqExists"), + SQ_EXISTS(FIND_SUBQUERY, "sqExists", false, false), /** * A subquery used as part of an in where clause. */ - SQ_IN(FIND_SUBQUERY, "sqIn"), + SQ_IN(FIND_SUBQUERY, "sqIn", false, false), /** * Delete query. @@ -112,17 +112,21 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod UPDATE(FIND_UPDATE, "update", true); private final boolean update; + private final boolean defaultSelect; private final String profileEventId; private final String label; Type(String profileEventId, String label) { - this(profileEventId, label, false); + this(profileEventId, label, false, true); } - Type(String profileEventId, String label, boolean update) { + this(profileEventId, label, update, true); + } + Type(String profileEventId, String label, boolean update, boolean defaultSelect) { this.profileEventId = profileEventId; this.label = label; this.update = update; + this.defaultSelect = defaultSelect; } /** @@ -132,6 +136,13 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod return update; } + /** + * Return true if this allows default select clause. + */ + public boolean defaultSelect() { + return defaultSelect; + } + public String profileEventId() { return profileEventId; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 41439e4b6..9a4b1d32e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -46,9 +46,7 @@ class CQueryBuilder { private final SqlLimiter sqlLimiter; private final CQueryBuilderRawSql rawSqlHandler; private final Binder binder; - private final boolean selectCountWithAlias; - private final CQueryHistorySupport historySupport; private final CQueryDraftSupport draftSupport; private final DatabasePlatform dbPlatform; @@ -79,7 +77,6 @@ class CQueryBuilder { if (sb.length() > 0) { sb.append(", "); } - sb.append(name); sb.append("."); sb.append(token.trim()); @@ -91,7 +88,6 @@ class CQueryBuilder { * Build the delete query. */ CQueryUpdate buildUpdateQuery(boolean deleteRequest, OrmQueryRequest request) { - SpiQuery query = request.getQuery(); String rootTableAlias = query.getAlias(); query.setupForDeleteOrUpdate(); @@ -105,7 +101,6 @@ class CQueryBuilder { } predicates.prepare(true); - SqlTree sqlTree = createSqlTree(request, predicates); String sql; @@ -114,7 +109,6 @@ class CQueryBuilder { } else { sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree); } - // cache the query plan queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); @@ -122,7 +116,6 @@ class CQueryBuilder { } private String buildDeleteSql(OrmQueryRequest request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) { - String alias = alias(rootTableAlias); if (sqlTree.noJoins() && !request.getQuery().hasMaxRowsOrFirstRow()) { if (dbPlatform.isSupportsDeleteTableAlias()) { @@ -151,7 +144,6 @@ class CQueryBuilder { } private String buildUpdateSql(OrmQueryRequest request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) { - StringBuilder sb = new StringBuilder(200); sb.append("update ").append(request.getBeanDescriptor().getBaseTable()); if (rootTableAlias != null) { @@ -159,7 +151,6 @@ class CQueryBuilder { } sb.append(" set ").append(predicates.getDbUpdateClause()); String updateClause = sb.toString(); - if (sqlTree.noJoins() && request.isInlineSqlUpdateLimit()) { // simple - update table set ... where ... return aliasStrip(buildSqlUpdate(updateClause, request, predicates, sqlTree).getSql()); @@ -186,7 +177,6 @@ class CQueryBuilder { } CQueryFetchSingleAttribute buildFetchAttributeQuery(OrmQueryRequest request) { - SpiQuery query = request.getQuery(); query.setSingleAttribute(); if (!query.isIncludeSoftDeletes()) { @@ -218,7 +208,6 @@ class CQueryBuilder { * Build the find ids query. */ CQueryFetchSingleAttribute buildFetchIdsQuery(OrmQueryRequest request) { - SpiQuery query = request.getQuery(); query.setSelectId(); BeanDescriptor desc = request.getBeanDescriptor(); @@ -246,9 +235,7 @@ class CQueryBuilder { * Build the row count query. */ CQueryRowCount buildRowCountQuery(OrmQueryRequest request) { - SpiQuery query = request.getQuery(); - // always set the order by to null for row count query query.setOrder(null); query.setFirstRow(0); @@ -310,7 +297,6 @@ class CQueryBuilder { // cache the query plan queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); - return new CQueryRowCount(queryPlan, request, predicates); } @@ -334,9 +320,7 @@ class CQueryBuilder { * names to physical deployment column names. */ CQuery buildQuery(OrmQueryRequest request) { - CQueryPredicates predicates = new CQueryPredicates(binder, request); - CQueryPlan queryPlan = request.getQueryPlan(); if (queryPlan != null) { // Reuse the query plan so skip generating SqlTree and SQL. @@ -370,7 +354,6 @@ class CQueryBuilder { boolean rawSql = request.isRawSql(); if (rawSql) { queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql()); - } else { queryPlan = new CQueryPlan(request, res, sqlTree, false, predicates.getLogWhereSql()); } @@ -380,11 +363,9 @@ class CQueryBuilder { // log the query plan based bean type (i.e. ignoring query disabling for logging the sql/plan) desc.getReadAuditLogger().queryPlan(new ReadAuditQueryPlan(desc.getFullName(), queryPlan.getAuditQueryKey(), queryPlan.getSql())); } - // cache the query plan because we can reuse it and also // gather query performance statistics based on it. request.putQueryPlan(queryPlan); - return new CQuery<>(request, predicates, queryPlan); } @@ -393,18 +374,15 @@ class CQueryBuilder { *

* The SqlTree is immutable after construction and so is safe to use by * concurrent threads. - *

*

* The predicates is used to add additional joins that come from the where or * order by clauses that are not already included for the select clause. - *

*/ private SqlTree createSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { return createSqlTree(request, predicates, false); } private SqlTree createSqlTree(OrmQueryRequest request, CQueryPredicates predicates, boolean forceColumnAlias) { - if (request.isNativeSql()) { return createNativeSqlTree(request, predicates); } @@ -423,9 +401,7 @@ class CQueryBuilder { * Create the SqlTree by reading the ResultSetMetaData and mapping table/columns to bean property paths. */ private SqlTree createNativeSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { - SpiQuery query = request.getQuery(); - // parse named parameters returning the final sql to execute String sql = predicates.parseBindParams(query.getNativeSql()); if (query.hasMaxRowsOrFirstRow()) { @@ -469,10 +445,8 @@ class CQueryBuilder { } private SqlTree createRawSqlSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { - BeanDescriptor descriptor = request.getBeanDescriptor(); ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping(); - PathProperties pathProps = new PathProperties(); // convert list of columns into (tree like) PathProperties @@ -498,12 +472,10 @@ class CQueryBuilder { } OrmQueryDetail detail = new OrmQueryDetail(); - // transfer PathProperties into OrmQueryDetail for (PathProperties.Props props : pathProps.getPathProps()) { detail.fetch(props.getPath(), props.getProperties()); } - // check if @Id property included in RawSql boolean rawNoId = true; BeanProperty idProperty = descriptor.getIdProperty(); @@ -511,7 +483,6 @@ class CQueryBuilder { // contains the @Id property for the root level bean rawNoId = false; } - // build SqlTree based on OrmQueryDetail of the RawSql return new SqlTreeBuilder(request, predicates, detail, rawNoId).build(); } @@ -603,7 +574,6 @@ class CQueryBuilder { private void appendSelect() { if (selectClause != null) { sb.append(selectClause); - } else { useSqlLimiter = (query.hasMaxRowsOrFirstRow() && select.getManyProperty() == null); if (!useSqlLimiter) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index dea84fb80..b5aeec323 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -565,8 +565,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { * Limit the number of fetch joins to Many properties, mark as query joins as needed. */ private void markQueryJoins() { - detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), - type != Type.ATTRIBUTE && type != Type.SQ_EXISTS && type != Type.SQ_IN); + detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type.defaultSelect()); } private boolean isAllowOneManyFetch() { @@ -579,7 +578,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { @Override public void setDefaultSelectClause() { - if (type != Type.ATTRIBUTE && type != Type.SQ_EXISTS && type != Type.SQ_IN) { + if (type.defaultSelect()) { detail.setDefaultSelectClause(beanDescriptor); } else if (!detail.hasSelectClause()) { // explicit empty select when single attribute query on non-root fetch path