Use MD5 for QueryBindHash

This commit is contained in:
Roland Praml
2021-08-09 09:19:53 +02:00
parent 7ed2de9178
commit 181dc73d39
40 changed files with 436 additions and 137 deletions
@@ -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();
}
@@ -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());
@@ -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);
}
}
@@ -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.
@@ -635,7 +635,7 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
* query).
* </p>
*/
int queryBindHash();
void queryBindHash(BindHash hash);
/**
* Identifies queries that are exactly the same including bind variables.
@@ -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) {
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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<T> implements SpiExpressionList<T> {
* 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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<T> implements SpiJunction<T>, SpiExpression, Expression
}
@Override
public int queryBindHash() {
public void queryBindHash(BindHash hash) {
int hc = JunctionExpression.class.getName().hashCode();
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
hc = hc * 92821 + aList.queryBindHash();
aList.queryBindHash(hash);
}
return hc;
}
@Override
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
}
}
@@ -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
@@ -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
@@ -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<T> extends AbstractQuery implements SpiQuery<T> {
private boolean orderById;
private final String bindHashAlgorithm;
private ProfileLocation profileLocation;
public DefaultOrmQuery(BeanDescriptor<T> desc, SpiEbeanServer server, ExpressionFactory expressionFactory) {
@@ -291,6 +295,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
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<T> extends AbstractQuery implements SpiQuery<T> {
* </p>
*/
@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<T> extends AbstractQuery implements SpiQuery<T> {
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
@@ -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;
}
}
@@ -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;
}
}