dirtyValues, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if (isChangedProp(i)) {
// the property has been changed on this bean
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
Object newVal = owner._ebean_getField(i);
@@ -706,7 +747,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if (isChangedProp(i)) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
@@ -741,7 +782,7 @@ public final class EntityBeanIntercept implements Serializable {
}
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
- if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // we do not check against mutablecontent here.
sb.append(i).append(',');
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
@@ -1112,7 +1153,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
public void setLoadError(int propertyIndex, Exception t) {
if (loadErrors == null) {
- loadErrors = new Exception[owner._ebean_getPropertyNames().length];
+ loadErrors = new Exception[flags.length];
}
loadErrors[propertyIndex] = t;
flags[propertyIndex] |= FLAG_LOADED_PROP;
@@ -1138,4 +1179,60 @@ public final class EntityBeanIntercept implements Serializable {
}
return ret;
}
+
+ private boolean isChangedProp(int i) {
+ if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
+ return true;
+ } else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
+ return false;
+ } else {
+ // mark for change
+ flags[i] |= FLAG_CHANGED_PROP;
+ dirty = true; // this makes the bean automatically dirty!
+ return true;
+ }
+ }
+
+ /**
+ * Return the MutableValueInfo for the given property or null.
+ */
+ public MutableValueInfo mutableInfo(int propertyIndex) {
+ return mutableInfo == null ? null : mutableInfo[propertyIndex];
+ }
+
+ /**
+ * Set the MutableValueInfo for the given property.
+ */
+ public void mutableInfo(int propertyIndex, MutableValueInfo info) {
+ if (mutableInfo == null) {
+ mutableInfo = new MutableValueInfo[flags.length];
+ }
+ flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET;
+ mutableInfo[propertyIndex] = info;
+ }
+
+ /**
+ * Dirty detection set the next mutable property content and info .
+ *
+ * Set here as the mutable property dirty detection is based on json content comparison.
+ * We only want to perform the json serialisation once so storing it here as part of
+ * dirty detection so that we can get it back to bind in insert or update etc.
+ */
+ public void mutableNext(int propertyIndex, MutableValueNext next) {
+ if (mutableNext == null) {
+ mutableNext = new MutableValueNext[flags.length];
+ }
+ mutableNext[propertyIndex] = next;
+ }
+
+ /**
+ * Update the 'next' mutable info returning the content that was obtained via dirty detection.
+ */
+ public String mutableNext(int propertyIndex) {
+ if (mutableNext == null) {
+ return null;
+ }
+ return mutableNext[propertyIndex].content();
+ }
+
}
diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java
new file mode 100644
index 000000000..27716b13c
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java
@@ -0,0 +1,42 @@
+package io.ebean.bean;
+
+/**
+ * Holds information on mutable values (like plain beans stored as json).
+ *
+ * Used internally in EntityBeanIntercept for dirty detection on mutable values.
+ * Typically, mutation detection is based on a hash/checksum of json content or the
+ * original json content itself.
+ *
+ * Refer to the mapping options {@code @DbJson(mutationDetection)}.
+ */
+public interface MutableValueInfo {
+
+ /**
+ * Compares the given json returning null if deemed unchanged or returning
+ * the MutableValueNext to use if deemed dirty/changed.
+ *
+ * Returning MutableValueNext allows an implementation based on hash/checksum
+ * to only perform that computation once.
+ *
+ * @return Null if deemed unchanged or the MutableValueNext if deemed changed.
+ */
+ MutableValueNext nextDirty(String json);
+
+ /**
+ * Compares the given object to an internal value.
+ *
+ * This is used to support changelog/beanState. The implementation can serialize the
+ * object into json form and compare it against the original json.
+ */
+ boolean isEqualToObject(Object obj);
+
+ /**
+ * Creates a new instance from the internal json string.
+ *
+ * This is used to provide an original/old value for change logging / persist listeners.
+ * This is only available for properties that have {@code @DbJson(keepSource=true)}.
+ */
+ default Object get() {
+ return null;
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java
new file mode 100644
index 000000000..401d3c223
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java
@@ -0,0 +1,17 @@
+package io.ebean.bean;
+
+/**
+ * Represents a next value to use for mutable content properties (DbJson with jackson beans).
+ */
+public interface MutableValueNext {
+
+ /**
+ * Return the next content to use. Provided such that we serialise to json once.
+ */
+ String content();
+
+ /**
+ * Return the next MutableValueInfo to use after an update.
+ */
+ MutableValueInfo info();
+}
diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java b/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java
index 6da14d3a5..957b0fa88 100644
--- a/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java
+++ b/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java
@@ -3,9 +3,7 @@ package io.ebean.common;
import io.ebean.bean.EntityBean;
import java.io.Serializable;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
+import java.util.*;
/**
* Holds sets of additions and deletions from a 'owner' List Set or Map.
@@ -23,19 +21,19 @@ class ModifyHolder implements Serializable {
/**
* Deletions list for manyToMany persistence.
*/
- private Set modifyDeletions = new LinkedHashSet<>();
+ private Map modifyDeletions = new IdentityHashMap<>();
/**
* Additions list for manyToMany persistence.
*/
- private Set modifyAdditions = new LinkedHashSet<>();
+ private Map modifyAdditions = new IdentityHashMap<>();
private boolean touched;
void reset() {
touched = false;
- modifyDeletions = new LinkedHashSet<>();
- modifyAdditions = new LinkedHashSet<>();
+ modifyDeletions = new IdentityHashMap<>();
+ modifyAdditions = new IdentityHashMap<>();
}
/**
@@ -50,51 +48,46 @@ class ModifyHolder implements Serializable {
}
private boolean undoDeletion(E bean) {
- return (bean != null) && modifyDeletions.remove(bean);
+ return (bean != null) && modifyDeletions.remove(bean) != null;
}
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
-
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
}
-
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
- // Insert
- modifyAdditions.add(bean);
+ modifyAdditions.put(bean, bean);
}
}
}
private boolean undoAddition(Object bean) {
- return (bean != null) && modifyAdditions.remove(bean);
+ return (bean != null) && modifyAdditions.remove(bean) != null;
}
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
-
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
}
-
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
- modifyDeletions.add((E) bean);
+ modifyDeletions.put((E) bean, bean);
}
}
}
Set getModifyAdditions() {
- return modifyAdditions;
+ return modifyAdditions.keySet();
}
Set getModifyRemovals() {
- return modifyDeletions;
+ return modifyDeletions.keySet();
}
/**
diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
index bc3ae587f..873304c3a 100644
--- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
+++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
@@ -7,9 +7,7 @@ import io.ebean.EbeanVersion;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
import io.ebean.Transaction;
-import io.ebean.annotation.Encrypted;
-import io.ebean.annotation.PersistBatch;
-import io.ebean.annotation.Platform;
+import io.ebean.annotation.*;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -194,10 +192,9 @@ public class DatabaseConfig {
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
/**
- * When true then by default DbJson beans are assumed to be dirty.
- * I believe we want to change this default to false in the future.
+ * The default mode used for {@code @DbJson} with Jackson ObjectMapper.
*/
- private boolean jsonDirtyByDefault = true;
+ private MutationDetection jsonMutationDetection = MutationDetection.HASH;
/**
* The database platform name. Used to imply a DatabasePlatform to use.
@@ -322,6 +319,8 @@ public class DatabaseConfig {
*/
private ExternalTransactionManager externalTransactionManager;
+ private boolean skipDataSourceCheck;
+
/**
* The data source (if programmatically provided).
*/
@@ -744,23 +743,21 @@ public class DatabaseConfig {
}
/**
- * Return true if DbJson beans are assumed dirty by default.
- *
- * That is, when true beans that do not implement ModifyAwareType are by
- * default assumed to be dirty and included in updates.
+ * Return the default MutableDetection to use with {@code @DbJson} using Jackson.
+ *
+ * @see DbJson#mutationDetection()
*/
- public boolean isJsonDirtyByDefault() {
- return jsonDirtyByDefault;
+ public MutationDetection getJsonMutationDetection() {
+ return jsonMutationDetection;
}
/**
- * Set to false if we want DbJson beans to not be assumed to be dirty.
- *
- * That is, when true beans that do not implement ModifyAwareType are by
- * default assumed to be dirty and included in updates.
+ * Set the default MutableDetection to use with {@code @DbJson} using Jackson.
+ *
+ * @see DbJson#mutationDetection()
*/
- public void setJsonDirtyByDefault(boolean jsonDirtyByDefault) {
- this.jsonDirtyByDefault = jsonDirtyByDefault;
+ public void setJsonMutationDetection(MutationDetection jsonMutationDetection) {
+ this.jsonMutationDetection = jsonMutationDetection;
}
/**
@@ -1656,6 +1653,20 @@ public class DatabaseConfig {
this.autoTuneConfig = autoTuneConfig;
}
+ /**
+ * Return true if the startup DataSource check should be skipped.
+ */
+ public boolean skipDataSourceCheck() {
+ return skipDataSourceCheck;
+ }
+
+ /**
+ * Set to true to skip the startup DataSource check.
+ */
+ public void setSkipDataSourceCheck(boolean skipDataSourceCheck) {
+ this.skipDataSourceCheck = skipDataSourceCheck;
+ }
+
/**
* Return the DataSource.
*/
@@ -2935,8 +2946,9 @@ public class DatabaseConfig {
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
- jsonDirtyByDefault = p.getBoolean("jsonDirtyByDefault", jsonDirtyByDefault);
+ jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection);
+ skipDataSourceCheck = p.getBoolean("skipDataSourceCheck", skipDataSourceCheck);
runMigration = p.getBoolean("migration.run", runMigration);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
diff --git a/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java
index 323b572ec..51df586d9 100644
--- a/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java
+++ b/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java
@@ -18,22 +18,22 @@ public abstract class AbstractMetricVisitor implements MetricVisitor {
}
@Override
- public boolean isReset() {
+ public boolean reset() {
return reset;
}
@Override
- public boolean isCollectTransactionMetrics() {
+ public boolean collectTransactionMetrics() {
return collectTransactionMetrics;
}
@Override
- public boolean isCollectQueryMetrics() {
+ public boolean collectQueryMetrics() {
return collectQueryMetrics;
}
@Override
- public boolean isCollectL2Metrics() {
+ public boolean collectL2Metrics() {
return collectL2Metrics;
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java
index c821c63a7..0db953159 100644
--- a/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java
+++ b/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java
@@ -27,17 +27,17 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
}
@Override
- public List getTimedMetrics() {
+ public List timedMetrics() {
return timed;
}
@Override
- public List getQueryMetrics() {
+ public List queryMetrics() {
return query;
}
@Override
- public List getCountMetrics() {
+ public List countMetrics() {
return count;
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java
index 8b6af3594..cdaa2d7fa 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java
@@ -8,6 +8,13 @@ public interface MetaCountMetric extends MetaMetric {
/**
* Return the total count.
*/
- long getCount();
+ long count();
+ /**
+ * Migrate to count()
+ */
+ @Deprecated
+ default long getCount() {
+ return count();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java
index 0d716fe23..671dc05c1 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java
@@ -8,6 +8,13 @@ public interface MetaMetric {
/**
* Return the metric name.
*/
- String getName();
+ String name();
+ /**
+ * Migrate to name().
+ */
+ @Deprecated
+ default String getName() {
+ return name();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java
index 514b700dc..1c9b5fe12 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java
@@ -8,21 +8,45 @@ public interface MetaQueryMetric extends MetaTimedMetric {
/**
* The type of entity or DTO bean.
*/
- Class> getType();
+ Class> type();
+
+ /**
+ * Migrate to type().
+ */
+ @Deprecated
+ default Class> getType() {
+ return type();
+ }
/**
* The label for the query (can be null).
*/
- String getLabel();
+ String label();
+
+ /**
+ * Migrate to label().
+ */
+ @Deprecated
+ default String getLabel() {
+ return label();
+ }
/**
* The actual SQL of the query.
*/
- String getSql();
+ String sql();
+
+ /**
+ * Migrate to sql().
+ */
+ @Deprecated
+ default String getSql() {
+ return sql();
+ }
/**
* Return the hash of the plan.
*/
- String getHash();
+ String hash();
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java
index fb2b6d095..c1ae44a82 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java
@@ -10,45 +10,45 @@ public interface MetaQueryPlan {
/**
* Return the bean type for the query.
*/
- Class> getBeanType();
+ Class> beanType();
/**
* Return the label of the query.
*/
- String getLabel();
+ String label();
/**
* Return the profile location for the query.
*/
- ProfileLocation getProfileLocation();
+ ProfileLocation profileLocation();
/**
* Return the sql of the query.
*/
- String getSql();
+ String sql();
/**
* Return the hash of the plan.
*/
- String getHash();
+ String hash();
/**
* Return a description of the bind values.
*/
- String getBind();
+ String bind();
/**
* Return the raw plan.
*/
- String getPlan();
+ String plan();
/**
* Return the query execution time associated with the bind values capture.
*/
- long getQueryTimeMicros();
+ long queryTimeMicros();
/**
* Return the total count of times bind capture has occurred.
*/
- long getCaptureCount();
+ long captureCount();
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java
index c6ba13516..f95f3248d 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java
@@ -9,27 +9,68 @@ public interface MetaTimedMetric extends MetaMetric {
/**
* Return the metric location if defined.
*/
- String getLocation();
+ String location();
+
+ /**
+ * Migrate to location()
+ */
+ @Deprecated
+ default String getLocation() {
+ return location();
+ }
/**
* Return the total count.
*/
- long getCount();
+ long count();
+
+ /**
+ * Migrate to count()
+ */
+ @Deprecated
+ default long getCount() {
+ return count();
+ }
/**
* Return the total execution time in micros.
*/
- long getTotal();
+ long total();
+
+ /**
+ * Migrate to total()
+ */
+ @Deprecated
+ default long getTotal() {
+ return total();
+ }
/**
* Return the max execution time in micros.
*/
- long getMax();
+ long max();
+
+ /**
+ * Migrate to max()
+ */
+ @Deprecated
+ default long getMax() {
+ return max();
+ }
/**
* Return the mean execution time in micros.
*/
- long getMean();
+ long mean();
+
+
+ /**
+ * Migrate to mean()
+ */
+ @Deprecated
+ default long getMean() {
+ return mean();
+ }
/**
* Return true if this is the first metrics collection for this query.
diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java
index 18c7a72dc..fac9f050e 100644
--- a/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java
+++ b/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java
@@ -8,22 +8,22 @@ public interface MetricVisitor {
/**
* Return true if the metrics should be reset.
*/
- boolean isReset();
+ boolean reset();
/**
* Return true if we should visit the transaction metrics.
*/
- boolean isCollectTransactionMetrics();
+ boolean collectTransactionMetrics();
/**
* Return true if we should visit the ORM and SQL query metrics.
*/
- boolean isCollectQueryMetrics();
+ boolean collectQueryMetrics();
/**
* Return true if we should visit the L2 cache metrics.
*/
- boolean isCollectL2Metrics();
+ boolean collectL2Metrics();
/**
* Visit has started.
diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java
index 3c19caa39..5cce68994 100644
--- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java
+++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java
@@ -32,7 +32,7 @@ public class QueryPlanInit {
* Return the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
- public long getThresholdMicros() {
+ public long thresholdMicros() {
return thresholdMicros;
}
@@ -40,7 +40,7 @@ public class QueryPlanInit {
* Set the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
- public void setThresholdMicros(long thresholdMicros) {
+ public void thresholdMicros(long thresholdMicros) {
this.thresholdMicros = thresholdMicros;
}
@@ -54,14 +54,14 @@ public class QueryPlanInit {
/**
* Return the specific hashes that we want to collect query plans on.
*/
- public Set getHashes() {
+ public Set hashes() {
return hashes;
}
/**
* Set the specific hashes that we want to collect query plans on.
*/
- public void setHashes(Set hashes) {
+ public void hashes(Set hashes) {
this.hashes = hashes;
}
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java
index c4a69b1aa..421e0c151 100644
--- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java
+++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java
@@ -18,7 +18,7 @@ public class QueryPlanRequest {
* have been around for a while (e.g. 5 mins) and so reasonably represent
* bind values that match the slowest execution for this query plan.
*/
- public long getSince() {
+ public long since() {
return since;
}
@@ -28,14 +28,14 @@ public class QueryPlanRequest {
*
* @param since The minimum age of the bind values capture.
*/
- public void setSince(long since) {
+ public void since(long since) {
this.since = since;
}
/**
* Return the maximum number of plans to capture.
*/
- public int getMaxCount() {
+ public int maxCount() {
return maxCount;
}
@@ -45,7 +45,7 @@ public class QueryPlanRequest {
* Use this to limit how much query plan capturing is done as query
* plan capture is actual database load.
*/
- public void setMaxCount(int maxCount) {
+ public void maxCount(int maxCount) {
this.maxCount = maxCount;
}
@@ -54,7 +54,7 @@ public class QueryPlanRequest {
*
* Query plan collection will stop once this time is exceeded.
*/
- public long getMaxTimeMillis() {
+ public long maxTimeMillis() {
return maxTimeMillis;
}
@@ -65,7 +65,7 @@ public class QueryPlanRequest {
* this to ensure the query plan capture does not use excessive amount
* of time - put too much load on the database.
*/
- public void setMaxTimeMillis(long maxTimeMillis) {
+ public void maxTimeMillis(long maxTimeMillis) {
this.maxTimeMillis = maxTimeMillis;
}
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java b/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java
index 8f0488d62..b8a50d85d 100644
--- a/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java
+++ b/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java
@@ -10,16 +10,39 @@ public interface ServerMetrics {
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
- List getTimedMetrics();
+ List timedMetrics();
+
+ /**
+ * Migrate to timedMetrics().
+ */
+ @Deprecated
+ default List getTimedMetrics() {
+ return timedMetrics();
+ }
/**
* Return the query metrics.
*/
- List getQueryMetrics();
+ List queryMetrics();
+
+ /**
+ * Migrate to queryMetrics().
+ */
+ @Deprecated
+ default List getQueryMetrics() {
+ return queryMetrics();
+ }
/**
* Return the Counter metrics.
*/
- List getCountMetrics();
+ List countMetrics();
+ /**
+ * Migrate to countMetrics().
+ */
+ @Deprecated
+ default List getCountMetrics() {
+ return countMetrics();
+ }
}
diff --git a/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java b/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java
index f48ecb5b6..7e42302c8 100644
--- a/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java
+++ b/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java
@@ -8,12 +8,12 @@ import java.util.Comparator;
public interface ServerMetricsAsJson {
/**
- * Set to false to exclude profile location and sql.
+ * Set to false in order to exclude profile location and sql.
*/
ServerMetricsAsJson withExtraAttributes(boolean withLocation);
/**
- * Set to false to exclude SQL hash.
+ * Set to false in order to exclude SQL hash.
*/
ServerMetricsAsJson withHash(boolean withHash);
diff --git a/ebean-api/src/main/java/io/ebean/meta/SortMetric.java b/ebean-api/src/main/java/io/ebean/meta/SortMetric.java
index 9df2c704b..30104f1ff 100644
--- a/ebean-api/src/main/java/io/ebean/meta/SortMetric.java
+++ b/ebean-api/src/main/java/io/ebean/meta/SortMetric.java
@@ -32,7 +32,7 @@ public class SortMetric {
@Override
public int compare(MetaCountMetric o1, MetaCountMetric o2) {
- return stringCompare(o1.getName(), o2.getName());
+ return stringCompare(o1.name(), o2.name());
}
}
@@ -43,8 +43,8 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- int i = stringCompare(o1.getName(), o2.getName());
- return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
+ int i = stringCompare(o1.name(), o2.name());
+ return i != 0 ? i : Long.compare(o1.count(), o2.count());
}
}
@@ -55,7 +55,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getCount(), o1.getCount());
+ return Long.compare(o2.count(), o1.count());
}
}
@@ -66,7 +66,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getTotal(), o1.getTotal());
+ return Long.compare(o2.total(), o1.total());
}
}
@@ -77,7 +77,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getMean(), o1.getMean());
+ return Long.compare(o2.mean(), o1.mean());
}
}
@@ -88,7 +88,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
- return Long.compare(o2.getMax(), o1.getMax());
+ return Long.compare(o2.max(), o1.max());
}
}
}
diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml
index 6b6e9511a..c47f7fe08 100644
--- a/ebean-autotune/pom.xml
+++ b/ebean-autotune/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
@@ -26,7 +26,7 @@
io.ebean
ebean-core
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
provided
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index 446c823f2..fff064c90 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
ebean bom
@@ -14,12 +14,12 @@
1.0
- 1.0
- 12.4.0
+ 1.1
+ 12.11.0
4.1
7.0
- 12.9.0
- 12.9.1
+ 12.11.0
+ 12.11.0
@@ -81,88 +81,88 @@
io.ebean
ebean
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-api
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-core
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-core-type
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-ddl-generator
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-externalmapping-api
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-externalmapping-xml
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-autotune
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-querybean
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
querybean-generator
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
provided
io.ebean
kotlin-querybean-generator
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
provided
io.ebean
ebean-test
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
test
io.ebean
ebean-postgis
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-redis
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml
index 9d9d22dfc..d8eab3e25 100644
--- a/ebean-core-type/pom.xml
+++ b/ebean-core-type/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
ebean-core-type
@@ -16,7 +16,7 @@
io.ebean
ebean-api
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java b/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java
index be6a3b559..b08bb529a 100644
--- a/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java
+++ b/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java
@@ -163,4 +163,15 @@ public interface DataBinder {
* Bind an array value.
*/
void setArray(String arrayType, Object[] elements) throws SQLException;
+
+ /**
+ * Push json from dirty detection to be available for binding.
+ */
+ void pushJson(String json);
+
+ /**
+ * Pop json made during dirty detection for scalarType binding.
+ */
+ String popJson();
+
}
diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java b/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java
index a8ace12be..ef61b9122 100644
--- a/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java
+++ b/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java
@@ -48,4 +48,14 @@ public interface DataReader {
Object getObject() throws SQLException;
InputStream getBinaryStream() throws SQLException;
+
+ /**
+ * Push json from dirty detection to be available for binding.
+ */
+ void pushJson(String json);
+
+ /**
+ * Pop json made during dirty detection for scalarType binding.
+ */
+ String popJson();
}
diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java
index a971a0115..6c01811c5 100644
--- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java
+++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java
@@ -2,6 +2,7 @@ package io.ebean.core.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
+
import io.ebean.text.StringFormatter;
import io.ebean.text.StringParser;
@@ -34,6 +35,10 @@ import java.sql.SQLException;
*/
public interface ScalarType extends StringParser, StringFormatter, ScalarDataReader {
+ default boolean isJsonMapper() {
+ return false;
+ }
+
/**
* Return true if this is a binary type and can not support parse() and format() from/to string.
* This allows Ebean to optimise marshalling types to string.
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 5febb1ea6..b99bf98fb 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -3,7 +3,7 @@
ebean-parent
io.ebean
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
ebean-core
@@ -52,13 +52,13 @@
io.avaje
classpath-scanner
- 4.2
+ 6.0
io.ebean
ebean-migration-auto
- 1.0
+ 1.1
@@ -72,7 +72,7 @@
io.ebean
ebean-ddl-generator
- 12.9.4-RC1
+ 12.11.0
test
@@ -87,19 +87,19 @@
io.ebean
ebean-api
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-core-type
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
io.ebean
ebean-externalmapping-api
- 12.10.0-SNAPSHOT
+ 12.11.1-SNAPSHOT
@@ -302,7 +302,7 @@
io.ebean
ebean-maven-plugin
- 12.9.1
+ 12.10.0
test
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..64a0a0287 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;
/**
@@ -54,12 +50,11 @@ public class BindParams implements Serializable {
positionedParameters.clear();
}
- public int queryBindHash() {
- int hc = namedParameters.hashCode();
- for (Param positionedParameter : positionedParameters) {
- hc = hc * 92821 + positionedParameter.hashCode();
+ public void queryBindHash(BindValuesKey key) {
+ key.add(positionedParameters.size());
+ for (Param param : positionedParameters) {
+ param.queryBindHash(key);
}
- return hc;
}
/**
@@ -424,7 +419,14 @@ public class BindParams implements Serializable {
@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 isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.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 c14cb88d7..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 int bindHash;
+ private final BindValuesKey bindValuesKey;
/**
* Create the HashQuery.
*/
- public HashQuery(CQueryPlanKey planHash, int 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;
+ 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 == 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/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/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java
index 007e9f29f..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.
*/
- int queryBindHash();
+ 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 12942e7d6..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).
*/
- int queryBindHash();
+ void queryBindKey(BindValuesKey key);
/**
* Identifies queries that are exactly the same including bind variables.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java
index 96a1eade7..0b4c3ea0a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java
@@ -18,7 +18,7 @@ public interface SpiQueryPlan {
String getName();
/**
- * The hash for the query plan.
+ * The hash of the sql.
*/
String getHash();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java
index 67844c631..a88f1bbe9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java
@@ -23,25 +23,16 @@ import javax.persistence.PersistenceException;
public abstract class AbstractSqlQueryRequest implements CancelableQuery {
protected final SpiSqlBinding query;
-
protected final SpiEbeanServer server;
-
protected SpiTransaction transaction;
-
private boolean createdTransaction;
-
protected String sql;
-
protected ResultSet resultSet;
-
protected String bindLog = "";
-
protected PreparedStatement pstmt;
-
protected long startNano;
-
private final ReentrantLock lock = new ReentrantLock();
-
+
/**
* Create the BeanFindRequest.
*/
@@ -161,7 +152,8 @@ public abstract class AbstractSqlQueryRequest implements CancelableQuery {
this.bindLog = binder.bind(bindParams, pstmt, conn);
}
if (isLogSql()) {
- transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
+ long micros = (System.nanoTime() - startNano) / 1000L;
+ transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ") --micros(", micros + ")"));
}
} finally {
lock.unlock();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
index cdefcc294..2c8bbfecc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
@@ -255,6 +255,9 @@ public class DefaultContainer implements SpiContainer {
}
throw new RuntimeException("DataSource not set?");
}
+ if (config.skipDataSourceCheck()) {
+ return true;
+ }
try (Connection connection = config.getDataSource().getConnection()) {
if (connection.getAutoCommit()) {
logger.warn("DataSource [{}] has autoCommit defaulting to true!", config.getName());
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java
index 051d331ba..20d7ec223 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java
@@ -18,8 +18,8 @@ class DefaultQueryPlanListener implements QueryPlanListener {
String dbName = capture.getDatabase().getName();
for (MetaQueryPlan plan : capture.getPlans()) {
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
- dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
- plan.getSql(), plan.getBind(), plan.getPlan());
+ dbName, plan.label(), plan.queryTimeMicros(), plan.profileLocation(),
+ plan.sql(), plan.bind(), plan.plan());
}
}
}
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 87580f90b..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
@@ -397,6 +397,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (dbSchema != null) {
migrationRunner.setDefaultDbSchema(dbSchema);
}
+ migrationRunner.setPlatform(config.getDatabasePlatform().getPlatform().base().name().toLowerCase());
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
@@ -415,8 +416,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private void collectQueryPlans() {
QueryPlanRequest request = new QueryPlanRequest();
- request.setMaxCount(config.getQueryPlanCaptureMaxCount());
- request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
+ request.maxCount(config.getQueryPlanCaptureMaxCount());
+ request.maxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
// obtains query explain plans ...
List plans = metaInfoManager.queryPlanCollectNow(request);
@@ -1283,14 +1284,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
- public boolean exists(Query> ormQuery, Transaction transaction) {
- Query> ormQueryCopy = ormQuery.copy();
- ormQueryCopy.setMaxRows(1);
+ public boolean exists(Query ormQuery, Transaction transaction) {
+ Query ormQueryCopy = ormQuery.copy().setMaxRows(1);
SpiOrmQueryRequest> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
try {
request.initTransIfRequired();
- List ids = request.findIds();
- return !ids.isEmpty();
+ return !request.findIds().isEmpty();
} finally {
request.endTransIfRequired();
}
@@ -2056,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);
}
}
@@ -2329,13 +2326,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void visitMetrics(MetricVisitor visitor) {
visitor.visitStart();
- if (visitor.isCollectTransactionMetrics()) {
+ if (visitor.collectTransactionMetrics()) {
transactionManager.visitMetrics(visitor);
}
- if (visitor.isCollectL2Metrics()) {
+ if (visitor.collectL2Metrics()) {
serverCacheManager.visitMetrics(visitor);
}
- if (visitor.isCollectQueryMetrics()) {
+ if (visitor.collectQueryMetrics()) {
beanDescriptorManager.visitMetrics(visitor);
dtoBeanManager.visitMetrics(visitor);
relationalQueryEngine.visitMetrics(visitor);
@@ -2352,7 +2349,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
List queryPlanInit(QueryPlanInit initRequest) {
if (initRequest.isAll()) {
- queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
+ queryPlanManager.setDefaultThreshold(initRequest.thresholdMicros());
}
return beanDescriptorManager.queryPlanInit(initRequest);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java
index 2b1a55f6e..c023f2d0d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java
@@ -28,11 +28,8 @@ public final class DtoQueryRequest extends AbstractSqlQueryRequest {
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
private final SpiDtoQuery query;
-
private final DtoQueryEngine queryEngine;
-
private DtoQueryPlan plan;
-
private DataReader dataReader;
DtoQueryRequest(SpiEbeanServer server, DtoQueryEngine engine, SpiDtoQuery query) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java
index 972ee2754..5274b4ced 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java
@@ -78,11 +78,11 @@ class DumpMetrics {
out("-- Dumping metrics for " + server.getName() + " -- ");
ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics();
- for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
+ for (MetaTimedMetric metric : serverMetrics.timedMetrics()) {
log(metric);
}
- List countMetrics = serverMetrics.getCountMetrics();
+ List countMetrics = serverMetrics.countMetrics();
if (!countMetrics.isEmpty()) {
out("\n-- Counters --");
countMetrics.sort(SortMetric.COUNT_NAME);
@@ -91,7 +91,7 @@ class DumpMetrics {
}
}
- List queryMetrics = serverMetrics.getQueryMetrics();
+ List queryMetrics = serverMetrics.queryMetrics();
if (!queryMetrics.isEmpty()) {
out("\n-- Queries --");
queryMetrics.sort(sortBy);
@@ -104,8 +104,8 @@ class DumpMetrics {
private void logCount(MetaCountMetric metric) {
StringBuilder sb = new StringBuilder();
- sb.append(padNameTimed(metric.getName())).append(" ");
- sb.append(" count:").append(pad(metric.getCount()));
+ sb.append(padNameTimed(metric.name())).append(" ");
+ sb.append(" count:").append(pad(metric.count()));
out(sb.toString());
}
@@ -120,38 +120,38 @@ class DumpMetrics {
appendQueryName(metric, sb);
appendCounters(metric, sb);
if (dumpHash) {
- sb.append("\n hash:").append(metric.getHash());
+ sb.append("\n hash:").append(metric.hash());
}
appendProfileAndSql(metric, sb);
out(sb.toString());
}
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
- sb.append("query:").append(padName(metric.getName())).append(" ");
+ sb.append("query:").append(padName(metric.name())).append(" ");
}
private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) {
- String location = metric.getLocation();
+ String location = metric.location();
if (dumpLoc && location != null) {
sb.append("\n loc:").append(location);
}
if (dumpSql) {
- sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n");
+ sb.append(" \n\n sql:").append(metric.sql()).append("\n\n");
}
}
private void log(MetaTimedMetric metric) {
StringBuilder sb = new StringBuilder();
- sb.append(padNameTimed(metric.getName())).append(" ");
+ sb.append(padNameTimed(metric.name())).append(" ");
appendCounters(metric, sb);
out(sb.toString());
}
private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) {
- sb.append(" count:").append(pad(timedMetric.getCount()))
- .append(" total:").append(pad(timedMetric.getTotal()))
- .append(" mean:").append(pad(timedMetric.getMean()))
- .append(" max:").append(pad(timedMetric.getMax()));
+ sb.append(" count:").append(pad(timedMetric.count()))
+ .append(" total:").append(pad(timedMetric.total()))
+ .append(" mean:").append(pad(timedMetric.mean()))
+ .append(" max:").append(pad(timedMetric.max()));
}
private String padName(String name) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java
index 9854cafea..4bc1e5035 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java
@@ -31,9 +31,9 @@ class DumpMetricsData {
private void collect(ServerMetrics serverMetrics) {
- final List timedMetrics = serverMetrics.getTimedMetrics();
- final List countMetrics = serverMetrics.getCountMetrics();
- final List queryMetrics = serverMetrics.getQueryMetrics();
+ final List timedMetrics = serverMetrics.timedMetrics();
+ final List countMetrics = serverMetrics.countMetrics();
+ final List queryMetrics = serverMetrics.queryMetrics();
for (MetaTimedMetric metric : timedMetrics) {
add(metric);
@@ -47,7 +47,7 @@ class DumpMetricsData {
}
private MetricData create(MetaMetric metric) {
- MetricData data = new MetricData(metric.getName());
+ MetricData data = new MetricData(metric.name());
list.add(data);
return data;
}
@@ -55,30 +55,30 @@ class DumpMetricsData {
private void add(MetaTimedMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
- data.setLoc(metric.getLocation());
+ data.setLoc(metric.location());
}
private void addCount(MetaCountMetric metric) {
final MetricData data = create(metric);
- data.setCount(metric.getCount());
+ data.setCount(metric.count());
}
private void addQuery(MetaQueryMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
appendLocationAndSql(data, metric);
- data.setHash(metric.getHash());
+ data.setHash(metric.hash());
}
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
- data.setLoc(metric.getLocation());
- data.setSql(metric.getSql());
+ data.setLoc(metric.location());
+ data.setSql(metric.sql());
}
private void appendCounters(MetricData data, MetaTimedMetric timedMetric) {
- data.setCount(timedMetric.getCount());
- data.setTotal(timedMetric.getTotal());
- data.setMean(timedMetric.getMean());
- data.setMax(timedMetric.getMax());
+ data.setCount(timedMetric.count());
+ data.setTotal(timedMetric.total());
+ data.setMean(timedMetric.mean());
+ data.setMax(timedMetric.max());
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java
index ed13151c4..90200f79b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java
@@ -84,11 +84,11 @@ class DumpMetricsJson implements ServerMetricsAsJson {
private void collect(ServerMetrics serverMetrics) {
try {
start();
- for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
+ for (MetaTimedMetric metric : serverMetrics.timedMetrics()) {
logTimed(metric);
}
- List countMetrics = serverMetrics.getCountMetrics();
+ List countMetrics = serverMetrics.countMetrics();
if (!countMetrics.isEmpty()) {
if (sortBy != null) {
countMetrics.sort(SortMetric.COUNT_NAME);
@@ -98,7 +98,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
}
- List queryMetrics = serverMetrics.getQueryMetrics();
+ List queryMetrics = serverMetrics.queryMetrics();
if (!queryMetrics.isEmpty()) {
if (sortBy != null) {
queryMetrics.sort(sortBy);
@@ -170,7 +170,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
objStart();
key("name");
- val(metric.getName());
+ val(metric.name());
}
private void metricEnd() throws IOException {
@@ -180,7 +180,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
private void logCount(MetaCountMetric metric) throws IOException {
metricStart(metric);
key("count");
- val(metric.getCount());
+ val(metric.count());
metricEnd();
}
@@ -188,7 +188,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (isIncludeDetail(metric)) {
- appendExtra("loc", metric.getLocation());
+ append("loc", metric.location());
}
metricEnd();
}
@@ -197,11 +197,11 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (withHash) {
- appendExtra("hash", metric.getHash());
+ append("hash", metric.hash());
}
if (isIncludeDetail(metric)) {
- appendExtra("loc", metric.getLocation());
- appendExtra("sql", metric.getSql());
+ append("loc", metric.location());
+ append("sql", metric.sql());
}
metricEnd();
}
@@ -210,7 +210,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
return includeExtraAttributes == 2 || includeExtraAttributes == 1 && metric.initialCollection();
}
- private void appendExtra(String key, String val) throws IOException {
+ private void append(String key, String val) throws IOException {
if (val != null) {
key(key);
val(val);
@@ -218,13 +218,14 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
private void appendTiming(MetaTimedMetric timedMetric) throws IOException {
- key("count");
- val(timedMetric.getCount());
- key("total");
- val(timedMetric.getTotal());
- key("mean");
- val(timedMetric.getMean());
- key("max");
- val(timedMetric.getMax());
+ append("count", timedMetric.count());
+ append("total", timedMetric.total());
+ append("mean", timedMetric.mean());
+ append("max", timedMetric.max());
+ }
+
+ private void append(String key, long value) throws IOException {
+ key(key);
+ val(value);
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java
index a5130ef22..ef3874008 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java
@@ -18,11 +18,8 @@ import java.util.function.Predicate;
public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
private final RelationalQueryEngine queryEngine;
-
private String[] propertyNames;
-
private int estimateCapacity;
-
private int rows;
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java
index f7c1a5fea..aba5533e0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.core.bootup;
-import io.avaje.classpath.scanner.ClassFilter;
import io.ebean.annotation.DocStore;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.IdGenerator;
@@ -27,27 +26,23 @@ import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Predicate;
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
-public class BootupClasses implements ClassFilter {
+public class BootupClasses implements Predicate> {
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
private final List> embeddableList = new ArrayList<>();
-
private final List> entityList = new ArrayList<>();
-
private final List>> scalarTypeList = new ArrayList<>();
-
private final List>> scalarConverterList = new ArrayList<>();
-
private final List>> attributeConverterList = new ArrayList<>();
// The following objects are instantiated on first request
@@ -55,19 +50,12 @@ public class BootupClasses implements ClassFilter {
// instance list, that holds the instance. Once a class is instantiated
// (or added) it will get removed from the candidate list
private final List> idGeneratorCandidates = new ArrayList<>();
-
private final List> beanPersistControllerCandidates = new ArrayList<>();
-
private final List> beanPostLoadCandidates = new ArrayList<>();
-
private final List> beanPostConstructListenerCandidates = new ArrayList<>();
-
private final List> beanFindControllerCandidates = new ArrayList<>();
-
private final List> beanPersistListenerCandidates = new ArrayList<>();
-
private final List> beanQueryAdapterCandidates = new ArrayList<>();
-
private final List> serverConfigStartupCandidates = new ArrayList<>();
private final List idGeneratorInstances = new ArrayList<>();
@@ -98,7 +86,7 @@ public class BootupClasses implements ClassFilter {
public BootupClasses(List> list) {
if (list != null) {
for (Class> cls : list) {
- isMatch(cls);
+ test(cls);
}
}
}
@@ -188,13 +176,11 @@ public class BootupClasses implements ClassFilter {
}
public void addChangeLogInstances(DatabaseConfig config) {
-
readAuditPrepare = config.getReadAuditPrepare();
readAuditLogger = config.getReadAuditLogger();
changeLogPrepare = config.getChangeLogPrepare();
changeLogListener = config.getChangeLogListener();
changeLogRegister = config.getChangeLogRegister();
-
// if not already set create the implementations found
// via classpath scanning
if (readAuditPrepare == null && readAuditPrepareClass != null) {
@@ -341,18 +327,14 @@ public class BootupClasses implements ClassFilter {
}
@Override
- public boolean isMatch(Class> cls) {
-
+ public boolean test(Class> cls) {
if (isEmbeddable(cls)) {
embeddableList.add(cls);
-
} else if (isEntity(cls)) {
entityList.add(cls);
-
} else {
return isInterestingInterface(cls);
}
-
return true;
}
@@ -364,7 +346,6 @@ public class BootupClasses implements ClassFilter {
*/
@SuppressWarnings("unchecked")
private boolean isInterestingInterface(Class> cls) {
-
if (Modifier.isAbstract(cls.getModifiers())) {
// do not include abstract classes as we can
// not instantiate them
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 63599b7ad..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
@@ -1559,7 +1559,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType {
void queryPlanInit(QueryPlanInit request, List list) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (request.includeHash(queryPlan.getHash())) {
- queryPlan.queryPlanInit(request.getThresholdMicros());
+ queryPlan.queryPlanInit(request.thresholdMicros());
list.add(queryPlan.createMeta(null, null));
}
}
@@ -1572,7 +1572,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType {
iudMetrics.visit(visitor);
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (!queryPlan.isEmptyStats()) {
- visitor.visitQuery(queryPlan.getSnapshot(visitor.isReset()));
+ visitor.visitQuery(queryPlan.getSnapshot(visitor.reset()));
}
}
}
@@ -2022,7 +2022,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType {
public boolean isTableManaged(String tableName) {
return owner.isTableManaged(tableName);
}
-
+
/**
* Return the order column property.
*/
@@ -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);
}
/**
@@ -3198,9 +3198,9 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType {
public void checkMutableProperties(EntityBeanIntercept ebi) {
for (BeanProperty beanProperty : propertiesMutable) {
int propertyIndex = beanProperty.getPropertyIndex();
- if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) {
+ if (ebi.isLoadedProperty(propertyIndex)) {
Object value = beanProperty.getValue(ebi.getOwner());
- if (value != null && beanProperty.isDirtyValue(value)) {
+ if (beanProperty.checkMutable(value, ebi.isDirtyProperty(propertyIndex), ebi)) {
// mutable scalar value which is considered dirty so mark
// it as such so that it is included in an update
ebi.markPropertyAsChanged(propertyIndex);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java
index af6e7b9e4..5def0dc64 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java
@@ -42,11 +42,7 @@ class BeanEmbeddedMetaFactory {
int dbScale = dbScale(column, sourceProperties[i]);
String colDefn = getDbColumnDefn(column, sourceProperties[i]);
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn, dbNullable, dbLength, dbScale, colDefn);
- if (sourceProperties[i] instanceof BeanPropertyAssocOne) {
- embeddedProperties[i] = new BeanPropertyAssocOne((BeanPropertyAssocOne)sourceProperties[i], overrides);
- } else {
- embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
- }
+ embeddedProperties[i] = sourceProperties[i].override(overrides);
}
return new BeanEmbeddedMeta(embeddedProperties);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java
index 8793942b6..413d0b460 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonToken;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
+import io.ebean.bean.MutableValueInfo;
import io.ebean.bean.PersistenceContext;
import io.ebean.config.EncryptKey;
import io.ebean.config.dbplatform.DbEncryptFunction;
@@ -13,6 +14,7 @@ import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import io.ebean.plugin.Property;
import io.ebean.text.StringParser;
+import io.ebean.text.TextException;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
@@ -31,11 +33,7 @@ import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.query.STreeProperty;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
-import io.ebeaninternal.server.type.DataBind;
-import io.ebeaninternal.server.type.LocalEncryptedType;
-import io.ebeaninternal.server.type.ScalarTypeBoolean;
-import io.ebeaninternal.server.type.ScalarTypeEnum;
-import io.ebeaninternal.server.type.ScalarTypeLogicalType;
+import io.ebeaninternal.server.type.*;
import io.ebeaninternal.util.ValueUtil;
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
@@ -52,6 +50,7 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.Types;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -366,13 +365,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return InternString.intern(s);
}
+ public BeanProperty override(BeanPropertyOverride override) {
+ return new BeanProperty(this, override);
+ }
+
/**
- * Create a Matching BeanProperty with some attributes overridden.
- *
- * Primarily for supporting Embedded beans with overridden dbColumn
- * mappings.
+ * Create a Matching BeanProperty with some attributes overridden for Embedded beans.
*/
- public BeanProperty(BeanProperty source, BeanPropertyOverride override) {
+ protected BeanProperty(BeanProperty source, BeanPropertyOverride override) {
this.descriptor = source.descriptor;
this.propertyIndex = source.propertyIndex;
this.name = source.getName();
@@ -642,15 +642,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
}
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
- try {
- Object value = scalarType.read(ctx.getDataReader());
- if (bean != null) {
- setValue(bean, value);
- }
- return value;
- } catch (Exception e) {
- throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
- }
+ return readSet(ctx.getDataReader(), bean);
}
@SuppressWarnings("unchecked")
@@ -828,6 +820,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return scalarType.parse(value);
}
+ /**
+ * creates a mutableHash for the given JSON value.
+ */
+ public MutableValueInfo createMutableInfo(String json) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Read the value for this property from L2 cache entry and set it to the bean.
*
@@ -909,7 +908,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the name of the property.
*/
- @Override @Nonnull
+ @Override
+ @Nonnull
public String getName() {
return name;
}
@@ -1018,8 +1018,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
* Return true if the mutable value is considered dirty.
* This is only used for 'mutable' scalar types like hstore etc.
*/
- boolean isDirtyValue(Object value) {
- return scalarType.isDirty(value);
+ boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) {
+ return alreadyDirty || value != null && scalarType.isDirty(value);
}
/**
@@ -1281,7 +1281,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
@Override
public Object localEncrypt(Object value) {
- return ((LocalEncryptedType)scalarType).localEncrypt(value);
+ return ((LocalEncryptedType) scalarType).localEncrypt(value);
}
/**
@@ -1394,7 +1394,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the property type.
*/
- @Override @Nonnull
+ @Override
+ @Nonnull
public Class> getPropertyType() {
return propertyType;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java
index ca81267eb..9a69b8c9c 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java
@@ -94,10 +94,12 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr
}
}
- /**
- * Copy constructor for ManyToOne inside Embeddable.
- */
- public BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) {
+ @Override
+ public BeanPropertyAssocOne> override(BeanPropertyOverride override) {
+ return new BeanPropertyAssocOne<>(this, override);
+ }
+
+ protected BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) {
super(source, override);
primaryKeyExport = source.primaryKeyExport;
primaryKeyJoin = source.primaryKeyJoin;
@@ -255,12 +257,8 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr
}
private SqlUpdate deleteByParentIdList(List parentIds) {
-
- StringBuilder sb = new StringBuilder(100);
- sb.append(deleteByParentIdInSql);
- sb.append(targetIdBinder.getIdInValueExpr(false, parentIds.size()));
-
- DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
+ String sql = deleteByParentIdInSql + targetIdBinder.getIdInValueExpr(false, parentIds.size());
+ DefaultSqlUpdate delete = new DefaultSqlUpdate(sql);
bindParentIds(delete, parentIds);
return delete;
}
@@ -501,7 +499,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr
return targetDescriptor.getIdProperty();
}
- ScalarType getIdScalarType() {
+ ScalarType> getIdScalarType() {
return targetDescriptor.getIdProperty().scalarType;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java
new file mode 100644
index 000000000..6593186a6
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java
@@ -0,0 +1,58 @@
+package io.ebeaninternal.server.deploy;
+
+import io.ebean.bean.EntityBean;
+import io.ebean.core.type.DataReader;
+import io.ebean.text.TextException;
+import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
+
+import javax.persistence.PersistenceException;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A DbJson property that does not use Jackson ObjectMapper.
+ */
+public class BeanPropertyJsonBasic extends BeanProperty {
+
+ public BeanPropertyJsonBasic(BeanDescriptor> descriptor, DeployBeanProperty deploy) {
+ super(descriptor, deploy);
+ }
+
+ protected BeanPropertyJsonBasic(BeanProperty source, BeanPropertyOverride override) {
+ super(source, override);
+ }
+
+ @Override
+ public BeanProperty override(BeanPropertyOverride override) {
+ return new BeanPropertyJsonBasic(this, override);
+ }
+
+ protected Object checkForEmpty(EntityBean bean) {
+ final Object value = getValue(bean);
+ if (value instanceof Collection && ((Collection>) value).isEmpty()
+ || value instanceof Map && ((Map, ?>) value).isEmpty()) {
+ return value;
+ }
+ return null;
+ }
+
+ @Override
+ public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
+ try {
+ Object value = scalarType.read(reader);
+ if (value == null) {
+ value = checkForEmpty(bean);
+ }
+ if (bean != null) {
+ setValue(bean, value);
+ }
+ return value;
+ } catch (TextException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
+ }
+ }
+
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java
new file mode 100644
index 000000000..fdea2e449
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java
@@ -0,0 +1,208 @@
+package io.ebeaninternal.server.deploy;
+
+import io.ebean.annotation.MutationDetection;
+import io.ebean.bean.EntityBean;
+import io.ebean.bean.EntityBeanIntercept;
+import io.ebean.bean.MutableValueInfo;
+import io.ebean.bean.MutableValueNext;
+import io.ebean.core.type.DataReader;
+import io.ebean.core.type.ScalarType;
+import io.ebean.text.TextException;
+import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
+import io.ebeaninternal.server.util.Checksum;
+
+import javax.persistence.PersistenceException;
+import java.sql.SQLException;
+import java.util.Objects;
+
+/**
+ * Handle json property with MutationDetection of SOURCE or HASH only.
+ */
+public class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
+
+ private final boolean sourceDetection;
+
+ public BeanPropertyJsonMapper(BeanDescriptor> desc, DeployBeanProperty deployProp) {
+ super(desc, deployProp);
+ this.sourceDetection = deployProp.getMutationDetection() == MutationDetection.SOURCE;
+ }
+
+ private BeanPropertyJsonMapper(BeanPropertyJsonMapper source, BeanPropertyOverride override) {
+ super(source, override);
+ this.sourceDetection = source.sourceDetection;
+ }
+
+ @Override
+ public BeanProperty override(BeanPropertyOverride override) {
+ return new BeanPropertyJsonMapper(this, override);
+ }
+
+ @Override
+ public MutableValueInfo createMutableInfo(String json) {
+ if (sourceDetection) {
+ return new SourceMutableValue(scalarType, json);
+ } else {
+ return new ChecksumMutableValue(scalarType, json);
+ }
+ }
+
+ /**
+ * Next when no prior MutableValueInfo.
+ */
+ private MutableValueNext next(String json) {
+ if (sourceDetection) {
+ return new SourceMutableValue(scalarType, json);
+ } else {
+ return new NextPair(json, new ChecksumMutableValue(scalarType, json));
+ }
+ }
+
+ /**
+ * Return true if the json property is considered dirty.
+ */
+ @Override
+ boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) {
+ // mutation detection based on json content or checksum of json content
+ // only perform serialisation to json once
+ final String json = scalarType.format(value);
+ final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex);
+ if (oldHash == null) {
+ if (value == null) {
+ return false; // no change, still null
+ }
+ ebi.mutableNext(propertyIndex, next(json));
+ return true;
+ }
+ // only perform compute of checksum/hash once (if checksum based)
+ final MutableValueNext next = oldHash.nextDirty(json);
+ if (next != null) {
+ ebi.mutableNext(propertyIndex, next);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
+ try {
+ Object value = scalarType.read(reader);
+ if (value == null) {
+ value = checkForEmpty(bean);
+ }
+ if (bean != null) {
+ setValue(bean, value);
+ String json = reader.popJson();
+ if (json != null) {
+ final MutableValueInfo hash = createMutableInfo(json);
+ bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
+ }
+ }
+ return value;
+ } catch (TextException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
+ }
+ }
+
+ private static final class NextPair implements MutableValueNext {
+
+ private final String json;
+ private final MutableValueInfo next;
+
+ NextPair(String json, MutableValueInfo next) {
+ this.json = json;
+ this.next = next;
+ }
+
+ @Override
+ public String content() {
+ return json;
+ }
+
+ @Override
+ public MutableValueInfo info() {
+ return next;
+ }
+ }
+
+ /**
+ * Hold checksum of json source content to use for dirty detection.
+ *
+ * Does not support rebuilding 'oldValue' as no original json content.
+ */
+ private static final class ChecksumMutableValue implements MutableValueInfo {
+
+ private final ScalarType> parent;
+ private final long checksum;
+
+ ChecksumMutableValue(ScalarType> parent, String json) {
+ this.parent = parent;
+ this.checksum = Checksum.checksum(json);
+ }
+
+ /**
+ * Create with pre-computed checksum.
+ */
+ ChecksumMutableValue(ScalarType> parent, long checksum) {
+ this.parent = parent;
+ this.checksum = checksum;
+ }
+
+ @Override
+ public MutableValueNext nextDirty(String json) {
+ final long nextChecksum = Checksum.checksum(json);
+ return nextChecksum == checksum ? null : new NextPair(json, new ChecksumMutableValue(parent, nextChecksum));
+ }
+
+ @Override
+ public boolean isEqualToObject(Object obj) {
+ return Checksum.checksum(parent.format(obj)) == checksum;
+ }
+
+ @Override
+ public Object get() {
+ return null; // cannot create object from json
+ }
+ }
+
+ /**
+ * Hold json source content. This supports rebuilding the 'oldValue'.
+ */
+ private static final class SourceMutableValue implements MutableValueInfo, MutableValueNext {
+
+ private final String originalJson;
+ private final ScalarType> parent;
+
+ SourceMutableValue(ScalarType> parent, String json) {
+ this.parent = parent;
+ this.originalJson = json;
+ }
+
+ @Override
+ public MutableValueNext nextDirty(String json) {
+ return Objects.equals(originalJson, json) ? null : new SourceMutableValue(parent, json);
+ }
+
+ @Override
+ public boolean isEqualToObject(Object obj) {
+ return Objects.equals(originalJson, parent.format(obj));
+ }
+
+ @Override
+ public Object get() {
+ // rebuild the 'oldValue' for change log etc
+ return parent.parse(originalJson);
+ }
+
+ @Override
+ public String content() {
+ return originalJson;
+ }
+
+ @Override
+ public MutableValueInfo info() {
+ return this;
+ }
+ }
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java
index 366ba5a97..49dfccdc8 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java
@@ -28,11 +28,6 @@ class ElementEntityBean implements EntityBean {
return properties[pos];
}
- @Override
- public String _ebean_getMarker() {
- return null;
- }
-
@Override
public Object _ebean_newInstance() {
return new ElementEntityBean(properties);
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/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
index e20c898ce..5e5455b2f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
@@ -1,18 +1,6 @@
package io.ebeaninternal.server.deploy.meta;
-import io.ebean.annotation.CreatedTimestamp;
-import io.ebean.annotation.DocCode;
-import io.ebean.annotation.DocProperty;
-import io.ebean.annotation.DocSortable;
-import io.ebean.annotation.Formula;
-import io.ebean.annotation.Platform;
-import io.ebean.annotation.SoftDelete;
-import io.ebean.annotation.UpdatedTimestamp;
-import io.ebean.annotation.WhenCreated;
-import io.ebean.annotation.WhenModified;
-import io.ebean.annotation.Where;
-import io.ebean.annotation.WhoCreated;
-import io.ebean.annotation.WhoModified;
+import io.ebean.annotation.*;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -39,7 +27,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Types;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -109,6 +96,7 @@ public class DeployBeanProperty {
private boolean jsonSerialize = true;
private boolean jsonDeserialize = true;
+ private MutationDetection mutationDetection;
private boolean dbEncrypted;
private DbEncryptFunction dbEncryptFunction;
@@ -327,6 +315,14 @@ public class DeployBeanProperty {
this.jsonDeserialize = jsonDeserialize;
}
+ public MutationDetection getMutationDetection() {
+ return mutationDetection;
+ }
+
+ public void setMutationDetection(MutationDetection dirtyDetection) {
+ this.mutationDetection = dirtyDetection;
+ }
+
/**
* Return the sortOrder for the properties.
*/
@@ -1201,4 +1197,11 @@ public class DeployBeanProperty {
return false;
}
+ boolean isJsonMapper() {
+ return scalarType != null && scalarType.isJsonMapper();
+ }
+
+ boolean isJsonType() {
+ return mutationDetection != null;
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
index f6280b260..5466d5bca 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
@@ -1,15 +1,7 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.bean.EntityBean;
-import io.ebeaninternal.server.deploy.BeanDescriptor;
-import io.ebeaninternal.server.deploy.BeanDescriptorMap;
-import io.ebeaninternal.server.deploy.BeanProperty;
-import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
-import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
-import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
-import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
-import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
-import io.ebeaninternal.server.deploy.InheritInfo;
+import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.type.ScalarTypeString;
@@ -30,47 +22,28 @@ public class DeployBeanPropertyLists {
private static final NoopSetter NOOP_SETTER = new NoopSetter();
private BeanProperty versionProperty;
-
private BeanProperty unmappedJson;
-
private BeanProperty draft;
-
private BeanProperty draftDirty;
-
private BeanProperty tenant;
-
private final BeanDescriptor> desc;
-
private final LinkedHashMap propertyMap;
-
private BeanProperty id;
private final List local = new ArrayList<>();
-
private final List mutable = new ArrayList<>();
-
private final List> manys = new ArrayList<>();
-
private final List nonManys = new ArrayList<>();
-
private final List aggs = new ArrayList<>();
-
private final List> ones = new ArrayList<>();
-
private final List> onesImported = new ArrayList<>();
-
private final List> embedded = new ArrayList<>();
-
private final List baseScalar = new ArrayList<>();
-
private final List transients = new ArrayList<>();
-
private final List nonTransients = new ArrayList<>();
-
private final BeanPropertyAssocOne> unidirectional;
private final BeanProperty orderColumn;
- @SuppressWarnings({"unchecked"})
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor> desc, DeployBeanDescriptor> deploy) {
this.desc = desc;
@@ -86,7 +59,7 @@ public class DeployBeanPropertyLists {
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
DeployBeanPropertyAssocOne> deployUnidirectional = deploy.getUnidirectional();
- this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
+ this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne<>(owner, desc, deployUnidirectional);
this.propertyMap = new LinkedHashMap<>();
@@ -127,7 +100,7 @@ public class DeployBeanPropertyLists {
}
if (orderColumn != null) {
- orderColumn.setDeployOrder(order++);
+ orderColumn.setDeployOrder(order);
allocateToList(orderColumn);
propertyMap.put(orderColumn.getName(), orderColumn);
}
@@ -154,7 +127,6 @@ public class DeployBeanPropertyLists {
}
private void setImportedPrimaryKeysFor(DeployBeanDescriptor> deploy, DeployBeanPropertyAssocOne> id) {
-
for (DeployBeanProperty prop : id.getTargetDeploy().properties()) {
DeployBeanProperty match = findImported(deploy, prop);
if (match != null) {
@@ -164,7 +136,6 @@ public class DeployBeanPropertyLists {
}
private DeployBeanProperty findImported(DeployBeanDescriptor> deploy, DeployBeanProperty embeddedScalar) {
-
// the logical name and db column we are looking for a match on
String name = embeddedScalar.getName();
String dbColumn = embeddedScalar.getDbColumn();
@@ -180,7 +151,6 @@ public class DeployBeanPropertyLists {
return assocOne;
}
}
-
return null;
}
@@ -368,7 +338,6 @@ public class DeployBeanPropertyLists {
}
public BeanProperty getSoftDeleteProperty() {
-
for (BeanProperty prop : nonManys) {
if (prop.isSoftDelete()) {
return prop;
@@ -385,7 +354,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on insert.
*/
public BeanProperty[] getGeneratedInsert() {
-
List list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -400,7 +368,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on update.
*/
public BeanProperty[] getGeneratedUpdate() {
-
List list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -438,8 +405,7 @@ public class DeployBeanPropertyLists {
}
}
}
-
- return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[0]);
+ return list.toArray(new BeanPropertyAssocOne>[0]);
}
private BeanPropertyAssocMany>[] getMany2Many() {
@@ -449,8 +415,7 @@ public class DeployBeanPropertyLists {
list.add(prop);
}
}
-
- return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
+ return list.toArray(new BeanPropertyAssocMany>[0]);
}
private BeanPropertyAssocMany>[] getMany(Mode mode) {
@@ -471,25 +436,26 @@ public class DeployBeanPropertyLists {
break;
}
}
-
- return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
+ return list.toArray(new BeanPropertyAssocMany>[0]);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) {
-
if (deployProp instanceof DeployBeanPropertyAssocOne) {
return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp);
}
-
if (deployProp instanceof DeployBeanPropertySimpleCollection>) {
return new BeanPropertySimpleCollection(desc, (DeployBeanPropertySimpleCollection) deployProp);
}
-
if (deployProp instanceof DeployBeanPropertyAssocMany) {
return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp);
}
-
+ if (deployProp.isJsonMapper()) {
+ return new BeanPropertyJsonMapper(desc, deployProp);
+ }
+ if (deployProp.isJsonType()) {
+ return new BeanPropertyJsonBasic(desc, deployProp);
+ }
return new BeanProperty(desc, deployProp);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java
index 65c7cf45e..68cb1c741 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java
@@ -1,10 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
-import io.ebean.annotation.DbArray;
-import io.ebean.annotation.DbJson;
-import io.ebean.annotation.DbJsonB;
-import io.ebean.annotation.DbJsonType;
-import io.ebean.annotation.DbMap;
+import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptDeploy;
import io.ebean.config.EncryptDeployManager;
@@ -209,26 +205,22 @@ public class DeployUtil {
}
}
- /**
- * This property is marked as a Lob object.
- */
void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) {
-
int dbType = getDbJsonStorage(dbJsonType.storage());
- setDbJsonType(prop, dbType, dbJsonType.length());
+ setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.mutationDetection());
}
void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) {
- setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length());
+ setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.mutationDetection());
}
- private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) {
-
+ private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, MutationDetection mutationDetection) {
+ prop.setDbType(dbType);
+ prop.setMutationDetection(mutationDetection);
ScalarType> scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength);
if (scalarType == null) {
throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]");
}
- prop.setDbType(dbType);
prop.setScalarType(scalarType);
if (dbType == Types.VARCHAR || dbLength > 0) {
// determine the db column size
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..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ // 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..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -122,14 +123,11 @@ class AllEqualsExpression extends NonPrepareExpression {
}
@Override
- public int queryBindHash() {
-
- int hc = 92821;
+ public void queryBindKey(BindValuesKey key) {
+ key.add(propMap.size());
for (Object value : propMap.values()) {
- hc = hc * 92821 + (value == null ? 0 : value.hashCode());
+ key.add(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..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ key.add(values.length);
+ for (Object value : values) {
+ key.add(value);
}
- 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..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 bfdfa1d52..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 a50ec888f..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 073ee51df..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 52e39fbfa..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 aade99f5a..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
@@ -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.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -136,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);
}
}
@@ -186,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);
}
}
@@ -228,25 +227,20 @@ 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("]");
}
- /**
- * Return a hash for the actual bind values used.
- */
@Override
- public int queryBindHash() {
- int hc = DefaultExampleExpression.class.getName().hashCode();
- for (SpiExpression aList : list) {
- hc = hc * 92821 + aList.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(list.size());
+ for (SpiExpression expr : list) {
+ expr.queryBindKey(key);
}
- return hc;
}
@Override
@@ -267,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 ab80ae9e5..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
@@ -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.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -109,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 {
@@ -121,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);
}
}
@@ -174,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;
@@ -210,7 +208,6 @@ public class DefaultExpressionList implements SpiExpressionList {
@Override
public void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException {
-
if (allDocNestedPath != null) {
context.startNested(allDocNestedPath);
}
@@ -227,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();
}
@@ -278,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);
}
}
@@ -630,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) {
@@ -642,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);
}
}
@@ -667,23 +662,19 @@ 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("]");
}
- /**
- * Calculate a hash based on the expressions.
- */
@Override
- public int queryBindHash() {
- int hash = DefaultExpressionList.class.getName().hashCode();
- for (SpiExpression aList : list) {
- hash = hash * 92821 + aList.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ key.add(list.size());
+ for (SpiExpression expr : list) {
+ expr.queryBindKey(key);
}
- 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..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 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 b5280b868..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 4c08b24d2..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ key.add(idCollection.size());
+ for (Object elem : idCollection) {
+ key.add(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..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
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ key.add(bindValues.size());
for (Object bindValue : bindValues) {
- hc = 92821 * hc + bindValue.hashCode();
+ key.add(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..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
@@ -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.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ key.add(entries.size());
for (Pairs.Entry entry : entries) {
- hc = 92821 * hc + entry.hashCode();
+ key.add(entry.getA()).add(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..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 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 e1d170b43..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 d3a3a1da6..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ // 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..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 0aae30886..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
@@ -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.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -113,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();
}
@@ -123,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();
}
@@ -138,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
@@ -176,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("(");
@@ -204,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);
}
}
@@ -216,22 +207,18 @@ 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("]");
}
@Override
- public int queryBindHash() {
- int hc = JunctionExpression.class.getName().hashCode();
- List list = exprList.internalList();
- for (SpiExpression aList : list) {
- hc = hc * 92821 + aList.queryBindHash();
+ public void queryBindKey(BindValuesKey key) {
+ for (SpiExpression expr : exprList.internalList()) {
+ expr.queryBindKey(key);
}
- return hc;
}
@Override
@@ -275,7 +262,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
return exprList.textCommonTerms(search, options);
}
-
@Override
public ExpressionList allEq(Map propertyMap) {
return exprList.allEq(propertyMap);
@@ -1025,7 +1011,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) {
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..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 482aac6e8..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
@@ -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.BindValuesKey;
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 queryBindKey(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 c3376f656..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(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 77f46828b..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 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 28c9e3c78..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
// 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..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
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.event.BeanQueryRequest;
+import io.ebeaninternal.api.BindValuesKey;
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 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 90894dbde..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
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
+import io.ebeaninternal.api.BindValuesKey;
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 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 295ddc75b..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
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
+import io.ebeaninternal.api.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ key.add(values.length);
for (Object value : values) {
- hc = hc * 92821 + value.hashCode();
+ key.add(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..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
@@ -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.BindValuesKey;
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 queryBindKey(BindValuesKey key) {
+ key.add(value());
}
@Override
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/persist/SaveManyBeans.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java
index f273b9dbc..1f1da9fc0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java
@@ -295,7 +295,16 @@ public class SaveManyBeans extends SaveManyBase {
}
transaction.depth(+1);
-
+ if (deletions != null && !deletions.isEmpty()) {
+ for (Object other : deletions) {
+ EntityBean otherDelete = (EntityBean) other;
+ // the object from the 'other' side of the ManyToMany
+ // build a intersection row for 'delete'
+ IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
+ SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
+ persister.executeOrQueue(sqlDelete, transaction, queue);
+ }
+ }
if (additions != null && !additions.isEmpty()) {
for (Object other : additions) {
EntityBean otherBean = (EntityBean) other;
@@ -318,16 +327,6 @@ public class SaveManyBeans extends SaveManyBase {
}
}
}
- if (deletions != null && !deletions.isEmpty()) {
- for (Object other : deletions) {
- EntityBean otherDelete = (EntityBean) other;
- // the object from the 'other' side of the ManyToMany
- // build a intersection row for 'delete'
- IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
- SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
- persister.executeOrQueue(sqlDelete, transaction, queue);
- }
- }
// decrease the depth back to what it was
transaction.depth(-1);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java
index 189613808..0a4bb6b8a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java
@@ -63,6 +63,11 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
}
}
+ @Override
+ public void pushJson(String json) {
+ dataBind.pushJson(json);
+ }
+
@Override
public long now() {
return now;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java
new file mode 100644
index 000000000..1cbb7eeca
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java
@@ -0,0 +1,42 @@
+package io.ebeaninternal.server.persist.dmlbind;
+
+import io.ebean.bean.EntityBean;
+import io.ebean.bean.MutableValueInfo;
+import io.ebeaninternal.server.deploy.BeanProperty;
+
+import java.sql.SQLException;
+
+/**
+ * For JSON Jackson properties - dirty detection via MD5 of json content.
+ */
+class BindablePropertyJsonInsert extends BindableProperty {
+
+ private final int propertyIndex;
+
+ BindablePropertyJsonInsert(BeanProperty prop) {
+ super(prop);
+ this.propertyIndex = prop.getPropertyIndex();
+ }
+
+ /**
+ * Normal binding of a property value from the bean.
+ */
+ @Override
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+ if (bean == null) {
+ request.bind(null, prop);
+ } else {
+ Object value = prop.getValue(bean);
+ if (value == null) {
+ request.bind(null, prop);
+ } else {
+ // on insert store hash and push json
+ final String json = prop.format(value);
+ final MutableValueInfo hash = prop.createMutableInfo(json);
+ bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
+ request.pushJson(json);
+ request.bind(value, prop);
+ }
+ }
+ }
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java
new file mode 100644
index 000000000..cfa056a63
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java
@@ -0,0 +1,33 @@
+package io.ebeaninternal.server.persist.dmlbind;
+
+import io.ebean.bean.EntityBean;
+import io.ebeaninternal.server.deploy.BeanProperty;
+
+import java.sql.SQLException;
+
+/**
+ * For JSON Jackson properties - dirty detection via MD5 of json content.
+ */
+class BindablePropertyJsonUpdate extends BindableProperty {
+
+ private final int propertyIndex;
+
+ BindablePropertyJsonUpdate(BeanProperty prop) {
+ super(prop);
+ this.propertyIndex = prop.getPropertyIndex();
+ }
+
+ /**
+ * Normal binding of a property value from the bean.
+ */
+ @Override
+ public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
+ if (bean == null) {
+ request.bind(null, prop);
+ } else {
+ // update mutableInfo and push json
+ request.pushJson(bean._ebean_getIntercept().mutableNext(propertyIndex));
+ request.bind(prop.getValue(bean), prop);
+ }
+ }
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java
index 4a1fc6754..e06862e86 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java
@@ -57,4 +57,9 @@ public interface BindableRequest {
* Return true if this is an update request.
*/
boolean isUpdate();
+
+ /**
+ * Push json content for scalarType bind().
+ */
+ void pushJson(String json);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java
index b47b4ecb6..d5c59daf3 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.persist.dmlbind;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+import io.ebeaninternal.server.deploy.BeanPropertyJsonMapper;
import io.ebeaninternal.server.persist.dml.DmlMode;
/**
@@ -23,14 +24,12 @@ class FactoryProperty {
* Create a Bindable for the property given the mode and withLobs flag.
*/
public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs, boolean allowManyToOne) {
-
if (DmlMode.INSERT == mode && !prop.isDbInsertable()) {
return null;
}
if (DmlMode.UPDATE == mode && !prop.isDbUpdatable()) {
return null;
}
-
if (prop.isLob() && !withLobs) {
// Lob exclusion
return null;
@@ -38,11 +37,16 @@ class FactoryProperty {
if (prop.isDbEncrypted()){
return new BindableEncryptedProperty(prop, bindEncryptDataFirst);
}
-
if (allowManyToOne && prop instanceof BeanPropertyAssocOne) {
return new BindableAssocOne((BeanPropertyAssocOne>)prop);
}
-
+ if (prop instanceof BeanPropertyJsonMapper) {
+ if (DmlMode.INSERT == mode) {
+ return new BindablePropertyJsonInsert(prop);
+ } else if (DmlMode.UPDATE == mode) {
+ return new BindablePropertyJsonUpdate(prop);
+ }
+ }
return new BindableProperty(prop);
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java
index 507938a96..d7d5b0fd2 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java
@@ -13,7 +13,7 @@ final class BasicProfileLocation implements ProfileLocation {
BasicProfileLocation(String fullLocation) {
this.fullLocation = fullLocation;
- this.location = shortDesc(fullLocation);
+ this.location = UtilLocation.loc(fullLocation);
this.label = UtilLocation.label(location);
}
@@ -57,15 +57,4 @@ final class BasicProfileLocation implements ProfileLocation {
// do nothing
}
- private String shortDesc(String location) {
- int lastPer = location.lastIndexOf('.');
- if (lastPer > -1) {
- lastPer = location.lastIndexOf('.', lastPer - 1);
- if (lastPer > -1) {
- return location.substring(lastPer + 1);
- }
- }
- return location;
- }
-
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java
index 2cf7a25a7..d93e0dbdb 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java
@@ -49,7 +49,7 @@ class DCountMetric implements CountMetric {
@Override
public void visit(MetricVisitor visitor) {
- long val = visitor.isReset() ? count.sumThenReset() : count.sum();
+ long val = visitor.reset() ? count.sumThenReset() : count.sum();
if (val > 0) {
visitor.visitCount(new DCountMetricStats(name, val));
}
@@ -66,12 +66,12 @@ class DCountMetric implements CountMetric {
}
@Override
- public String getName() {
+ public String name() {
return name;
}
@Override
- public long getCount() {
+ public long count() {
return count;
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java
index c5ff7d3ac..63fa0e11a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java
@@ -8,17 +8,13 @@ import io.ebean.ProfileLocation;
class DProfileLocation implements ProfileLocation {
private static final String IO_EBEAN = "io.ebean";
-
private static final String UNKNOWN = "unknown";
private String fullLocation;
-
private String location;
-
private String label;
private final int lineNumber;
-
private int traceCount;
DProfileLocation() {
@@ -49,10 +45,10 @@ class DProfileLocation implements ProfileLocation {
return false;
}
final String loc = create();
- final String shortDesc = shortDesc(loc);
- label = UtilLocation.label(shortDesc);
- location = shortDesc;
- fullLocation = loc;
+ final String location = UtilLocation.loc(loc);
+ this.label = UtilLocation.label(location);
+ this.location = location;
+ this.fullLocation = loc;
initWith(label);
return true;
}
@@ -113,20 +109,4 @@ class DProfileLocation implements ProfileLocation {
return traceLine.substring(0, traceLine.length() - 1) + ":" + lineNumber + ")";
}
}
-
- private String shortDesc(String location) {
- int pos = location.lastIndexOf('(');
- if (pos == -1) {
- pos = location.length();
- }
-
- pos = location.lastIndexOf('.', pos);
- if (pos > -1) {
- pos = location.lastIndexOf('.', pos - 1);
- if (pos > -1) {
- return location.substring(pos + 1);
- }
- }
- return location;
- }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java
index 0690b5fdd..67f724051 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java
@@ -22,15 +22,8 @@ class DQueryPlanMeta {
name += "_" + label;
}
this.name = name;
- this.hash = initHash();
- }
-
- private String initHash() {
- StringBuilder sb = new StringBuilder(sql).append("|").append(name);
- if (profileLocation != null) {
- sb.append("|").append(profileLocation.location());
- }
- return Md5.hash(sb.toString());
+ String loc = profileLocation == null ? null : profileLocation.location();
+ this.hash = Md5.hash(sql, name, loc);
}
public Class> getType() {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java
index 21559a152..e24da73c7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java
@@ -19,7 +19,7 @@ class DQueryPlanMetric implements QueryPlanMetric {
@Override
public void visit(MetricVisitor visitor) {
- TimedMetricStats stats = metric.collect(visitor.isReset());
+ TimedMetricStats stats = metric.collect(visitor.reset());
if (stats != null) {
visitor.visitQuery(new Stats(meta, stats, collected));
collected = true;
@@ -45,11 +45,11 @@ class DQueryPlanMetric implements QueryPlanMetric {
@Override
public String toString() {
- return meta + " " + stats + " sql:" + getSql();
+ return meta + " " + stats + " sql:" + sql();
}
@Override
- public Class> getType() {
+ public Class> type() {
return meta.getType();
}
@@ -59,48 +59,48 @@ class DQueryPlanMetric implements QueryPlanMetric {
}
@Override
- public String getHash() {
+ public String hash() {
return meta.getHash();
}
@Override
- public String getLabel() {
+ public String label() {
return meta.getLabel();
}
@Override
- public String getSql() {
+ public String sql() {
return meta.getSql();
}
@Override
- public String getName() {
+ public String name() {
return meta.getName();
}
@Override
- public String getLocation() {
+ public String location() {
return meta.getLocation();
}
@Override
- public long getCount() {
- return stats.getCount();
+ public long count() {
+ return stats.count();
}
@Override
- public long getTotal() {
- return stats.getTotal();
+ public long total() {
+ return stats.total();
}
@Override
- public long getMax() {
- return stats.getMax();
+ public long max() {
+ return stats.max();
}
@Override
- public long getMean() {
- return stats.getMean();
+ public long mean() {
+ return stats.mean();
}
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java
index 54146abe9..a4243e31a 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java
@@ -7,18 +7,14 @@ import io.ebean.metric.TimedMetricStats;
*/
class DTimeMetricStats implements TimedMetricStats {
- private String name;
-
private final boolean collected;
-
- private String location;
-
private final long count;
-
private final long total;
-
private final long max;
+ private String name;
+ private String location;
+
DTimeMetricStats(String name, boolean collected, long count, long total, long max) {
this.name = name;
this.collected = collected;
@@ -60,12 +56,12 @@ class DTimeMetricStats implements TimedMetricStats {
}
@Override
- public String getName() {
+ public String name() {
return name;
}
@Override
- public String getLocation() {
+ public String location() {
return location;
}
@@ -73,7 +69,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the count of values collected.
*/
@Override
- public long getCount() {
+ public long count() {
return count;
}
@@ -81,7 +77,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the total of all the values.
*/
@Override
- public long getTotal() {
+ public long total() {
return total;
}
@@ -89,7 +85,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the Max value collected.
*/
@Override
- public long getMax() {
+ public long max() {
return max;
}
@@ -97,7 +93,7 @@ class DTimeMetricStats implements TimedMetricStats {
* Return the mean value rounded up.
*/
@Override
- public long getMean() {
+ public long mean() {
return (count < 1) ? 0L : Math.round((double)(total / count));
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java
index 63eb275f6..cc344c3e6 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java
@@ -71,7 +71,7 @@ class DTimedMetric implements TimedMetric {
@Override
public void visit(MetricVisitor visitor) {
- DTimeMetricStats metric = collect(visitor.isReset());
+ DTimeMetricStats metric = collect(visitor.reset());
if (metric != null) {
visitor.visitTimed(metric);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java
index 7ef4e9084..27537737f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java
@@ -48,7 +48,7 @@ class DTimedProfileLocation extends DProfileLocation implements TimedProfileLoca
@Override
public void visit(MetricVisitor visitor) {
- TimedMetricStats collect = timedMetric.collect(visitor.isReset());
+ TimedMetricStats collect = timedMetric.collect(visitor.reset());
if (collect != null) {
if (overrideMetricName) {
collect.setName(fullName);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java
index 7b70dfcb7..65ab1ce4d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java
@@ -2,15 +2,30 @@ package io.ebeaninternal.server.profile;
final class UtilLocation {
- static String label(String shortDescription) {
- int pos = shortDescription.indexOf("(");
- if (pos == -1) {
- return shortDescription;
+ static String loc(String full) {
+ final int pos = full.lastIndexOf('(');
+ if (pos > -1) {
+ return full.substring(0, pos);
} else {
- return trimInit(shortDescription.substring(0, pos));
+ return full;
}
}
+ static String label(String location) {
+ return trimInit(shortDesc(location));
+ }
+
+ private static String shortDesc(String location) {
+ int pos = location.lastIndexOf('.');
+ if (pos > -1) {
+ pos = location.lastIndexOf('.', pos - 1);
+ if (pos > -1) {
+ return location.substring(pos + 1);
+ }
+ }
+ return location;
+ }
+
/**
* Trim constructor init to be "safe" without greater than or less than chars.
*/
@@ -21,5 +36,4 @@ final class UtilLocation {
}
return desc;
}
-
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java
index e7674a31c..7fc636af0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java
@@ -541,9 +541,13 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran
updateStatistics();
}
+ long micros() {
+ return (System.nanoTime() - startNano) / 1000L;
+ }
+
private void updateStatistics() {
try {
- executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
+ executionTimeMicros = micros();
if (autoTuneProfiling) {
profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
}
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 1162e914d..41439e4b6 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
@@ -189,6 +189,12 @@ class CQueryBuilder {
SpiQuery> query = request.getQuery();
query.setSingleAttribute();
+ if (!query.isIncludeSoftDeletes()) {
+ BeanDescriptor> desc = request.getBeanDescriptor();
+ if (desc.isSoftDelete()) {
+ query.addSoftDeletePredicate(desc.getSoftDeletePredicate(alias(query.getAlias())));
+ }
+ }
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java
index a1a211c0a..03074b43f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java
@@ -79,15 +79,10 @@ public class CQueryEngine {
private int executeUpdate(OrmQueryRequest request, CQueryUpdate query) {
try {
int rows = query.execute();
-
if (request.isLogSql()) {
- String logSql = query.getGeneratedSql();
- logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
- request.logSql(logSql);
+ request.logSql(Str.add(query.getGeneratedSql(), "; --bind(", query.getBindLog(), ") --micros(", query.micros() + ") --rows(", rows + ")"));
}
-
return rows;
-
} catch (SQLException e) {
throw translate(request, query.getBindLog(), query.getGeneratedSql(), e);
}
@@ -97,7 +92,6 @@ public class CQueryEngine {
* Build and execute the findSingleAttributeList query.
*/
public List findSingleAttributeList(OrmQueryRequest> request) {
-
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request);
request.setCancelableQuery(rcQuery);
return findAttributeList(request, rcQuery);
@@ -108,14 +102,13 @@ public class CQueryEngine {
try {
List list = (List ) rcQuery.findList();
if (request.isLogSql()) {
- logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
+ logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog(), rcQuery.micros());
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.isQueryCachePut()) {
request.addDependentTables(rcQuery.getDependentTables());
-
list = Collections.unmodifiableList(list);
request.putToQueryCache(list);
if (Boolean.FALSE.equals(request.getQuery().isReadOnly())) {
@@ -123,7 +116,6 @@ public class CQueryEngine {
}
}
return list;
-
} catch (SQLException e) {
throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e);
}
@@ -139,10 +131,8 @@ public class CQueryEngine {
String msg = "ERROR executing query, bindLog[" + bindLog + "] error[" + StringHelper.removeNewLines(e.getMessage()) + "]";
t.logSummary(msg);
}
-
// ensure 'rollback' is logged if queryOnly transaction
t.getConnection();
-
// build a decent error message for the exception
String m = "Query threw SQLException:" + e.getMessage() + " Bind values:[" + bindLog + "] Query was:" + sql;
return dbPlatform.translate(m, e);
@@ -152,46 +142,37 @@ public class CQueryEngine {
* Build and execute the find Id's query.
*/
public List findIds(OrmQueryRequest> request) {
-
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request);
request.setCancelableQuery(rcQuery);
return findAttributeList(request, rcQuery);
}
- private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog) {
- request.logSql(Str.add(sql, "; --bind(", bindLog, ")"));
+ private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog, long micros) {
+ request.logSql(Str.add(sql, "; --bind(", bindLog, ") --micros(", micros + ")"));
}
/**
* Build and execute the row count query.
*/
public int findCount(OrmQueryRequest request) {
-
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
request.setCancelableQuery(rcQuery);
try {
-
int count = rcQuery.findCount();
-
if (request.isLogSql()) {
- logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
+ logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog(), rcQuery.micros());
}
-
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
-
if (request.getQuery().isFutureFetch()) {
request.getTransaction().end();
}
-
if (request.isQueryCachePut()) {
request.addDependentTables(rcQuery.getDependentTables());
request.putToQueryCache(count);
}
-
return count;
-
} catch (SQLException e) {
throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e);
}
@@ -254,9 +235,7 @@ public class CQueryEngine {
* Execute the find versions query returning version beans.
*/
public List> findVersions(OrmQueryRequest request) {
-
SpiQuery query = request.getQuery();
-
String sysPeriodLower = getSysPeriodLower(query);
if (query.isVersionsBetween() && !historySupport.isStandardsBased()) {
query.where().lt(sysPeriodLower, query.getVersionEnd());
@@ -272,26 +251,21 @@ public class CQueryEngine {
if (request.isLogSql()) {
logSql(cquery);
}
-
List> versions = cquery.readVersions();
// just order in memory rather than use NULLS LAST as that
// is not universally supported, not expect huge list here
versions.sort(OrderVersionDesc.INSTANCE);
deriveVersionDiffs(versions, request);
-
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
-
if (request.isAuditReads()) {
cquery.auditFindMany();
}
-
return versions;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
-
} finally {
if (cquery != null) {
cquery.close();
@@ -300,9 +274,7 @@ public class CQueryEngine {
}
private void deriveVersionDiffs(List> versions, OrmQueryRequest request) {
-
BeanDescriptor descriptor = request.getBeanDescriptor();
-
if (!versions.isEmpty()) {
Version current = versions.get(0);
if (versions.size() > 1) {
@@ -367,10 +339,8 @@ public class CQueryEngine {
* Find a list/map/set of beans.
*/
BeanCollection findMany(OrmQueryRequest request) {
-
CQuery cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
-
try {
if (defaultFetchSizeFindList > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
@@ -380,30 +350,24 @@ public class CQueryEngine {
logger.trace("Future fetch already cancelled");
return null;
}
-
if (request.isLogSql()) {
logSql(cquery);
}
-
BeanCollection beanCollection = cquery.readCollection();
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
-
if (request.isAuditReads()) {
cquery.auditFindMany();
}
-
request.executeSecondaryQueries(false);
if (request.isQueryCachePut()) {
request.addDependentTables(cquery.getDependentTables());
}
-
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
-
} finally {
if (cquery != null) {
cquery.close();
@@ -416,38 +380,27 @@ public class CQueryEngine {
*/
@SuppressWarnings("unchecked")
public T find(OrmQueryRequest request) {
-
EntityBean bean = null;
-
CQuery cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
-
try {
cquery.prepareBindExecuteQuery();
-
if (request.isLogSql()) {
logSql(cquery);
}
-
if (cquery.readBean()) {
bean = cquery.next();
}
-
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
-
if (request.isAuditReads()) {
cquery.auditFind(bean);
}
-
request.executeSecondaryQueries(false);
-
return (T) bean;
-
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
-
} finally {
cquery.close();
}
@@ -457,17 +410,13 @@ public class CQueryEngine {
* Log the generated SQL to the transaction log.
*/
private void logSql(CQuery> query) {
-
- String sql = query.getGeneratedSql();
- sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
- query.getTransaction().logSql(sql);
+ query.getTransaction().logSql(Str.add(query.getGeneratedSql(), "; --bind(", query.getBindLog(), ") --micros(", query.micros() + ")"));
}
/**
* Log the FindById summary to the transaction log.
*/
private void logFindBeanSummary(CQuery> q) {
-
SpiQuery> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
@@ -504,7 +453,6 @@ public class CQueryEngine {
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] bind[").append(q.getBindLog()).append("]");
-
q.getTransaction().logSummary(msg.toString());
}
@@ -512,7 +460,6 @@ public class CQueryEngine {
* Log the FindMany to the transaction log.
*/
private void logFindManySummary(CQuery> q) {
-
SpiQuery> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
@@ -551,7 +498,6 @@ public class CQueryEngine {
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
-
q.getTransaction().logSummary(msg.toString());
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java
index b68813bd4..a89cd28fa 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java
@@ -29,45 +29,19 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class);
private final CQueryPlan queryPlan;
-
- /**
- * The overall find request wrapper object.
- */
private final OrmQueryRequest> request;
-
private final BeanDescriptor> desc;
-
private final SpiQuery> query;
-
- /**
- * Where clause predicates.
- */
private final CQueryPredicates predicates;
-
- /**
- * The final sql that is generated.
- */
private final String sql;
-
private RsetDataReader dataReader;
-
- /**
- * The statement used to create the resultSet.
- */
private PreparedStatement pstmt;
-
private String bindLog;
-
private long executionTimeMicros;
-
private int rowCount;
-
private final ScalarDataReader> reader;
-
private final boolean containsCounts;
-
private long profileOffset;
-
private final ReentrantLock lock = new ReentrantLock();
/**
@@ -95,19 +69,20 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
.append("] type[").append(desc.getName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
-
return sb.toString();
}
+ long micros() {
+ return executionTimeMicros;
+ }
+
/**
* Execute the query returning the row count.
*/
List findList() throws SQLException {
-
long startNano = System.nanoTime();
try {
prepareExecute();
-
List result = new ArrayList<>();
while (dataReader.next()) {
Object value = reader.read(dataReader);
@@ -117,16 +92,13 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
result.add(value);
rowCount++;
}
-
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
}
getTransaction().profileEvent(this);
-
return result;
-
} finally {
close();
}
@@ -158,14 +130,12 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab
profileOffset = t.profileOffset();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
-
if (query.getBufferFetchSizeHint() > 0) {
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
-
bindLog = predicates.bind(pstmt, conn);
} finally {
lock.unlock();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java
index 295cbe7c2..de365f253 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java
@@ -13,12 +13,12 @@ import io.ebeaninternal.api.SpiQueryBindCapture;
import io.ebeaninternal.api.SpiQueryPlan;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
+import io.ebeaninternal.server.util.Md5;
import io.ebeaninternal.server.util.Str;
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.DataBindCapture;
import io.ebeaninternal.server.type.RsetDataReader;
-import io.ebeaninternal.server.util.Md5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,46 +54,31 @@ public class CQueryPlan implements SpiQueryPlan {
static final String RESULT_SET_BASED_RAW_SQL = "--ResultSetBasedRawSql";
private final SpiEbeanServer server;
-
private final ProfileLocation profileLocation;
-
private final String location;
-
private final String label;
-
private final String name;
-
private final CQueryPlanKey planKey;
-
private final boolean rawSql;
-
private final String sql;
private final String hash;
-
private final String logWhereSql;
-
private final SqlTree sqlTree;
/**
* Encrypted properties required additional binding.
*/
private final STreeProperty[] encryptedProps;
-
private final CQueryPlanStats stats;
-
private final Class> beanType;
-
final DataTimeZone dataTimeZone;
-
private final int asOfTableCount;
/**
* Key used to identify the query plan in audit logging.
*/
private volatile String auditQueryHash;
-
private final Set dependentTables;
-
private final SpiQueryBindCapture bindCapture;
/**
@@ -106,9 +91,9 @@ public class CQueryPlan implements SpiQueryPlan {
this.planKey = request.getQueryPlanKey();
SpiQuery> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
+ this.location = (profileLocation == null) ? null : profileLocation.location();
this.label = query.getPlanLabel();
this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName());
- this.location = location();
this.asOfTableCount = query.getAsOfTableCount();
this.sql = sqlRes.getSql();
this.sqlTree = sqlTree;
@@ -118,7 +103,7 @@ public class CQueryPlan implements SpiQueryPlan {
this.stats = new CQueryPlanStats(this);
this.dependentTables = sqlTree.dependentTables();
this.bindCapture = initBindCapture(query);
- this.hash = md5Hash();
+ this.hash = Md5.hash(sql, name, location);
}
/**
@@ -130,9 +115,9 @@ public class CQueryPlan implements SpiQueryPlan {
this.beanType = request.getBeanDescriptor().getBeanType();
SpiQuery> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
+ this.location = (profileLocation == null) ? null : profileLocation.location();
this.label = query.getPlanLabel();
this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName());
- this.location = location();
this.planKey = buildPlanKey(sql, logWhereSql);
this.asOfTableCount = 0;
this.sql = sql;
@@ -143,7 +128,7 @@ public class CQueryPlan implements SpiQueryPlan {
this.stats = new CQueryPlanStats(this);
this.dependentTables = sqlTree.dependentTables();
this.bindCapture = initBindCaptureRaw(sql, query);
- this.hash = md5Hash();
+ this.hash = Md5.hash(sql, name, location);
}
private String deriveName(String label, SpiQuery.Type type, String simpleName) {
@@ -169,10 +154,6 @@ public class CQueryPlan implements SpiQueryPlan {
return sql.equals(RESULT_SET_BASED_RAW_SQL) || query.getType().isUpdate() ? SpiQueryBindCapture.NOOP : server.createQueryBindCapture(this);
}
- private String location() {
- return (profileLocation == null) ? null : profileLocation.location();
- }
-
private CQueryPlanKey buildPlanKey(String sql, String logWhereSql) {
return new RawSqlQueryPlanKey(sql, false, logWhereSql);
}
@@ -276,21 +257,6 @@ public class CQueryPlan implements SpiQueryPlan {
return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey();
}
- /**
- * Return the MD5 hash of the sql.
- */
- private String md5Hash() {
- StringBuilder sb = new StringBuilder(sql)
- .append("|").append(name)
- .append("|").append(location);
- try {
- return Md5.hash(sb.toString());
- } catch (Exception e) {
- logger.error("Failed to MD5 hash the query", e);
- return "error";
- }
- }
-
SqlTree getSqlTree() {
return sqlTree;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java
index 86e15f389..315e7a07d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java
@@ -24,10 +24,10 @@ class CQueryPlanRequest {
CQueryPlanRequest(Connection connection, QueryPlanRequest req, Iterator iterator) {
this.connection = connection;
this.iterator = iterator;
- this.maxCount = req.getMaxCount();
- long reqSince = req.getSince();
+ this.maxCount = req.maxCount();
+ long reqSince = req.since();
this.since = (reqSince == 0) ? Long.MAX_VALUE: reqSince;
- long maxTimeMillis = req.getMaxTimeMillis();
+ long maxTimeMillis = req.maxTimeMillis();
this.maxTime = maxTimeMillis > 0 ? System.currentTimeMillis() + maxTimeMillis : 0;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java
index 23de6de5c..ffc749eb9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java
@@ -83,56 +83,56 @@ public final class CQueryPlanStats {
@Override
public String toString() {
- return "label:" + getLabel() + " location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql();
+ return "label:" + label() + " location:" + location() + " metrics:" + metrics + " sql:" + sql();
}
@Override
- public Class> getType() {
+ public Class> type() {
return queryPlan.getBeanType();
}
@Override
- public String getLabel() {
+ public String label() {
return queryPlan.getLabel();
}
@Override
- public String getName() {
+ public String name() {
return queryPlan.getName();
}
@Override
- public String getLocation() {
+ public String location() {
return queryPlan.getLocation();
}
@Override
- public long getCount() {
- return metrics.getCount();
+ public long count() {
+ return metrics.count();
}
@Override
- public long getTotal() {
- return metrics.getTotal();
+ public long total() {
+ return metrics.total();
}
@Override
- public long getMax() {
- return metrics.getMax();
+ public long max() {
+ return metrics.max();
}
@Override
- public long getMean() {
- return metrics.getMean();
+ public long mean() {
+ return metrics.mean();
}
@Override
- public String getHash() {
+ public String hash() {
return queryPlan.getHash();
}
@Override
- public String getSql() {
+ public String sql() {
return queryPlan.getSql();
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java
index 248fa9058..0c1df9483 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java
@@ -22,44 +22,17 @@ import java.util.concurrent.locks.ReentrantLock;
class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
private final CQueryPlan queryPlan;
-
- /**
- * The overall find request wrapper object.
- */
private final OrmQueryRequest> request;
-
private final BeanDescriptor> desc;
-
private final SpiQuery> query;
-
- /**
- * Where clause predicates.
- */
private final CQueryPredicates predicates;
-
- /**
- * The final sql that is generated.
- */
private final String sql;
-
- /**
- * The resultSet that is read and converted to objects.
- */
private ResultSet rset;
-
- /**
- * The statement used to create the resultSet.
- */
private PreparedStatement pstmt;
-
private String bindLog;
-
private long executionTimeMicros;
-
private int rowCount;
-
private long profileOffset;
-
private final ReentrantLock lock = new ReentrantLock();
/**
@@ -104,11 +77,14 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
return sql;
}
+ long micros() {
+ return executionTimeMicros;
+ }
+
/**
* Execute the query returning the row count.
*/
public int findCount() throws SQLException {
-
long startNano = System.nanoTime();
try {
SpiTransaction t = getTransaction();
@@ -118,24 +94,19 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
try {
query.checkCancelled();
pstmt = conn.prepareStatement(sql);
-
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
-
bindLog = predicates.bind(pstmt, conn);
} finally {
lock.unlock();
}
rset = pstmt.executeQuery();
query.checkCancelled();
-
if (!rset.next()) {
throw new PersistenceException("Expecting 1 row but got none?");
}
-
rowCount = rset.getInt(1);
-
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(executionTimeMicros)) {
@@ -143,7 +114,6 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
}
t.profileEvent(this);
return rowCount;
-
} finally {
close();
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java
index eec5d2e6f..302b1bcf0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java
@@ -19,36 +19,18 @@ import java.util.concurrent.locks.ReentrantLock;
class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
private final CQueryPlan queryPlan;
-
private final OrmQueryRequest> request;
-
private final BeanDescriptor> desc;
-
private final SpiQuery> query;
-
- /**
- * Where clause predicates.
- */
private final CQueryPredicates predicates;
-
- /**
- * The final sql that is generated.
- */
private final String sql;
-
- /**
- * The statement used to create the resultSet.
- */
private PreparedStatement pstmt;
-
private String bindLog;
-
private int rowCount;
-
private long profileOffset;
-
+ private long executionTimeMicros;
private final ReentrantLock lock = new ReentrantLock();
-
+
/**
* Create the Sql select based on the request.
*/
@@ -80,7 +62,6 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
* Execute the update or delete statement returning the row count.
*/
public int execute() throws SQLException {
-
long startNano = System.nanoTime();
try {
SpiTransaction t = getTransaction();
@@ -90,19 +71,16 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
try {
query.checkCancelled();
pstmt = conn.prepareStatement(sql);
-
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
-
bindLog = predicates.bind(pstmt, conn);
} finally {
lock.unlock();
}
rowCount = pstmt.executeUpdate();
query.checkCancelled();
-
- long executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
+ executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
@@ -115,6 +93,10 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
}
}
+ long micros() {
+ return executionTimeMicros;
+ }
+
private SpiTransaction getTransaction() {
return request.getTransaction();
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java
index 8065a62dc..1c1a41d07 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java
@@ -16,8 +16,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
private final String sql;
private final String bind;
private final String plan;
-
- private String hash;
+ private final String hash;
private long queryTimeMicros;
private long captureCount;
@@ -32,7 +31,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
}
@Override
- public String getHash() {
+ public String hash() {
return hash;
}
@@ -40,7 +39,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the associated bean.
*/
@Override
- public Class> getBeanType() {
+ public Class> beanType() {
return beanType;
}
@@ -48,12 +47,12 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the query label if set.
*/
@Override
- public String getLabel() {
+ public String label() {
return label;
}
@Override
- public ProfileLocation getProfileLocation() {
+ public ProfileLocation profileLocation() {
return profileLocation;
}
@@ -61,7 +60,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the sql of query.
*/
@Override
- public String getSql() {
+ public String sql() {
return sql;
}
@@ -69,7 +68,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return a description of the bind values used.
*/
@Override
- public String getBind() {
+ public String bind() {
return bind;
}
@@ -77,7 +76,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the query plan.
*/
@Override
- public String getPlan() {
+ public String plan() {
return plan;
}
@@ -86,7 +85,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* to build the query plan.
*/
@Override
- public long getQueryTimeMicros() {
+ public long queryTimeMicros() {
return queryTimeMicros;
}
@@ -94,7 +93,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
* Return the total count of times bind capture has occurred.
*/
@Override
- public long getCaptureCount() {
+ public long captureCount() {
return captureCount;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java
index ac1386e11..83c7b2936 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java
@@ -18,19 +18,15 @@ public class SqlBeanLoad {
private final DbReadContext ctx;
private final EntityBean bean;
private final EntityBeanIntercept ebi;
-
private final Class> type;
private final boolean lazyLoading;
- private final boolean refreshLoading;
private final boolean rawSql;
SqlBeanLoad(DbReadContext ctx, Class> type, EntityBean bean, Mode queryMode) {
-
this.ctx = ctx;
this.rawSql = ctx.isRawSql();
this.type = type;
this.lazyLoading = queryMode == Mode.LAZYLOAD_BEAN;
- this.refreshLoading = queryMode == Mode.REFRESH_BEAN;
this.bean = bean;
this.ebi = bean == null ? null : bean._ebean_getIntercept();
}
@@ -50,34 +46,21 @@ public class SqlBeanLoad {
}
public Object load(BeanProperty prop) {
-
if (!rawSql && !prop.isLoadProperty(ctx.isDraftQuery())) {
return null;
}
-
if ((bean == null)
|| (lazyLoading && ebi.isLoadedProperty(prop.getPropertyIndex()))
|| (type != null && !prop.isAssignableFrom(type))) {
-
// ignore this property
// ... null: bean already in persistence context
// ... lazyLoading: partial bean that is lazy loading
// ... type: inheritance and not assignable to this instance
-
prop.loadIgnore(ctx);
return null;
}
-
try {
- Object dbVal = prop.read(ctx);
- if (!refreshLoading) {
- prop.setValue(bean, dbVal);
- } else {
- prop.setValueIntercept(bean, dbVal);
- }
-
- return dbVal;
-
+ return prop.readSet(ctx, bean);
} catch (Exception e) {
bean._ebean_getIntercept().setLoadError(prop.getPropertyIndex(), e);
ctx.handleLoadError(prop.getFullBeanName(), e);
@@ -89,10 +72,6 @@ public class SqlBeanLoad {
* Load the given value into the property.
*/
public void load(BeanProperty target, Object dbVal) {
- if (!refreshLoading) {
- target.setValue(bean, dbVal);
- } else {
- target.setValueIntercept(bean, dbVal);
- }
+ target.setValue(bean, dbVal);
}
}
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 e4c936e45..06465355c 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/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 3a81183e9..db3a605ea 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,29 +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.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;
@@ -66,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;
@@ -91,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;
@@ -1269,22 +1218,13 @@ 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 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 queryBindKey(BindValuesKey key) {
+ key.add(id);
+ 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);
}
/**
@@ -1298,8 +1238,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
- int hc = queryBindHash();
- return new HashQuery(queryPlanKey, hc);
+ BindValuesKey bindKey = new BindValuesKey();
+ queryBindKey(bindKey);
+ return new HashQuery(queryPlanKey, bindKey);
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java b/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java
index 0b5eb4ad6..5a405dbc4 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.query.DefaultSqlRow;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
+import java.sql.Types;
public class DRawSqlService implements SpiRawSqlService {
@@ -48,7 +49,26 @@ public class DRawSqlService implements SpiRawSqlService {
if (ret.containsKey(name)) {
name = combine(meta.getSchemaName(i), meta.getTableName(i), name);
}
- ret.put(name, resultSet.getObject(i));
+
+ // convert (C/B)LOBs to java objects.
+ // A java.sql.Clob depends on an open connection, so storing this object in a map
+ // that is accessed later, when the connection is closed, will result in a "connection is closed" exception.
+ // From the java.sql.Clob documentation: "... which means that a Clob object contains a logical pointer to the SQL CLOB
+ // data rather than the data itself."
+ switch (meta.getColumnType(i)) {
+ case Types.CLOB:
+ case Types.NCLOB:
+ ret.put(name, resultSet.getString(i));
+ break;
+
+ case Types.BLOB:
+ ret.put(name, resultSet.getBytes(i));
+ break;
+
+ default:
+ ret.put(name, resultSet.getObject(i));
+ break;
+ }
}
return ret;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java
index 2959c872c..a1a9ec8cf 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java
@@ -36,6 +36,7 @@ public class DataBind implements DataBinder {
private List inputStreams;
protected int pos;
+ private String json;
public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt, Connection connection) {
this.dataTimeZone = dataTimeZone;
@@ -43,6 +44,19 @@ public class DataBind implements DataBinder {
this.connection = connection;
}
+ @Override
+ public void pushJson(String json) {
+ assert this.json == null; // we can only push one value
+ this.json = json;
+ }
+
+ @Override
+ public String popJson() {
+ String ret = json;
+ json = null;
+ return ret;
+ }
+
@Override
public StringBuilder append(Object entry) {
return bindLog.append(entry);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
index 82c3ff46a..1400b2180 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.JsonConfig;
@@ -135,7 +134,7 @@ public final class DefaultTypeManager implements TypeManager {
this.postgres = isPostgres(config.getDatabasePlatform());
this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent();
this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null;
- this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.isJsonDirtyByDefault()) : null;
+ this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.getJsonMutationDetection()) : null;
this.extraTypeFactory = new DefaultTypeFactory(config);
this.arrayTypeListFactory = arrayTypeListFactory(config.getDatabasePlatform());
this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform());
@@ -426,7 +425,7 @@ public final class DefaultTypeManager implements TypeManager {
if (objectMapper == null) {
throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present");
}
- return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, (AnnotatedField) prop.getJacksonField(), dbType, docType);
+ return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, prop, dbType, docType);
}
/**
@@ -557,7 +556,7 @@ public final class DefaultTypeManager implements TypeManager {
// no override or further mapping required
return scalarType;
}
- ScalarTypeEnum> scalarEnum = (ScalarTypeEnum>)scalarType;
+ ScalarTypeEnum> scalarEnum = (ScalarTypeEnum>) scalarType;
if (scalarEnum != null && !scalarEnum.isOverrideBy(type)) {
if (type != null && !scalarEnum.isCompatible(type)) {
throw new IllegalStateException("Error mapping Enum type:" + enumType + " It is mapped using 2 different modes when only one is supported (ORDINAL, STRING or an Ebean mapping)");
@@ -674,7 +673,7 @@ public final class DefaultTypeManager implements TypeManager {
private Object initObjectMapper(DatabaseConfig config) {
Object objectMapper = config.getObjectMapper();
if (objectMapper == null) {
- objectMapper = new ObjectMapper();
+ objectMapper = InitObjectMapper.init();
config.setObjectMapper(objectMapper);
}
return objectMapper;
@@ -750,12 +749,15 @@ public final class DefaultTypeManager implements TypeManager {
}
private void initialiseJavaTimeTypes(DatabaseConfig config) {
+
+ ZoneId zoneId = getZoneId(config);
+
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
addType(java.time.Period.class, new ScalarTypePeriod());
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime));
- addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime));
- addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime));
+ addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime, zoneId));
+ addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime, zoneId));
addType(Instant.class, new ScalarTypeInstant(jsonDateTime));
addType(DayOfWeek.class, new ScalarTypeDayOfWeek());
addType(Month.class, new ScalarTypeMonth());
@@ -771,6 +773,11 @@ public final class DefaultTypeManager implements TypeManager {
addType(Duration.class, (durationNanos) ? new ScalarTypeDurationWithNanos() : new ScalarTypeDuration());
}
+ private ZoneId getZoneId(DatabaseConfig config) {
+ final String dataTimeZone = config.getDataTimeZone();
+ return (dataTimeZone == null) ? ZoneOffset.systemDefault() : TimeZone.getTimeZone(dataTimeZone).toZoneId();
+ }
+
private void addType(Class> clazz, ScalarType> scalarType) {
typeMap.put(clazz, scalarType);
logicalMap.putIfAbsent(clazz.getSimpleName(), scalarType);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java
new file mode 100644
index 000000000..90a86ec79
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java
@@ -0,0 +1,22 @@
+package io.ebeaninternal.server.type;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Initialise the Jackson ObjectMapper.
+ */
+class InitObjectMapper {
+
+ /**
+ * Create and return the default ObjectMapper.
+ */
+ static Object init() {
+ SimpleModule module = new SimpleModule();
+ module.addAbstractTypeMapping(Set.class, LinkedHashSet.class);
+ return new ObjectMapper().registerModule(module);
+ }
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java
index 9763a5667..b85c8e688 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java
@@ -20,22 +20,29 @@ import java.util.Calendar;
public class RsetDataReader implements DataReader {
private static final int bufferSize = 512;
-
static final int clobBufferSize = 512;
-
static final int stringInitialSize = 512;
private final DataTimeZone dataTimeZone;
-
private final ResultSet rset;
-
protected int pos;
+ private String json;
public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) {
this.dataTimeZone = dataTimeZone;
this.rset = rset;
}
+ @Override
+ public void pushJson(String json) {
+ this.json = json;
+ }
+
+ @Override
+ public String popJson() {
+ return json;
+ }
+
@Override
public void close() throws SQLException {
rset.close();
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 5e24412b5..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
@@ -7,14 +7,13 @@ import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
-import io.ebeaninternal.json.ModifyAwareList;
-import io.ebeaninternal.json.ModifyAwareMap;
-import io.ebeaninternal.json.ModifyAwareSet;
+import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import javax.persistence.PersistenceException;
import java.io.DataInput;
@@ -22,9 +21,6 @@ import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* Supports @DbJson properties using Jackson ObjectMapper.
@@ -34,81 +30,85 @@ class ScalarTypeJsonObjectMapper {
/**
* Create and return the appropriate ScalarType.
*/
- static ScalarType> createTypeFor(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
- Class> type = field.getRawType();
- if (Set.class.equals(type)) {
- return new OmSet(jsonManager, field, dbType, docType);
+ static ScalarType> createTypeFor(TypeJsonManager jsonManager, DeployBeanProperty prop, int dbType, DocPropertyType docType) {
+ AnnotatedField field = (AnnotatedField) prop.getJacksonField();
+ MutationDetection mode = prop.getMutationDetection();
+ if (mode == MutationDetection.NONE) {
+ return new NoMutationDetection(jsonManager, field, dbType, docType);
+ } else if (mode != MutationDetection.DEFAULT) {
+ return new GenericObject(jsonManager, field, dbType, docType);
}
- if (List.class.equals(type)) {
- return new OmList(jsonManager, field, dbType, docType);
- }
- if (Map.class.equals(type)) {
- return new OmMap(jsonManager, field, dbType);
- }
- return new GenericObject(jsonManager, field, dbType, type);
+ // using the global default MutationDetection mode (defaults to HASH)
+ prop.setMutationDetection(jsonManager.mutationDetection());
+ return new GenericObject(jsonManager, field, dbType, docType);
}
/**
- * Maps any type (Object) using Jackson ObjectMapper.
+ * No mutation detection on this json property.
+ */
+ private static class NoMutationDetection extends Base {
+
+ NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
+ super(Object.class, jsonManager, field, dbType, docType);
+ }
+
+ @Override
+ public boolean isMutable() {
+ return false;
+ }
+
+ @Override
+ public boolean isDirty(Object value) {
+ return false;
+ }
+ }
+
+ /**
+ * Supports HASH and SOURCE dirty detection modes.
*/
private static class GenericObject extends Base {
- GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class> rawType) {
- super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType);
- }
- }
-
- /**
- * Type for Sets wrapping the ObjectMapper Set as a ModifyAwareSet.
- */
- @SuppressWarnings("rawtypes")
- private static class OmSet extends Base {
-
- OmSet(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
- super(Set.class, jsonManager, field, dbType, docType);
+ GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
+ super(Object.class, jsonManager, field, dbType, docType);
}
@Override
- @SuppressWarnings("unchecked")
- public Set read(DataReader reader) throws SQLException {
- Set value = super.read(reader);
- return value == null ? null : new ModifyAwareSet(value);
- }
- }
-
- /**
- * Type for Lists wrapping the ObjectMapper List as a ModifyAwareList.
- */
- @SuppressWarnings("rawtypes")
- private static class OmList extends Base {
-
- OmList(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
- super(List.class, jsonManager, field, dbType, docType);
+ public boolean isJsonMapper() {
+ return true;
}
@Override
- @SuppressWarnings("unchecked")
- public List read(DataReader reader) throws SQLException {
- List value = super.read(reader);
- return value == null ? null : new ModifyAwareList(value);
- }
- }
-
- /**
- * Type for Map wrapping the ObjectMapper Map as a ModifyAwareMap.
- */
- @SuppressWarnings("rawtypes")
- private static class OmMap extends Base {
-
- OmMap(TypeJsonManager jsonManager, AnnotatedField field, int dbType) {
- super(Map.class, jsonManager, field, dbType, DocPropertyType.OBJECT);
+ 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;
+ }
+ try {
+ return objectReader.readValue(json, deserType);
+ } catch (IOException e) {
+ throw new TextException("Failed to parse JSON [{}] as " + deserType, json, e);
+ }
}
@Override
- @SuppressWarnings("unchecked")
- public Map read(DataReader reader) throws SQLException {
- Map value = super.read(reader);
- return value == null ? null : new ModifyAwareMap(value);
+ public void bind(DataBinder binder, Object value) throws SQLException {
+ // popJson as dirty detection already converted to json string
+ String rawJson = binder.popJson();
+ if (rawJson == null && value != null) {
+ rawJson = formatValue(value); // not expected, need to check?
+ }
+ if (pgType != null) {
+ binder.setObject(PostgresHelper.asObject(pgType, rawJson));
+ } else {
+ if (value == null) {
+ // use varchar, otherwise SqlServer/db2 will fail with 'Invalid JDBC data type 5.001.'
+ binder.setNull(Types.VARCHAR);
+ } else {
+ binder.setString(rawJson);
+ }
+ }
}
}
@@ -118,44 +118,27 @@ class ScalarTypeJsonObjectMapper {
*/
private static abstract class Base extends ScalarTypeBase {
- private final ObjectWriter objectWriter;
- private final ObjectMapper objectReader;
- private final JavaType deserType;
- private final String pgType;
+ protected final ObjectWriter objectWriter;
+ protected final ObjectMapper objectReader;
+ protected final JavaType deserType;
+ protected final String pgType;
private final DocPropertyType docType;
- private final TypeJsonManager.DirtyHandler dirtyHandler;
Base(Class cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
- this(cls, jsonManager, field, dbType, docType, cls);
- }
-
- Base(Class cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType, Class> rawType) {
super(cls, false, dbType);
this.objectReader = jsonManager.objectMapper();
this.pgType = jsonManager.postgresType(dbType);
this.docType = docType;
- this.dirtyHandler = jsonManager.dirtyHandler(cls, rawType);
final JacksonTypeHelper helper = new JacksonTypeHelper(field, objectReader);
this.deserType = helper.type();
this.objectWriter = helper.objectWriter();
}
- /**
- * Consider as a mutable type. Use the isDirty() method to check for dirty state.
- */
@Override
public boolean isMutable() {
return true;
}
- /**
- * Return true if the value should be considered dirty (and included in an update).
- */
- @Override
- public boolean isDirty(Object value) {
- return dirtyHandler.isDirty(value);
- }
-
@Override
public T read(DataReader reader) throws SQLException {
String json = reader.getString();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java
index b62c693d3..b68f3eba9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java
@@ -15,8 +15,11 @@ import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso;
*/
public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime {
- public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode) {
+ private final ZoneId zoneId;
+
+ public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode, ZoneId zoneId) {
super(mode, OffsetDateTime.class, false, Types.TIMESTAMP);
+ this.zoneId = zoneId;
}
@Override
@@ -46,7 +49,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime {
- public ScalarTypeZonedDateTime(JsonConfig.DateTime mode) {
+ private final ZoneId zoneId;
+
+ public ScalarTypeZonedDateTime(JsonConfig.DateTime mode, ZoneId zoneId) {
super(mode, ZonedDateTime.class, false, Types.TIMESTAMP);
+ this.zoneId = zoneId;
}
@Override
@@ -44,7 +47,7 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime cls, Class> rawType) {
- if (!Object.class.equals(cls) || ModifyAwareType.class.isAssignableFrom(rawType)) {
- // Set, List and Map are modify aware
- return modifyAwareHandler;
- }
- return defaultHandler;
- }
-
/**
* Return true if the value should be considered dirty (and included in an update).
*/
@@ -71,28 +58,4 @@ class TypeJsonManager {
}
}
- static final class ModifyAwareHandler implements DirtyHandler {
- @Override
- public boolean isDirty(Object value) {
- return checkModifyAware(value);
- }
- }
-
- /**
- * Effectively constant based on {@link DatabaseConfig#isJsonDirtyByDefault()}
- */
- static final class DefaultHandler implements DirtyHandler {
-
- private final boolean dirty;
-
- DefaultHandler(boolean dirty) {
- this.dirty = dirty;
- }
-
- @Override
- public boolean isDirty(Object value) {
- return dirty;
- }
- }
-
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java
new file mode 100644
index 000000000..8295703c1
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java
@@ -0,0 +1,20 @@
+package io.ebeaninternal.server.util;
+
+import java.nio.charset.StandardCharsets;
+import java.util.zip.CRC32;
+
+/**
+ * Compute a checksum for String content. Use when we desire cheaper option than MD5.
+ */
+public final class Checksum {
+
+ /**
+ * Return the checksum for the given String input.
+ */
+ public static long checksum(String input) {
+ CRC32 checksum = new CRC32();
+ final byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
+ checksum.update(bytes, 0, bytes.length);
+ return checksum.getValue();
+ }
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java
index 1c848f55d..45df901f0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java
@@ -8,10 +8,15 @@ public final class Md5 {
/**
* Return the MD5 hash of the underlying sql.
*/
- public static String hash(String content) {
+ public static String hash(String... values) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
- return digestToHex(md.digest(content.getBytes(StandardCharsets.UTF_8)));
+ for (String val : values) {
+ if (val != null) {
+ md.update(val.getBytes(StandardCharsets.UTF_8));
+ }
+ }
+ return digestToHex(md.digest());
} catch (Exception e) {
throw new RuntimeException("MD5 hashing failed", e);
}
@@ -21,8 +26,7 @@ public final class Md5 {
* Convert the digest into a hex value.
*/
private static String digestToHex(byte[] digest) {
-
- StringBuilder sb = new StringBuilder();
+ StringBuilder sb = new StringBuilder(32);
for (byte aDigest : digest) {
sb.append(Integer.toString((aDigest & 0xff) + 0x100, 16).substring(1));
}
diff --git a/ebean-core/src/test/java/io/ebean/BaseTestCase.java b/ebean-core/src/test/java/io/ebean/BaseTestCase.java
index 0205d5294..988e73b59 100644
--- a/ebean-core/src/test/java/io/ebean/BaseTestCase.java
+++ b/ebean-core/src/test/java/io/ebean/BaseTestCase.java
@@ -90,14 +90,14 @@ public abstract class BaseTestCase {
}
protected List visitTimedMetrics() {
- return collectMetrics().getTimedMetrics();
+ return collectMetrics().timedMetrics();
}
protected List sqlMetrics() {
List timedMetrics = visitTimedMetrics();
return timedMetrics.stream()
- .filter((it) -> it.getName().startsWith("sql.") || it.getName().startsWith("orm."))
+ .filter((it) -> it.name().startsWith("sql.") || it.name().startsWith("orm."))
.collect(Collectors.toList());
}
diff --git a/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java b/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java
index ba30e9a5c..625cc9ae9 100644
--- a/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java
+++ b/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java
@@ -191,13 +191,13 @@ public class DtoQuery2Test extends BaseTestCase {
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true);
server().getMetaInfoManager().visitMetrics(basic);
- List stats = basic.getQueryMetrics();
+ List stats = basic.queryMetrics();
assertThat(stats).hasSize(1);
MetaQueryMetric queryMetric = stats.get(0);
- assertThat(queryMetric.getLabel()).isEqualTo("basic");
- assertThat(queryMetric.getCount()).isEqualTo(3);
- assertThat(queryMetric.getName()).isEqualTo("dto.DCust_basic");
+ assertThat(queryMetric.label()).isEqualTo("basic");
+ assertThat(queryMetric.count()).isEqualTo(3);
+ assertThat(queryMetric.name()).isEqualTo("dto.DCust_basic");
server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name")
@@ -207,7 +207,7 @@ public class DtoQuery2Test extends BaseTestCase {
BasicMetricVisitor metric2 = server().getMetaInfoManager().visitBasic();
- stats = metric2.getQueryMetrics();
+ stats = metric2.queryMetrics();
assertThat(stats).hasSize(2);
log.info("stats " + stats);
diff --git a/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java b/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java
index ee26e9409..28b0bd490 100644
--- a/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java
+++ b/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java
@@ -27,12 +27,12 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
@AfterClass
public static void reportStats() {
ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics();
- for (MetaQueryMetric metric : metrics.getQueryMetrics()) {
+ for (MetaQueryMetric metric : metrics.queryMetrics()) {
System.out.println(metric);
}
System.out.println("-- transaction metrics --");
- for (MetaTimedMetric metric : metrics.getTimedMetrics()) {
+ for (MetaTimedMetric metric : metrics.timedMetrics()) {
System.out.println(metric);
}
}
@@ -59,15 +59,15 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
ServerMetrics metrics = collectMetrics();
- List stats = metrics.getQueryMetrics();
+ List stats = metrics.queryMetrics();
for (MetaQueryMetric stat : stats) {
- long meanMicros = stat.getMean();
+ long meanMicros = stat.mean();
assertThat(meanMicros).isLessThan(900_000);
- assertThat(stat.getLocation()).isSameAs(loc0.location());
+ assertThat(stat.location()).isSameAs(loc0.location());
}
assertThat(stats).hasSize(1);
- assertThat(stats.get(0).getCount()).isEqualTo(4);
+ assertThat(stats.get(0).count()).isEqualTo(4);
}
@ForPlatform(Platform.H2)
diff --git a/ebean-core/src/test/java/io/ebean/DtoQueryTest.java b/ebean-core/src/test/java/io/ebean/DtoQueryTest.java
index eea1c6da3..0331a8215 100644
--- a/ebean-core/src/test/java/io/ebean/DtoQueryTest.java
+++ b/ebean-core/src/test/java/io/ebean/DtoQueryTest.java
@@ -42,14 +42,14 @@ public class DtoQueryTest extends BaseTestCase {
ServerMetrics metrics = collectMetrics();
- List stats = metrics.getQueryMetrics();
+ List stats = metrics.queryMetrics();
for (MetaQueryMetric stat : stats) {
- long meanMicros = stat.getMean();
+ long meanMicros = stat.mean();
assertThat(meanMicros).isLessThan(900_000);
}
assertThat(stats).hasSize(1);
- assertThat(stats.get(0).getCount()).isEqualTo(1);
+ assertThat(stats.get(0).count()).isEqualTo(1);
}
@Test
@@ -283,13 +283,13 @@ public class DtoQueryTest extends BaseTestCase {
BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true);
server().getMetaInfoManager().visitMetrics(basic);
- List stats = basic.getQueryMetrics();
+ List stats = basic.queryMetrics();
assertThat(stats).hasSize(1);
MetaQueryMetric queryMetric = stats.get(0);
- assertThat(queryMetric.getLabel()).isEqualTo("basic");
- assertThat(queryMetric.getCount()).isEqualTo(3);
- assertThat(queryMetric.getName()).isEqualTo("dto.DCust_basic");
+ assertThat(queryMetric.label()).isEqualTo("basic");
+ assertThat(queryMetric.count()).isEqualTo(3);
+ assertThat(queryMetric.name()).isEqualTo("dto.DCust_basic");
server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name")
@@ -299,7 +299,7 @@ public class DtoQueryTest extends BaseTestCase {
ServerMetrics metric2 = server().getMetaInfoManager().collectMetrics();
- stats = metric2.getQueryMetrics();
+ stats = metric2.queryMetrics();
assertThat(stats).hasSize(2);
log.info("stats " + stats);
diff --git a/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java b/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java
index 1e1900a13..66af5d164 100644
--- a/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java
+++ b/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java
@@ -11,7 +11,7 @@ import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
public class EbeanServer_refresh {
@@ -39,8 +39,11 @@ public class EbeanServer_refresh {
assertEquals(rows, 1);
+ basic.setName("modify");
+ assertTrue(DB.getBeanState(basic).isDirty());
server.refresh(basic);
assertEquals(basic.getStatus(), EBasic.Status.ACTIVE);
+ assertFalse(DB.getBeanState(basic).isDirty());
}
@Test
diff --git a/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java b/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java
index ca2caa4c8..5b9c42991 100644
--- a/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java
+++ b/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java
@@ -39,10 +39,10 @@ public class UpdateQueryTest extends BaseTestCase {
assertSql(query).contains("update o_customer set status=?, updtime=? where status = ? and id > ?");
ServerMetrics metrics = collectMetrics();
- List ormQueryMetrics = metrics.getQueryMetrics();
+ List ormQueryMetrics = metrics.queryMetrics();
assertThat(ormQueryMetrics).hasSize(1);
- assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class);
- assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateActive");
+ assertThat(ormQueryMetrics.get(0).type()).isEqualTo(Customer.class);
+ assertThat(ormQueryMetrics.get(0).label()).isEqualTo("updateActive");
}
@Test
@@ -69,10 +69,10 @@ public class UpdateQueryTest extends BaseTestCase {
assertSql(sql.get(0)).contains("update o_customer set status = status");
ServerMetrics metrics = collectMetrics();
- List ormQueryMetrics = metrics.getQueryMetrics();
+ List ormQueryMetrics = metrics.queryMetrics();
assertThat(ormQueryMetrics).hasSize(1);
- assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class);
- assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateAll");
+ assertThat(ormQueryMetrics.get(0).type()).isEqualTo(Customer.class);
+ assertThat(ormQueryMetrics.get(0).label()).isEqualTo("updateAll");
}
@Test
diff --git a/ebean-core/src/test/java/io/ebean/common/BeanListTest.java b/ebean-core/src/test/java/io/ebean/common/BeanListTest.java
index 11a7429d7..03c08efad 100644
--- a/ebean-core/src/test/java/io/ebean/common/BeanListTest.java
+++ b/ebean-core/src/test/java/io/ebean/common/BeanListTest.java
@@ -4,7 +4,6 @@ import io.ebean.bean.BeanCollection;
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -13,9 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BeanListTest {
- private Object object1 = new Object();
- private Object object2 = new Object();
- private Object object3 = new Object();
+ private final Object object1 = new Object();
+ private final Object object2 = new Object();
+ private final Object object3 = new Object();
private List all() {
List all = new ArrayList<>();
@@ -33,7 +32,7 @@ public class BeanListTest {
}
@Test
- public void test_setModifyListening_null() throws Exception {
+ public void test_setModifyListening_null() {
BeanList list = new BeanList<>();
list.setModifyListening(null);
@@ -45,7 +44,7 @@ public class BeanListTest {
}
@Test
- public void test_setModifyListening_none() throws Exception {
+ public void test_setModifyListening_none() {
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.NONE);
@@ -57,28 +56,28 @@ public class BeanListTest {
}
@Test
- public void testAdd() throws Exception {
+ public void testAdd() {
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.add(object1);
- assertThat(list.getModifyAdditions()).containsExactly(object1);
+ assertThat(list.getModifyAdditions()).containsOnly(object1);
assertThat(list.getModifyRemovals()).isEmpty();
list.add(object1);
- assertThat(list.getModifyAdditions()).containsExactly(object1);
+ assertThat(list.getModifyAdditions()).containsOnly(object1);
list.add(object2);
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2);
list.remove(object1);
- assertThat(list.getModifyAdditions()).containsExactly(object2);
+ assertThat(list.getModifyAdditions()).containsOnly(object2);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void testAddAll_given_emptyStart() throws Exception {
+ public void testAddAll_given_emptyStart() {
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -86,12 +85,12 @@ public class BeanListTest {
// act
list.addAll(all());
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void test_removals_DeleteThenAddBack_expect_noChange() throws Exception {
+ public void test_removals_DeleteThenAddBack_expect_noChange() {
BeanList list = new BeanList<>(some());
list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS);
@@ -106,33 +105,33 @@ public class BeanListTest {
}
@Test
- public void test_sort_whenAll_expect_noChange() throws Exception {
+ public void test_sort_whenAll_expect_noChange() {
BeanList list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
// act
- Collections.sort(list, Comparator.comparingInt(Object::hashCode));
+ list.sort(Comparator.comparingInt(Object::hashCode));
assertThat(list.getModifyRemovals()).isEmpty();
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
- public void test_sort_whenRemovals_expect_noChange() throws Exception {
+ public void test_sort_whenRemovals_expect_noChange() {
BeanList list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS);
// act
- Collections.sort(list, Comparator.comparingInt(Object::hashCode));
+ list.sort(Comparator.comparingInt(Object::hashCode));
assertThat(list.getModifyRemovals()).isEmpty();
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
- public void testAdd_given_someAlreadyIn() throws Exception {
+ public void testAdd_given_someAlreadyIn() {
BeanList list = new BeanList<>(some());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -143,12 +142,12 @@ public class BeanListTest {
assertThat(list.contains(object2)).isTrue();
list.add(object2); // object2 added as List allows duplicates
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void testAddSome_given_someAlreadyIn() throws Exception {
+ public void testAddSome_given_someAlreadyIn() {
BeanList list = new BeanList<>(some());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -156,43 +155,43 @@ public class BeanListTest {
// act
list.addAll(all());
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemove_given_beansInAdditions() throws Exception {
+ public void testRemove_given_beansInAdditions() {
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.addAll(all());
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
list.remove(object2);
list.remove(object3);
- assertThat(list.getModifyAdditions()).containsExactly(object1);
+ assertThat(list.getModifyAdditions()).containsOnly(object1);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemoveAll_given_beansInAdditions() throws Exception {
+ public void testRemoveAll_given_beansInAdditions() {
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.addAll(all());
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
list.removeAll(some());
- assertThat(list.getModifyAdditions()).containsExactly(object1);
+ assertThat(list.getModifyAdditions()).containsOnly(object1);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemove_given_beansNotInAdditions() throws Exception {
+ public void testRemove_given_beansNotInAdditions() {
BeanList list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -203,11 +202,11 @@ public class BeanListTest {
// assert
assertThat(list.getModifyAdditions()).isEmpty();
- assertThat(list.getModifyRemovals()).containsExactly(object2, object3);
+ assertThat(list.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
- public void testRemoveAll_given_beansNotInAdditions() throws Exception {
+ public void testRemoveAll_given_beansNotInAdditions() {
BeanList list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -217,11 +216,11 @@ public class BeanListTest {
// assert
assertThat(list.getModifyAdditions()).isEmpty();
- assertThat(list.getModifyRemovals()).containsExactly(object2, object3);
+ assertThat(list.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
- public void testClear() throws Exception {
+ public void testClear() {
BeanList list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -230,12 +229,12 @@ public class BeanListTest {
list.clear();
//assert
- assertThat(list.getModifyRemovals()).containsExactly(object1, object2, object3);
+ assertThat(list.getModifyRemovals()).containsOnly(object1, object2, object3);
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
- public void testClear_given_someBeansInAdditions() throws Exception {
+ public void testClear_given_someBeansInAdditions() {
BeanList list = new BeanList<>();
list.add(object1);
@@ -247,27 +246,27 @@ public class BeanListTest {
list.clear();
//assert
- assertThat(list.getModifyRemovals()).containsExactly(object1);
+ assertThat(list.getModifyRemovals()).containsOnly(object1);
assertThat(list.getModifyAdditions()).isEmpty();
}
@Test
- public void testRetainAll_given_beansInAdditions() throws Exception {
+ public void testRetainAll_given_beansInAdditions() {
BeanList list = new BeanList<>();
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
list.addAll(all());
- assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
list.retainAll(some());
- assertThat(list.getModifyAdditions()).containsExactly(object2, object3);
+ assertThat(list.getModifyAdditions()).containsOnly(object2, object3);
assertThat(list.getModifyRemovals()).isEmpty();
}
@Test
- public void testRetainAll_given_someBeansInAdditions() throws Exception {
+ public void testRetainAll_given_someBeansInAdditions() {
BeanList list = new BeanList<>();
list.add(object1);
@@ -278,12 +277,12 @@ public class BeanListTest {
// act
list.retainAll(some());
- assertThat(list.getModifyAdditions()).containsExactly(object3);
- assertThat(list.getModifyRemovals()).containsExactly(object1);
+ assertThat(list.getModifyAdditions()).containsOnly(object3);
+ assertThat(list.getModifyRemovals()).containsOnly(object1);
}
@Test
- public void testRetainAll_given_noBeansInAdditions() throws Exception {
+ public void testRetainAll_given_noBeansInAdditions() {
BeanList list = new BeanList<>(all());
list.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -291,7 +290,7 @@ public class BeanListTest {
// act
list.retainAll(some());
- assertThat(list.getModifyRemovals()).containsExactly(object1);
+ assertThat(list.getModifyRemovals()).containsOnly(object1);
}
@Test
diff --git a/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java b/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java
index 003539a72..3101fc0f2 100644
--- a/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java
+++ b/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java
@@ -34,30 +34,30 @@ public class BeanMapTest {
}
@Test
- public void testAdd() throws Exception {
+ public void testAdd() {
BeanMap map = new BeanMap<>();
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
map.put("1", object1);
map.put("4", null);
- assertThat(map.getModifyAdditions()).containsExactly(object1);
+ assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
map.put("1", object1);
map.put("4", null);
- assertThat(map.getModifyAdditions()).containsExactly(object1);
+ assertThat(map.getModifyAdditions()).containsOnly(object1);
map.put("2", object2);
- assertThat(map.getModifyAdditions()).containsExactly(object1, object2);
+ assertThat(map.getModifyAdditions()).containsOnly(object1, object2);
map.remove("1");
- assertThat(map.getModifyAdditions()).containsExactly(object2);
+ assertThat(map.getModifyAdditions()).containsOnly(object2);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
- public void testAddAll_given_emptyStart() throws Exception {
+ public void testAddAll_given_emptyStart() {
BeanMap set = new BeanMap<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -65,28 +65,28 @@ public class BeanMapTest {
// act
set.putAll(all());
- assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testAdd_given_someAlreadyIn() throws Exception {
+ public void testAdd_given_someAlreadyIn() {
BeanMap map = new BeanMap<>(some());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
// act
- assertThat(map.values().contains(object1)).isFalse();
+ assertThat(map.containsValue(object1)).isFalse();
map.put("1", object1);
- assertThat(map.values().contains(object2)).isTrue();
+ assertThat(map.containsValue(object2)).isTrue();
map.put("2", object2);
- assertThat(map.getModifyAdditions()).containsExactly(object1);
+ assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
- public void testAddSome_given_someAlreadyIn() throws Exception {
+ public void testAddSome_given_someAlreadyIn() {
BeanMap map = new BeanMap<>(some());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -94,44 +94,44 @@ public class BeanMapTest {
// act
map.putAll(all());
- assertThat(map.getModifyAdditions()).containsExactly(object1);
+ assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemove_given_beansInAdditions() throws Exception {
+ public void testRemove_given_beansInAdditions() {
BeanMap map = new BeanMap<>();
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
map.putAll(all());
- assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
map.remove("2");
map.remove("3");
- assertThat(map.getModifyAdditions()).containsExactly(object1);
+ assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemoveAll_given_beansInAdditions() throws Exception {
+ public void testRemoveAll_given_beansInAdditions() {
BeanMap map = new BeanMap<>();
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
map.putAll(all());
- assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
map.remove("2");
map.remove("3");
- assertThat(map.getModifyAdditions()).containsExactly(object1);
+ assertThat(map.getModifyAdditions()).containsOnly(object1);
assertThat(map.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemove_given_beansNotInAdditions() throws Exception {
+ public void testRemove_given_beansNotInAdditions() {
BeanMap map = new BeanMap<>(all());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -142,11 +142,11 @@ public class BeanMapTest {
// assert
assertThat(map.getModifyAdditions()).isEmpty();
- assertThat(map.getModifyRemovals()).containsExactly(object2, object3);
+ assertThat(map.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
- public void testRemoveAll_given_beansNotInAdditions() throws Exception {
+ public void testRemoveAll_given_beansNotInAdditions() {
BeanMap map = new BeanMap<>(all());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -157,11 +157,11 @@ public class BeanMapTest {
// assert
assertThat(map.getModifyAdditions()).isEmpty();
- assertThat(map.getModifyRemovals()).containsExactly(object2, object3);
+ assertThat(map.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
- public void testClear() throws Exception {
+ public void testClear() {
BeanMap map = new BeanMap<>(all());
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -170,12 +170,12 @@ public class BeanMapTest {
map.clear();
//assert
- assertThat(map.getModifyRemovals()).containsExactly(object1, object2, object3);
+ assertThat(map.getModifyRemovals()).containsOnly(object1, object2, object3);
assertThat(map.getModifyAdditions()).isEmpty();
}
@Test
- public void testClear_given_someBeansInAdditions() throws Exception {
+ public void testClear_given_someBeansInAdditions() {
BeanMap map = newModifyListeningMap();
map.put("2", object2);
@@ -185,7 +185,7 @@ public class BeanMapTest {
map.clear();
//assert
- assertThat(map.getModifyRemovals()).containsExactly(object1);
+ assertThat(map.getModifyRemovals()).containsOnly(object1);
assertThat(map.getModifyAdditions()).isEmpty();
}
@@ -228,7 +228,7 @@ public class BeanMapTest {
assertThat(map).doesNotContainKeys("1");
assertThat(map.get("1")).isNull();
- assertThat(map.getModifyRemovals()).containsExactly(object1);
+ assertThat(map.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -245,7 +245,7 @@ public class BeanMapTest {
assertThat(map).isEmpty();
assertThat(keySet).isEmpty();
- assertThat(map.getModifyRemovals()).containsExactly(object1, object2);
+ assertThat(map.getModifyRemovals()).containsOnly(object1, object2);
}
@Test
@@ -258,20 +258,14 @@ public class BeanMapTest {
map.setModifyListening(BeanCollection.ModifyListenMode.ALL);
final Set keySet = map.keySet();
- final Iterator iterator = keySet.iterator();
- while (iterator.hasNext()) {
- final String key = iterator.next();
- if (key.equals("2")) {
- iterator.remove();
- }
- }
+ keySet.removeIf(key -> key.equals("2"));
assertThat(map).hasSize(2);
assertThat(keySet).hasSize(2);
assertThat(keySet).containsExactly("1", "3");
assertThat(map).containsKeys("1", "3");
- assertThat(map.getModifyRemovals()).containsExactly(object2);
+ assertThat(map.getModifyRemovals()).containsOnly(object2);
}
@Test
@@ -294,7 +288,7 @@ public class BeanMapTest {
assertThat(keySet).containsExactly("1", "4");
assertThat(map).containsKeys("1", "4");
- assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
+ assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5);
}
@@ -318,7 +312,7 @@ public class BeanMapTest {
assertThat(keySet).containsExactly("2", "3", "5");
assertThat(map).containsKeys("2", "3", "5");
- assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
+ assertThat(map.getModifyRemovals()).containsOnly(object1, object4);
}
@Test(expected = UnsupportedOperationException.class)
@@ -348,7 +342,7 @@ public class BeanMapTest {
assertThat(entries).isEmpty();
assertThat(map).isEmpty();
- assertThat(map.getModifyRemovals()).containsExactly(object1);
+ assertThat(map.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -365,7 +359,7 @@ public class BeanMapTest {
assertThat(existed22).isFalse();
assertThat(map).hasSize(4);
- assertThat(map.getModifyRemovals()).containsExactly(object1);
+ assertThat(map.getModifyRemovals()).containsOnly(object1);
}
@Test
@@ -395,7 +389,7 @@ public class BeanMapTest {
}
assertThat(map).hasSize(3);
assertThat(entries).hasSize(3);
- assertThat(map.getModifyRemovals()).containsExactly(object2, object5);
+ assertThat(map.getModifyRemovals()).containsOnly(object2, object5);
}
@Test
@@ -406,7 +400,7 @@ public class BeanMapTest {
entries.removeAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
assertThat(map).hasSize(3);
assertThat(entries).hasSize(3);
- assertThat(map.getModifyRemovals()).containsExactly(object1, object4);
+ assertThat(map.getModifyRemovals()).containsOnly(object1, object4);
}
@Test
@@ -417,7 +411,7 @@ public class BeanMapTest {
entries.retainAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4)));
assertThat(map).hasSize(2);
assertThat(entries).hasSize(2);
- assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5);
+ assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5);
}
private BeanMap newModifyListeningMap() {
diff --git a/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java b/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java
index c274d34d7..40253faef 100644
--- a/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java
+++ b/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java
@@ -31,28 +31,28 @@ public class BeanSetTest {
}
@Test
- public void testAdd() throws Exception {
+ public void testAdd() {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.add(object1);
- assertThat(set.getModifyAdditions()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
set.add(object1);
- assertThat(set.getModifyAdditions()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object1);
set.add(object2);
- assertThat(set.getModifyAdditions()).containsExactly(object1, object2);
+ assertThat(set.getModifyAdditions()).containsOnly(object1, object2);
set.remove(object1);
- assertThat(set.getModifyAdditions()).containsExactly(object2);
+ assertThat(set.getModifyAdditions()).containsOnly(object2);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testAddAll_given_emptyStart() throws Exception {
+ public void testAddAll_given_emptyStart() {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -60,12 +60,12 @@ public class BeanSetTest {
// act
set.addAll(all());
- assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testAdd_given_someAlreadyIn() throws Exception {
+ public void testAdd_given_someAlreadyIn() {
BeanSet set = new BeanSet<>(some());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -76,12 +76,12 @@ public class BeanSetTest {
assertThat(set.contains(object2)).isTrue();
set.add(object2);
- assertThat(set.getModifyAdditions()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testAddSome_given_someAlreadyIn() throws Exception {
+ public void testAddSome_given_someAlreadyIn() {
BeanSet set = new BeanSet<>(some());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -89,43 +89,43 @@ public class BeanSetTest {
// act
set.addAll(all());
- assertThat(set.getModifyAdditions()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemove_given_beansInAdditions() throws Exception {
+ public void testRemove_given_beansInAdditions() {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.addAll(all());
- assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
set.remove(object2);
set.remove(object3);
- assertThat(set.getModifyAdditions()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemoveAll_given_beansInAdditions() throws Exception {
+ public void testRemoveAll_given_beansInAdditions() {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.addAll(all());
- assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
set.removeAll(some());
- assertThat(set.getModifyAdditions()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object1);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testRemove_given_beansNotInAdditions() throws Exception {
+ public void testRemove_given_beansNotInAdditions() {
BeanSet set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -136,11 +136,11 @@ public class BeanSetTest {
// assert
assertThat(set.getModifyAdditions()).isEmpty();
- assertThat(set.getModifyRemovals()).containsExactly(object2, object3);
+ assertThat(set.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
- public void testRemoveAll_given_beansNotInAdditions() throws Exception {
+ public void testRemoveAll_given_beansNotInAdditions() {
BeanSet set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -150,11 +150,11 @@ public class BeanSetTest {
// assert
assertThat(set.getModifyAdditions()).isEmpty();
- assertThat(set.getModifyRemovals()).containsExactly(object2, object3);
+ assertThat(set.getModifyRemovals()).containsOnly(object2, object3);
}
@Test
- public void testClear() throws Exception {
+ public void testClear() {
BeanSet set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -163,12 +163,12 @@ public class BeanSetTest {
set.clear();
//assert
- assertThat(set.getModifyRemovals()).containsExactly(object1, object2, object3);
+ assertThat(set.getModifyRemovals()).containsOnly(object1, object2, object3);
assertThat(set.getModifyAdditions()).isEmpty();
}
@Test
- public void testClear_given_someBeansInAdditions() throws Exception {
+ public void testClear_given_someBeansInAdditions() {
BeanSet set = new BeanSet<>();
set.add(object1);
@@ -180,27 +180,27 @@ public class BeanSetTest {
set.clear();
//assert
- assertThat(set.getModifyRemovals()).containsExactly(object1);
+ assertThat(set.getModifyRemovals()).containsOnly(object1);
assertThat(set.getModifyAdditions()).isEmpty();
}
@Test
- public void testRetainAll_given_beansInAdditions() throws Exception {
+ public void testRetainAll_given_beansInAdditions() {
BeanSet set = new BeanSet<>();
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
set.addAll(all());
- assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3);
+ assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3);
// act
set.retainAll(some());
- assertThat(set.getModifyAdditions()).containsExactly(object2, object3);
+ assertThat(set.getModifyAdditions()).containsOnly(object2, object3);
assertThat(set.getModifyRemovals()).isEmpty();
}
@Test
- public void testRetainAll_given_someBeansInAdditions() throws Exception {
+ public void testRetainAll_given_someBeansInAdditions() {
BeanSet set = new BeanSet<>();
set.add(object1);
@@ -211,12 +211,12 @@ public class BeanSetTest {
// act
set.retainAll(some());
- assertThat(set.getModifyAdditions()).containsExactly(object3);
- assertThat(set.getModifyRemovals()).containsExactly(object1);
+ assertThat(set.getModifyAdditions()).containsOnly(object3);
+ assertThat(set.getModifyRemovals()).containsOnly(object1);
}
@Test
- public void testRetainAll_given_noBeansInAdditions() throws Exception {
+ public void testRetainAll_given_noBeansInAdditions() {
BeanSet set = new BeanSet<>(all());
set.setModifyListening(BeanCollection.ModifyListenMode.ALL);
@@ -224,7 +224,7 @@ public class BeanSetTest {
// act
set.retainAll(some());
- assertThat(set.getModifyRemovals()).containsExactly(object1);
+ assertThat(set.getModifyRemovals()).containsOnly(object1);
}
}
diff --git a/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java b/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java
new file mode 100644
index 000000000..368bc9a8f
--- /dev/null
+++ b/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java
@@ -0,0 +1,199 @@
+package io.ebean.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.ebean.annotation.MutationDetection;
+import io.ebean.annotation.PersistBatch;
+import io.ebean.config.dbplatform.IdType;
+import io.ebean.datasource.DataSourceConfig;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class DatabaseConfigTest {
+
+ @Test
+ public void testLoadFromEbeanProperties() {
+
+ DatabaseConfig config = new DatabaseConfig();
+ config.loadFromProperties();
+
+ assertEquals(PersistBatch.NONE, config.getPersistBatch());
+ assertNotNull(config.getProperties());
+ }
+
+ @Test
+ public void evalPropertiesInput() {
+
+ String home = System.getenv("HOME");
+
+ Properties props = new Properties();
+ props.setProperty("ddl.initSql", "${HOME}/initSql");
+
+ DatabaseConfig config = new DatabaseConfig();
+ config.loadFromProperties(props);
+
+ String ddlInitSql = config.getDdlInitSql();
+ assertThat(ddlInitSql).isEqualTo(home+"/initSql");
+ }
+
+ @Test
+ public void testLoadWithProperties() {
+
+ DatabaseConfig config = new DatabaseConfig();
+ config.setPersistBatch(PersistBatch.NONE);
+ config.setPersistBatchOnCascade(PersistBatch.NONE);
+ config.setAutoReadOnlyDataSource(false);
+ config.setReadOnlyDataSource(null);
+ config.setReadOnlyDataSourceConfig(new DataSourceConfig());
+
+ Properties props = new Properties();
+ props.setProperty("persistBatch", "ALL");
+ props.setProperty("persistBatchOnCascade", "ALL");
+ props.setProperty("dbuuid", "binary");
+ props.setProperty("jdbcFetchSizeFindEach", "42");
+ props.setProperty("jdbcFetchSizeFindList", "43");
+ props.setProperty("backgroundExecutorShutdownSecs", "98");
+ props.setProperty("backgroundExecutorSchedulePoolSize", "4");
+ props.setProperty("dbOffline", "true");
+ props.setProperty("jsonDateTime", "MILLIS");
+ props.setProperty("jsonDate", "MILLIS");
+ props.setProperty("jsonMutationDetection", "NONE");
+ props.setProperty("autoReadOnlyDataSource", "true");
+ props.setProperty("disableL2Cache", "true");
+ props.setProperty("notifyL2CacheInForeground", "true");
+ props.setProperty("idType", "SEQUENCE");
+ props.setProperty("mappingLocations", "classpath:/foo;bar");
+ props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
+ props.setProperty("idGeneratorAutomatic", "true");
+ props.setProperty("enabledL2Regions", "r0,users,orgs");
+ props.setProperty("caseSensitiveCollation", "false");
+ props.setProperty("loadModuleInfo", "true");
+ props.setProperty("forUpdateNoKey", "true");
+ props.setProperty("defaultServer", "false");
+ props.setProperty("skipDataSourceCheck", "true");
+
+ props.setProperty("queryPlan.enable", "true");
+ props.setProperty("queryPlan.thresholdMicros", "10000");
+ props.setProperty("queryPlan.capture", "true");
+ props.setProperty("queryPlan.capturePeriodSecs", "42");
+ props.setProperty("queryPlan.captureMaxTimeMillis", "560");
+ props.setProperty("queryPlan.captureMaxCount", "7");
+
+ config.loadFromProperties(props);
+
+ assertFalse(config.isDefaultServer());
+ assertTrue(config.isDisableL2Cache());
+ assertTrue(config.isNotifyL2CacheInForeground());
+ assertTrue(config.isDbOffline());
+ assertTrue(config.isAutoReadOnlyDataSource());
+ assertTrue(config.isAutoLoadModuleInfo());
+ assertTrue(config.skipDataSourceCheck());
+
+ assertTrue(config.isIdGeneratorAutomatic());
+ assertFalse(config.getPlatformConfig().isCaseSensitiveCollation());
+ assertTrue(config.getPlatformConfig().isForUpdateNoKey());
+
+ assertThat(config.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
+
+ assertEquals(MutationDetection.NONE, config.getJsonMutationDetection());
+ config.setJsonMutationDetection(MutationDetection.SOURCE);
+ assertEquals(MutationDetection.SOURCE, config.getJsonMutationDetection());
+ assertEquals(IdType.SEQUENCE, config.getIdType());
+ assertEquals(PersistBatch.ALL, config.getPersistBatch());
+ assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade());
+ assertEquals(PlatformConfig.DbUuid.BINARY, config.getPlatformConfig().getDbUuid());
+ assertEquals(JsonConfig.DateTime.MILLIS, config.getJsonDateTime());
+ assertEquals(JsonConfig.Date.MILLIS, config.getJsonDate());
+
+ assertEquals("r0,users,orgs", config.getEnabledL2Regions());
+
+ assertEquals(42, config.getJdbcFetchSizeFindEach());
+ assertEquals(43, config.getJdbcFetchSizeFindList());
+ assertEquals(4, config.getBackgroundExecutorSchedulePoolSize());
+ assertEquals(98, config.getBackgroundExecutorShutdownSecs());
+
+ assertTrue(config.isQueryPlanEnable());
+ assertEquals(10000, config.getQueryPlanThresholdMicros());
+ assertTrue(config.isQueryPlanCapture());
+ assertEquals(42, config.getQueryPlanCapturePeriodSecs());
+ assertEquals(560, config.getQueryPlanCaptureMaxTimeMillis());
+ assertEquals(7, config.getQueryPlanCaptureMaxCount());
+
+ assertThat(config.getMappingLocations()).containsExactly("classpath:/foo","bar");
+
+ config.setPersistBatch(PersistBatch.NONE);
+ config.setPersistBatchOnCascade(PersistBatch.NONE);
+
+ Properties props1 = new Properties();
+ props1.setProperty("ebean.persistBatch", "ALL");
+ props1.setProperty("ebean.persistBatchOnCascade", "ALL");
+
+ config.setNotifyL2CacheInForeground(true);
+ config.setDisableL2Cache(true);
+ props1.setProperty("ebean.disableL2Cache", "false");
+ props1.setProperty("ebean.notifyL2CacheInForeground", "false");
+
+ config.loadFromProperties(props1);
+ assertFalse(config.isDisableL2Cache());
+ assertFalse(config.isNotifyL2CacheInForeground());
+
+ assertEquals(PersistBatch.ALL, config.getPersistBatch());
+ assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade());
+
+ config.setEnabledL2Regions("r0,orgs");
+ assertEquals("r0,orgs", config.getEnabledL2Regions());
+ }
+
+ @Test
+ public void test_defaults() {
+
+ DatabaseConfig config = new DatabaseConfig();
+ assertTrue(config.isIdGeneratorAutomatic());
+ assertTrue(config.isDefaultServer());
+ assertFalse(config.isAutoPersistUpdates());
+ assertFalse(config.skipDataSourceCheck());
+
+ config.setIdGeneratorAutomatic(false);
+ assertFalse(config.isIdGeneratorAutomatic());
+ assertEquals(JsonConfig.DateTime.ISO8601, config.getJsonDateTime());
+ assertEquals(JsonConfig.Date.ISO8601, config.getJsonDate());
+ assertEquals(MutationDetection.HASH, config.getJsonMutationDetection());
+ assertTrue(config.getPlatformConfig().isCaseSensitiveCollation());
+ assertTrue(config.isAutoLoadModuleInfo());
+
+ assertFalse(config.isQueryPlanEnable());
+ assertEquals(Long.MAX_VALUE, config.getQueryPlanThresholdMicros());
+ assertFalse(config.isQueryPlanCapture());
+ assertEquals(600, config.getQueryPlanCapturePeriodSecs());
+ assertEquals(10000L, config.getQueryPlanCaptureMaxTimeMillis());
+ assertEquals(10, config.getQueryPlanCaptureMaxCount());
+
+ config.setLoadModuleInfo(false);
+ assertFalse(config.isAutoLoadModuleInfo());
+ config.setAutoPersistUpdates(true);
+ assertTrue(config.isAutoPersistUpdates());
+ config.setSkipDataSourceCheck(true);
+ assertTrue(config.skipDataSourceCheck());
+ }
+
+ @Test
+ public void test_putServiceObject() {
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ DatabaseConfig config = new DatabaseConfig();
+ config.putServiceObject(objectMapper);
+
+ ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class);
+ ObjectMapper mapper1 = (ObjectMapper)config.getServiceObject("objectMapper");
+
+ assertThat(objectMapper).isSameAs(mapper0);
+ assertThat(objectMapper).isSameAs(mapper1);
+ }
+}
diff --git a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java b/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java
deleted file mode 100644
index dde2cb216..000000000
--- a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package io.ebean.config;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.ebean.annotation.PersistBatch;
-import io.ebean.config.dbplatform.IdType;
-import io.ebean.datasource.DataSourceConfig;
-import org.junit.Test;
-
-import java.util.Properties;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class ServerConfigTest {
-
- @Test
- public void testLoadFromEbeanProperties() {
-
- ServerConfig serverConfig = new ServerConfig();
- serverConfig.loadFromProperties();
-
- assertEquals(PersistBatch.NONE, serverConfig.getPersistBatch());
- assertNotNull(serverConfig.getProperties());
- }
-
- @Test
- public void evalPropertiesInput() {
-
- String home = System.getenv("HOME");
-
- Properties props = new Properties();
- props.setProperty("ddl.initSql", "${HOME}/initSql");
-
- ServerConfig serverConfig = new ServerConfig();
- serverConfig.loadFromProperties(props);
-
- String ddlInitSql = serverConfig.getDdlInitSql();
- assertThat(ddlInitSql).isEqualTo(home+"/initSql");
- }
-
- @Test
- public void testLoadWithProperties() {
-
- ServerConfig serverConfig = new ServerConfig();
- serverConfig.setPersistBatch(PersistBatch.NONE);
- serverConfig.setPersistBatchOnCascade(PersistBatch.NONE);
- serverConfig.setAutoReadOnlyDataSource(false);
- serverConfig.setReadOnlyDataSource(null);
- serverConfig.setReadOnlyDataSourceConfig(new DataSourceConfig());
-
- Properties props = new Properties();
- props.setProperty("persistBatch", "ALL");
- props.setProperty("persistBatchOnCascade", "ALL");
- props.setProperty("dbuuid", "binary");
- props.setProperty("jdbcFetchSizeFindEach", "42");
- props.setProperty("jdbcFetchSizeFindList", "43");
- props.setProperty("backgroundExecutorShutdownSecs", "98");
- props.setProperty("backgroundExecutorSchedulePoolSize", "4");
- props.setProperty("dbOffline", "true");
- props.setProperty("jsonDateTime", "MILLIS");
- props.setProperty("jsonDate", "MILLIS");
- props.setProperty("jsonDirtyByDefault", "false");
- props.setProperty("autoReadOnlyDataSource", "true");
- props.setProperty("disableL2Cache", "true");
- props.setProperty("notifyL2CacheInForeground", "true");
- props.setProperty("idType", "SEQUENCE");
- props.setProperty("mappingLocations", "classpath:/foo;bar");
- props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
- props.setProperty("idGeneratorAutomatic", "true");
- props.setProperty("enabledL2Regions", "r0,users,orgs");
- props.setProperty("caseSensitiveCollation", "false");
- props.setProperty("loadModuleInfo", "true");
- props.setProperty("forUpdateNoKey", "true");
- props.setProperty("defaultServer", "false");
-
- props.setProperty("queryPlan.enable", "true");
- props.setProperty("queryPlan.thresholdMicros", "10000");
- props.setProperty("queryPlan.capture", "true");
- props.setProperty("queryPlan.capturePeriodSecs", "42");
- props.setProperty("queryPlan.captureMaxTimeMillis", "560");
- props.setProperty("queryPlan.captureMaxCount", "7");
-
- serverConfig.loadFromProperties(props);
-
- assertFalse(serverConfig.isDefaultServer());
- assertTrue(serverConfig.isDisableL2Cache());
- assertTrue(serverConfig.isNotifyL2CacheInForeground());
- assertTrue(serverConfig.isDbOffline());
- assertTrue(serverConfig.isAutoReadOnlyDataSource());
- assertTrue(serverConfig.isAutoLoadModuleInfo());
-
- assertTrue(serverConfig.isIdGeneratorAutomatic());
- assertFalse(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
- assertTrue(serverConfig.getPlatformConfig().isForUpdateNoKey());
-
- assertThat(serverConfig.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
-
- assertEquals(IdType.SEQUENCE, serverConfig.getIdType());
- assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
- assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
- assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
- assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime());
- assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate());
- assertFalse(serverConfig.isJsonDirtyByDefault());
- serverConfig.setJsonDirtyByDefault(true);
- assertTrue(serverConfig.isJsonDirtyByDefault());
-
- assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
-
- assertEquals(42, serverConfig.getJdbcFetchSizeFindEach());
- assertEquals(43, serverConfig.getJdbcFetchSizeFindList());
- assertEquals(4, serverConfig.getBackgroundExecutorSchedulePoolSize());
- assertEquals(98, serverConfig.getBackgroundExecutorShutdownSecs());
-
- assertTrue(serverConfig.isQueryPlanEnable());
- assertEquals(10000, serverConfig.getQueryPlanThresholdMicros());
- assertTrue(serverConfig.isQueryPlanCapture());
- assertEquals(42, serverConfig.getQueryPlanCapturePeriodSecs());
- assertEquals(560, serverConfig.getQueryPlanCaptureMaxTimeMillis());
- assertEquals(7, serverConfig.getQueryPlanCaptureMaxCount());
-
- assertThat(serverConfig.getMappingLocations()).containsExactly("classpath:/foo","bar");
-
- serverConfig.setPersistBatch(PersistBatch.NONE);
- serverConfig.setPersistBatchOnCascade(PersistBatch.NONE);
-
- Properties props1 = new Properties();
- props1.setProperty("ebean.persistBatch", "ALL");
- props1.setProperty("ebean.persistBatchOnCascade", "ALL");
-
- serverConfig.setNotifyL2CacheInForeground(true);
- serverConfig.setDisableL2Cache(true);
- props1.setProperty("ebean.disableL2Cache", "false");
- props1.setProperty("ebean.notifyL2CacheInForeground", "false");
-
- serverConfig.loadFromProperties(props1);
- assertFalse(serverConfig.isDisableL2Cache());
- assertFalse(serverConfig.isNotifyL2CacheInForeground());
-
- assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
- assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
-
- serverConfig.setEnabledL2Regions("r0,orgs");
- assertEquals("r0,orgs", serverConfig.getEnabledL2Regions());
- }
-
- @Test
- public void test_defaults() {
-
- ServerConfig serverConfig = new ServerConfig();
- assertTrue(serverConfig.isIdGeneratorAutomatic());
- assertTrue(serverConfig.isDefaultServer());
- assertFalse(serverConfig.isAutoPersistUpdates());
-
- serverConfig.setIdGeneratorAutomatic(false);
- assertFalse(serverConfig.isIdGeneratorAutomatic());
- assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
- assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
- assertTrue(serverConfig.isJsonDirtyByDefault());
- assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
- assertTrue(serverConfig.isAutoLoadModuleInfo());
-
- assertFalse(serverConfig.isQueryPlanEnable());
- assertEquals(Long.MAX_VALUE, serverConfig.getQueryPlanThresholdMicros());
- assertFalse(serverConfig.isQueryPlanCapture());
- assertEquals(600, serverConfig.getQueryPlanCapturePeriodSecs());
- assertEquals(10000L, serverConfig.getQueryPlanCaptureMaxTimeMillis());
- assertEquals(10, serverConfig.getQueryPlanCaptureMaxCount());
-
- serverConfig.setLoadModuleInfo(false);
- assertFalse(serverConfig.isAutoLoadModuleInfo());
- serverConfig.setAutoPersistUpdates(true);
- assertTrue(serverConfig.isAutoPersistUpdates());
- }
-
- @Test
- public void test_putServiceObject() {
-
- ObjectMapper objectMapper = new ObjectMapper();
-
- ServerConfig config = new ServerConfig();
- config.putServiceObject(objectMapper);
-
- ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class);
- ObjectMapper mapper1 = (ObjectMapper)config.getServiceObject("objectMapper");
-
- assertThat(objectMapper).isSameAs(mapper0);
- assertThat(objectMapper).isSameAs(mapper1);
- }
-}
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 f0f0c0779..b09a0f54e 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java
@@ -650,7 +650,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
}
@Override
- public boolean exists(Query> ormQuery, Transaction transaction) {
+ public boolean exists(Query ormQuery, Transaction transaction) {
return false;
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java
index d53f0e973..9f26e978c 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java
@@ -23,11 +23,11 @@ public class BeanIudMetricsTest {
BasicMetricVisitor basic = new BasicMetricVisitor();
iudMetrics.visit(basic);
- List timed = basic.getTimedMetrics();
+ List timed = basic.timedMetrics();
assertThat(timed).hasSize(1);
- assertThat(timed.get(0).getCount()).isEqualTo(4);
- assertThat(timed.get(0).getName()).isEqualTo("iud.one.insertBatch");
+ assertThat(timed.get(0).count()).isEqualTo(4);
+ assertThat(timed.get(0).name()).isEqualTo("iud.one.insertBatch");
iudMetrics.addBatch(PersistRequest.Type.UPDATE, startNanos, 1);
iudMetrics.addBatch(PersistRequest.Type.DELETE_SOFT, startNanos, 2);
@@ -37,15 +37,15 @@ public class BeanIudMetricsTest {
basic = new BasicMetricVisitor();
iudMetrics.visit(basic);
- timed = basic.getTimedMetrics();
+ timed = basic.timedMetrics();
assertThat(timed).hasSize(3);
- assertThat(timed.get(0).getCount()).isEqualTo(16);
- assertThat(timed.get(0).getName()).isEqualTo("iud.one.insertBatch");
- assertThat(timed.get(1).getCount()).isEqualTo(3);
- assertThat(timed.get(1).getName()).isEqualTo("iud.one.updateBatch");
- assertThat(timed.get(2).getCount()).isEqualTo(12);
- assertThat(timed.get(2).getName()).isEqualTo("iud.one.deleteBatch");
+ assertThat(timed.get(0).count()).isEqualTo(16);
+ assertThat(timed.get(0).name()).isEqualTo("iud.one.insertBatch");
+ assertThat(timed.get(1).count()).isEqualTo(3);
+ assertThat(timed.get(1).name()).isEqualTo("iud.one.updateBatch");
+ assertThat(timed.get(2).count()).isEqualTo(12);
+ assertThat(timed.get(2).name()).isEqualTo("iud.one.deleteBatch");
}
@Test
@@ -63,15 +63,15 @@ public class BeanIudMetricsTest {
BasicMetricVisitor basic = new BasicMetricVisitor();
iudMetrics.visit(basic);
- List timed = basic.getTimedMetrics();
+ List timed = basic.timedMetrics();
assertThat(timed).hasSize(3);
- assertThat(timed.get(0).getCount()).isEqualTo(1);
- assertThat(timed.get(0).getName()).isEqualTo("iud.one.insert");
- assertThat(timed.get(1).getCount()).isEqualTo(2);
- assertThat(timed.get(1).getName()).isEqualTo("iud.one.update");
- assertThat(timed.get(2).getCount()).isEqualTo(2);
- assertThat(timed.get(2).getName()).isEqualTo("iud.one.delete");
+ assertThat(timed.get(0).count()).isEqualTo(1);
+ assertThat(timed.get(0).name()).isEqualTo("iud.one.insert");
+ assertThat(timed.get(1).count()).isEqualTo(2);
+ assertThat(timed.get(1).name()).isEqualTo("iud.one.update");
+ assertThat(timed.get(2).count()).isEqualTo(2);
+ assertThat(timed.get(2).name()).isEqualTo("iud.one.delete");
}
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java
index 012c13c39..014ddb4b6 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java
@@ -33,7 +33,7 @@ public class TestNotEnhancedMappedSuper extends BaseTestCase {
NotEnhancedMappedSuper mappedSuper = new NotEnhancedMappedSuper();
boolean enhanced = (mappedSuper instanceof EntityBean);
- Assert.assertFalse(enhanced);
+ Assert.assertTrue(enhanced);
}
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..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
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
+import io.ebeaninternal.api.BindValuesKey;
public class RawExpressionTest extends BaseExpressionTest {
@@ -61,11 +62,16 @@ public class RawExpressionTest extends BaseExpressionTest {
}
public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) {
- assertThat(exp0.queryBindHash()).isNotEqualTo(exp1.queryBindHash());
+ assertThat(bindKey(exp0)).isNotEqualTo(bindKey(exp1));
}
public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) {
- assertThat(exp0.queryBindHash()).isEqualTo(exp1.queryBindHash());
+ assertThat(bindKey(exp0)).isEqualTo(bindKey(exp1));
}
+ private BindValuesKey bindKey(RawExpression query) {
+ BindValuesKey bindValuesKey = new BindValuesKey();
+ query.queryBindKey(bindValuesKey);
+ return bindValuesKey;
+ }
}
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 6dfdad9f6..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("NativeMethodAccessorImpl.invoke0(Native Method:12)");
+ 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");
}
@@ -24,7 +28,7 @@ public class BasicProfileLocationTest {
BasicProfileLocation loc = new BasicProfileLocation("com.foo.Bar.all");
assertThat(loc.obtain()).isFalse();
assertThat(loc.fullLocation()).isEqualTo("com.foo.Bar.all");
- assertThat(loc.location()).isEqualTo("Bar.all");
+ assertThat(loc.location()).isEqualTo("com.foo.Bar.all");
assertThat(loc.label()).isEqualTo("Bar.all");
}
@@ -34,7 +38,7 @@ public class BasicProfileLocationTest {
BasicProfileLocation loc = new BasicProfileLocation("foo.Bar.all");
assertThat(loc.obtain()).isFalse();
assertThat(loc.fullLocation()).isEqualTo("foo.Bar.all");
- assertThat(loc.location()).isEqualTo("Bar.all");
+ assertThat(loc.location()).isEqualTo("foo.Bar.all");
assertThat(loc.label()).isEqualTo("Bar.all");
}
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java
index 43f0e0426..cb6d4d662 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java
@@ -21,17 +21,17 @@ public class DTimedMetricMapTest {
BasicMetricVisitor visitor = new BasicMetricVisitor();
metricMap.visit(visitor);
- MetaTimedMetric timedMetric = visitor.getTimedMetrics().get(0);
- assertThat(timedMetric.getCount()).isEqualTo(1);
- assertThat(timedMetric.getTotal()).isGreaterThan(10);
+ MetaTimedMetric timedMetric = visitor.timedMetrics().get(0);
+ assertThat(timedMetric.count()).isEqualTo(1);
+ assertThat(timedMetric.total()).isGreaterThan(10);
metricMap.addSinceNanos("some", nanos);
visitor = new BasicMetricVisitor();
metricMap.visit(visitor);
- timedMetric = visitor.getTimedMetrics().get(0);
- assertThat(timedMetric.getCount()).isEqualTo(1);
- assertThat(timedMetric.getTotal()).isGreaterThan(10);
+ timedMetric = visitor.timedMetrics().get(0);
+ assertThat(timedMetric.count()).isEqualTo(1);
+ assertThat(timedMetric.total()).isGreaterThan(10);
}
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java
index 28e651b88..6dbbb45d3 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java
@@ -17,16 +17,16 @@ public class DTimedMetricTest {
metric.addSinceNanos(start);
DTimeMetricStats stats = metric.collect(true);
- assertThat(stats.getCount()).isEqualTo(1);
- assertThat(stats.getTotal()).isGreaterThan(10);
- assertThat(stats.getMax()).isEqualTo(stats.getTotal());
+ assertThat(stats.count()).isEqualTo(1);
+ assertThat(stats.total()).isGreaterThan(10);
+ assertThat(stats.max()).isEqualTo(stats.total());
metric.addSinceNanos(start);
stats = metric.collect(true);
- assertThat(stats.getCount()).isEqualTo(1);
- assertThat(stats.getTotal()).isGreaterThan(10);
- assertThat(stats.getMax()).isEqualTo(stats.getTotal());
+ assertThat(stats.count()).isEqualTo(1);
+ assertThat(stats.total()).isGreaterThan(10);
+ assertThat(stats.max()).isEqualTo(stats.total());
}
@Test
@@ -40,16 +40,16 @@ public class DTimedMetricTest {
metric.addBatchSince(start, 5);
DTimeMetricStats stats = metric.collect(true);
- assertThat(stats.getCount()).isEqualTo(5);
- assertThat(stats.getTotal()).isGreaterThan(10000);
- assertThat(stats.getMax()).isEqualTo(stats.getTotal() / 5);
- assertThat(stats.getMax()).isGreaterThan(10000 / 5);
+ assertThat(stats.count()).isEqualTo(5);
+ assertThat(stats.total()).isGreaterThan(10000);
+ assertThat(stats.max()).isEqualTo(stats.total() / 5);
+ assertThat(stats.max()).isGreaterThan(10000 / 5);
metric.addBatchSince(start, 2);
stats = metric.collect(true);
- assertThat(stats.getCount()).isEqualTo(2);
- assertThat(stats.getTotal()).isGreaterThan(10000);
- assertThat(stats.getMax()).isEqualTo(stats.getTotal() / 2);
+ assertThat(stats.count()).isEqualTo(2);
+ assertThat(stats.total()).isGreaterThan(10000);
+ assertThat(stats.max()).isEqualTo(stats.total() / 2);
}
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java
index c67d14b30..29658566c 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java
@@ -26,7 +26,7 @@ public class SortMetricTest {
list.add(create("a"));
list.sort(sortMetric);
- String names = list.stream().map(DTimeMetricStats::getName).collect(Collectors.joining());
+ String names = list.stream().map(DTimeMetricStats::name).collect(Collectors.joining());
assertEquals("nullabcd", names);
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java
index 1bdbcd2f7..0b26078ff 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java
@@ -8,8 +8,13 @@ public class UtilLocationTest {
@Test
public void label() {
-
assertThat(UtilLocation.label("foo")).isEqualTo("foo");
- assertThat(UtilLocation.label("ProfileLocationTest$Other.(ProfileLocationTest.java:47)")).isEqualTo("ProfileLocationTest$Other.init");
+ assertThat(UtilLocation.label("ProfileLocationTest$Other.")).isEqualTo("ProfileLocationTest$Other.init");
+ }
+
+ @Test
+ public void loc() {
+ assertThat(UtilLocation.loc("org.foo.MyFoo.doIt(MyFoo.java:12)")).isEqualTo("org.foo.MyFoo.doIt");
+ assertThat(UtilLocation.label("org.foo.MyFoo.doIt")).isEqualTo("MyFoo.doIt");
}
}
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 d9e621221..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
@@ -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.BindValuesKey;
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(bindKey(q1)).isNotEqualTo(bindKey(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(bindKey(q1)).isNotEqualTo(bindKey(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(bindKey(q1)).isEqualTo(bindKey(q2));
}
@Test
@@ -110,4 +111,10 @@ public class DefaultOrmQueryTest extends BaseTestCase {
OrmQueryRequest r2 = createQueryRequest(SpiQuery.Type.LIST, q2, null);
q2.prepare(r2);
}
+
+ private BindValuesKey bindKey(DefaultOrmQuery query) {
+ BindValuesKey key = new BindValuesKey();
+ query.queryBindKey(key);
+ return key;
+ }
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java b/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java
index 4e0a32e24..8d4feeaf9 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java
@@ -9,17 +9,25 @@ import io.ebean.RawSqlBuilder;
import io.ebean.SqlRow;
import io.ebean.annotation.ForPlatform;
import io.ebean.annotation.Platform;
+import io.ebean.datasource.DataSourceConfig;
+import io.ebeaninternal.server.core.DefaultServer;
import io.ebeaninternal.server.rawsql.SpiRawSql.Sql;
import org.junit.Test;
import org.tests.model.basic.Customer;
+import org.tests.model.basic.EBasicClob;
+import org.tests.model.basic.PersistentFileContent;
import org.tests.model.basic.ResetBasicData;
import org.tests.model.rawsql.ERawSqlAggBean;
import javax.sql.DataSource;
+import java.nio.charset.StandardCharsets;
import java.sql.Connection;
+import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@@ -283,4 +291,58 @@ public class TestRawSqlBuilder extends BaseTestCase {
}
}
+ @Test
+ public void testCLobClosedConnection() throws Exception {
+ final EBasicClob eBasicClob = new EBasicClob();
+ eBasicClob.setName("eBasicClob");
+ final String description = "This is the CLob description";
+ eBasicClob.setDescription(description);
+ DB.save(eBasicClob);
+
+ final String sql = "select description from ebasic_clob where id = ?";
+
+ List rows = new ArrayList<>();
+ final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig();
+
+ try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
+ PreparedStatement stmt = connection.prepareStatement(sql)) {
+ stmt.setLong(1, eBasicClob.getId());
+
+ try (ResultSet resultSet = stmt.executeQuery()) {
+ while (resultSet.next()) {
+ rows.add(RawSqlBuilder.sqlRow(resultSet, "true", false));
+ }
+ }
+ }
+
+ assertThat(rows).hasSize(1);
+ assertThat(rows.get(0).getString("description")).isEqualTo(description);
+ }
+
+ @Test
+ public void testBLobClosedConnection() throws Exception {
+ final PersistentFileContent pfc = new PersistentFileContent();
+ final byte[] bytes = "This is the blob as String".getBytes(StandardCharsets.UTF_8);
+ pfc.setContent(bytes);
+ DB.save(pfc);
+
+ List rows = new ArrayList<>();
+ final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig();
+
+ final String sql = "select content from persistent_file_content where id = ?";
+ try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
+ PreparedStatement stmt = connection.prepareStatement(sql)) {
+ stmt.setLong(1, pfc.getId());
+
+ try (ResultSet resultSet = stmt.executeQuery()) {
+ while (resultSet.next()) {
+ rows.add(RawSqlBuilder.sqlRow(resultSet, "true", false));
+ }
+ }
+ }
+
+ assertThat(rows).hasSize(1);
+ assertThat(rows.get(0).get("content")).isEqualTo(bytes);
+ }
+
}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java
index ba12fcd05..b0b2b1eb0 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java
@@ -5,6 +5,8 @@ import org.junit.Test;
import java.sql.Timestamp;
import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.TimeZone;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@@ -14,12 +16,12 @@ import static org.junit.Assert.assertTrue;
public class ScalarTypeOffsetDateTimeTest {
- ScalarTypeOffsetDateTime type = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.MILLIS);
+ ScalarTypeOffsetDateTime type = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.MILLIS, ZoneOffset.systemDefault());
OffsetDateTime warmUp = OffsetDateTime.now();
@Test
- public void testConvertToMillis() throws Exception {
+ public void testConvertToMillis() {
warmUp.hashCode();
@@ -30,7 +32,43 @@ public class ScalarTypeOffsetDateTimeTest {
}
@Test
- public void testConvertFromTimestamp() throws Exception {
+ public void convertFromInstant_with_UTC_expect_matchingZoneOffset() {
+ final TimeZone timeZoneToUse = TimeZone.getTimeZone("UTC");
+ final ZoneOffset expectedZoneOffset = ZoneOffset.UTC;
+
+ convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedZoneOffset);
+ }
+
+ @Test
+ public void convertFromInstant_with_EST_expect_matchingZoneOffset() {
+ final TimeZone timeZoneToUse = TimeZone.getTimeZone("EST");
+ final ZoneOffset expectedOffset = OffsetDateTime.now(timeZoneToUse.toZoneId()).getOffset();
+
+ convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedOffset);
+ }
+
+ private void convertFromInstantWithConfiguredTimeZone(TimeZone timeZoneToUse, ZoneOffset expectedZoneOffset) {
+ TimeZone previous = TimeZone.getDefault();
+ try {
+ OffsetDateTime dateTime = OffsetDateTime.parse("2021-01-01T00:00:00+11:00");
+
+ // test ScalarTypeOffsetDateTime with the configured timeZone to use
+ ScalarTypeOffsetDateTime type = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.MILLIS, timeZoneToUse.toZoneId());
+
+ // effectively we desire to ignore the system timezone and use the configured one
+ TimeZone.setDefault(timeZoneToUse);
+
+ final OffsetDateTime offsetDateTime = type.convertFromInstant(dateTime.toInstant());
+
+ assertEquals(expectedZoneOffset, offsetDateTime.getOffset());
+
+ } finally {
+ TimeZone.setDefault(previous);
+ }
+ }
+
+ @Test
+ public void testConvertFromTimestamp() {
Timestamp now = new Timestamp(System.currentTimeMillis());
@@ -69,11 +107,11 @@ public class ScalarTypeOffsetDateTimeTest {
JsonTester jsonTester = new JsonTester<>(type);
jsonTester.test(now);
- ScalarTypeOffsetDateTime typeNanos = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.NANOS);
+ ScalarTypeOffsetDateTime typeNanos = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.NANOS, ZoneOffset.systemDefault());
jsonTester = new JsonTester<>(typeNanos);
jsonTester.test(now);
- ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601);
+ ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601, ZoneOffset.systemDefault());
jsonTester = new JsonTester<>(typeIso);
jsonTester.test(now);
}
@@ -81,7 +119,7 @@ public class ScalarTypeOffsetDateTimeTest {
@Test
public void isoJsonFormatParse() {
- ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601);
+ ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601, ZoneOffset.systemDefault());
OffsetDateTime now = OffsetDateTime.now();
String asJson = typeIso.toJsonISO8601(now);
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java
index ea501b273..168041723 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java
@@ -4,7 +4,11 @@ import io.ebean.config.JsonConfig;
import org.junit.Test;
import java.sql.Timestamp;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.util.TimeZone;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
@@ -12,12 +16,12 @@ import static org.junit.Assert.*;
public class ScalarTypeZonedDateTimeTest {
- ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS);
+ ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS, ZoneId.systemDefault());
ZonedDateTime warmUp = ZonedDateTime.now();
@Test
- public void testConvertToMillis() throws Exception {
+ public void testConvertToMillis() {
warmUp.hashCode();
@@ -29,7 +33,7 @@ public class ScalarTypeZonedDateTimeTest {
}
@Test
- public void testConvertFromTimestamp() throws Exception {
+ public void testConvertFromTimestamp() {
Timestamp now = new Timestamp(System.currentTimeMillis());
@@ -39,6 +43,41 @@ public class ScalarTypeZonedDateTimeTest {
assertEquals(now, timestamp);
}
+ @Test
+ public void convertFromInstant_with_UTC_expect_matchingZoneOffset() {
+ final TimeZone timeZoneToUse = TimeZone.getTimeZone("UTC");
+ final ZoneOffset expectedZoneOffset = ZoneOffset.UTC;
+
+ convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedZoneOffset);
+ }
+
+ @Test
+ public void convertFromInstant_with_EST_expect_matchingZoneOffset() {
+ final TimeZone timeZoneToUse = TimeZone.getTimeZone("EST");
+ final ZoneOffset expectedOffset = OffsetDateTime.now(timeZoneToUse.toZoneId()).getOffset();
+
+ convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedOffset);
+ }
+
+ private void convertFromInstantWithConfiguredTimeZone(TimeZone timeZoneToUse, ZoneOffset expectedZoneOffset) {
+ TimeZone previous = TimeZone.getDefault();
+ try {
+ OffsetDateTime dateTime = OffsetDateTime.parse("2021-01-01T00:00:00+11:00");
+
+ // test ScalarTypeOffsetDateTime with the configured timeZone to use
+ ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS, timeZoneToUse.toZoneId());
+
+ // effectively we desire to ignore the system timezone and use the configured one
+ TimeZone.setDefault(timeZoneToUse);
+
+ final ZonedDateTime zonedDateTime = type.convertFromInstant(dateTime.toInstant());
+
+ assertEquals(expectedZoneOffset, zonedDateTime.getOffset());
+
+ } finally {
+ TimeZone.setDefault(previous);
+ }
+ }
@Test
public void testToJdbcType() throws Exception {
@@ -68,11 +107,11 @@ public class ScalarTypeZonedDateTimeTest {
JsonTester jsonTester = new JsonTester<>(type);
jsonTester.test(now);
- ScalarTypeZonedDateTime typeNanos = new ScalarTypeZonedDateTime(JsonConfig.DateTime.NANOS);
+ ScalarTypeZonedDateTime typeNanos = new ScalarTypeZonedDateTime(JsonConfig.DateTime.NANOS, ZoneId.systemDefault());
jsonTester = new JsonTester<>(typeNanos);
jsonTester.test(now);
- ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601);
+ ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601, ZoneId.systemDefault());
jsonTester = new JsonTester<>(typeIso);
jsonTester.test(now);
}
@@ -80,7 +119,7 @@ public class ScalarTypeZonedDateTimeTest {
@Test
public void toJsonISO8601() {
- ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601);
+ ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601, ZoneId.systemDefault());
ZonedDateTime now = ZonedDateTime.now();
String asJson = typeIso.toJsonISO8601(now);
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java
new file mode 100644
index 000000000..9eceabb0a
--- /dev/null
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java
@@ -0,0 +1,23 @@
+package io.ebeaninternal.server.util;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ChecksumTest {
+
+ @Test
+ public void checksum() {
+ final long val = Checksum.checksum("Hello world");
+ assertThat(val).isEqualTo(2346098258L);
+ assertThat(Checksum.checksum("Hello world")).isEqualTo(val);
+ assertThat(Checksum.checksum("hello world")).isNotEqualTo(val);
+ }
+
+ @Test
+ public void checksum_shortString() {
+ final long val0 = Checksum.checksum("2012-01-11");
+ final long val1 = Checksum.checksum("2012-10-02");
+ assertThat(val0).isNotEqualTo(val1);
+ }
+}
diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java b/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java
index d9db27d3d..9f0106cae 100644
--- a/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java
+++ b/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java
@@ -3,16 +3,51 @@ package io.ebeaninternal.server.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
public class Md5Test {
@Test
public void hash() throws Exception {
-
String content = "some random content we wish to hash";
String hash1 = Md5.hash(content);
String hash2 = Md5.hash(content);
assertEquals(hash1, hash2);
+ assertEquals(hash1, "62c20bf679ff56cb746452ab5c88e3ed");
}
+ @Test
+ public void hashDifferent() throws Exception {
+ String hash1 = Md5.hash("one");
+ String hash2 = Md5.hash("two");
+ String hash3 = Md5.hash("onetwo");
+
+ assertNotEquals(hash1, hash2);
+ assertNotEquals(hash2, hash3);
+ assertEquals(hash1, "f97c5d29941bfb1b2fdab0874906ab82");
+ }
+
+ @Test
+ public void hashMulti() {
+ String hash1 = Md5.hash("one", "two");
+ String hash2 = Md5.hash("onetwo");
+
+ assertEquals(hash1, hash2);
+ assertEquals(hash1, "5b9164ad6f496d9dee12ec7634ce253f");
+ }
+
+ @Test
+ public void hashMulti_when_null() {
+ String hash1 = Md5.hash("one", null);
+ String hash2 = Md5.hash("one");
+
+ assertEquals(hash1, hash2);
+ assertEquals(hash1, "f97c5d29941bfb1b2fdab0874906ab82");
+ }
+
+ @Test
+ public void when_null() {
+ String hash1 = Md5.hash(null, null);
+ assertEquals(hash1, "d41d8cd98f00b204e9800998ecf8427e");
+ }
}
diff --git a/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java b/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java
index 6e956510a..2f9cdf7f1 100644
--- a/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java
+++ b/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java
@@ -80,15 +80,15 @@ public class TestBatchInsertFlush extends BaseTestCase {
}
ServerMetrics metrics = collectMetrics();
- List txnStats = metrics.getTimedMetrics();
+ List txnStats = metrics.timedMetrics();
for (MetaTimedMetric txnMetric : txnStats) {
System.out.println(txnMetric);
}
assertThat(txnStats).hasSize(4);
- assertThat(txnStats.get(0).getName()).isEqualTo("txn.main");
- assertThat(txnStats.get(1).getName()).isEqualTo("txn.named.TestBatchInsertFlush.no_cascade");
- assertThat(txnStats.get(2).getName()).isEqualTo("iud.TSDetail.insertBatch");
- assertThat(txnStats.get(3).getName()).isEqualTo("iud.TSMaster.insertBatch");
+ assertThat(txnStats.get(0).name()).isEqualTo("txn.main");
+ assertThat(txnStats.get(1).name()).isEqualTo("txn.named.TestBatchInsertFlush.no_cascade");
+ assertThat(txnStats.get(2).name()).isEqualTo("iud.TSDetail.insertBatch");
+ assertThat(txnStats.get(3).name()).isEqualTo("iud.TSMaster.insertBatch");
}
@Test
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
+ }
+
}
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/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java
index 106beb9f2..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
@@ -1,21 +1,48 @@
package org.tests.json;
import io.ebean.BaseTestCase;
+import io.ebean.BeanState;
import io.ebean.DB;
+import io.ebean.ValuePair;
+import io.ebean.event.BeanPersistAdapter;
+import io.ebean.event.BeanPersistRequest;
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;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class TestDbJson_Jackson3 extends BaseTestCase {
+ public static class EBasicJsonListPersistController extends BeanPersistAdapter {
+
+ private static Map updatedValues;
+
+ @Override
+ public boolean isRegisterFor(Class> cls) {
+ return EBasicJsonList.class.isAssignableFrom(cls);
+ }
+
+ @Override
+ public boolean preInsert(BeanPersistRequest> request) {
+ updatedValues = request.getUpdatedValues();
+ return true;
+ }
+
+ @Override
+ public boolean preUpdate(BeanPersistRequest> request) {
+ updatedValues = request.getUpdatedValues();
+ return true;
+ }
+ }
@Test
public void updateIncludesJsonColumn_when_explicit_isMarkedDirty() {
@@ -24,6 +51,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase {
EBasicJsonJackson3 bean = new EBasicJsonJackson3();
bean.setName("b1");
bean.setPlainValue(contentBean);
+ bean.setPlainValue2(contentBean);
bean.save();
@@ -32,20 +60,15 @@ public class TestDbJson_Jackson3 extends BaseTestCase {
LoggedSql.start();
found.save();
-
- List sql = LoggedSql.collect();
- assertThat(sql).hasSize(1);
- assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, version=? where id=? and version=?");
+ expectedSql(0, "update ebasic_json_jackson3 set name=?, version=? where id=? and version=?");
found.setName("b1-mod2");
found.getPlainValue().setName("b");
- found.getPlainValue().setMarkedDirty(true);
+ // found.getPlainValue().setMarkedDirty(true); // Irrelevant for SOURCE or HASH based mutation detection
found.save();
-
- sql = LoggedSql.stop();
- assertThat(sql).hasSize(1);
- assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?");
+ expectedSql(0, "update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?");
+ LoggedSql.stop();
final EBasicJsonJackson3 found2 = DB.find(EBasicJsonJackson3.class, bean.getId());
@@ -69,11 +92,153 @@ public class TestDbJson_Jackson3 extends BaseTestCase {
found.setName("p1-mod");
found.setBeanList(null);
+ BeanState state = DB.getBeanState(found);
+ assertThat(state.getChangedProps()).containsExactlyInAnyOrder("name", "beanList");
+
+ ValuePair pair = state.getDirtyValues().get("name");
+ assertThat(pair.getNewValue()).isEqualTo("p1-mod");
+ assertThat(pair.getOldValue()).isEqualTo("p1");
+
+ pair = state.getDirtyValues().get("beanList");
+ assertThat(pair.getNewValue()).isEqualTo(null);
+ assertThat((List)pair.getOldValue()).hasSize(1)
+ .extracting(PlainBean::getName).containsExactly("a");
+
+
LoggedSql.start();
DB.save(found);
- final List sql = LoggedSql.stop();
- assertThat(sql).hasSize(1);
- assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, plain_bean=?, version=? where id=?");
+ // plain_bean=?, no longer included with MD5 dirty detection
+ expectedSql(0, "update ebasic_json_list set name=?, bean_list=?, version=? where id=?");
+
+ assertThat(EBasicJsonListPersistController.updatedValues.entrySet())
+ .extracting(Map.Entry::toString)
+ .containsExactlyInAnyOrder("beanList=null,[name:a]","name=p1-mod,p1","version=2,1");
+
+ assertThat(DB.getBeanState(found).isDirty()).isFalse();
+
+ found.getPlainBean().setName("b");
+ assertThat(DB.getBeanState(found).isDirty()).isTrue();
+
+ state = DB.getBeanState(found);
+ assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainBean");
+ pair = state.getDirtyValues().get("plainBean");
+ assertThat(pair.getNewValue()).hasToString("name:b");
+ assertThat(pair.getOldValue()).hasToString("name:a");
+
+
+ LoggedSql.start();
+ DB.save(found);
+
+ // plain_bean=?, no longer included with MD5 dirty detection
+ expectedSql(0, "update ebasic_json_list set plain_bean=?, version=? where id=?");
+
+ assertThat(EBasicJsonListPersistController.updatedValues.entrySet())
+ .extracting(Map.Entry::toString)
+ .containsExactlyInAnyOrder("plainBean=name:b,name:a", "version=3,2");
+
+ LoggedSql.stop();
+ }
+
+ @Test
+ public void updateIncludesJsonColumn_when_list_loadedAndNotDirtyAware() {
+
+ PlainBean contentBean = new PlainBean("a", 42);
+ EBasicJsonList bean = new EBasicJsonList();
+ bean.setName("p1");
+ bean.setPlainBean(contentBean);
+ bean.setBeanList(Arrays.asList(contentBean));
+
+ DB.save(bean);
+ final EBasicJsonList found = DB.find(EBasicJsonList.class, bean.getId());
+ found.getBeanList().get(0).setName("p1-mod");
+
+ BeanState state = DB.getBeanState(found);
+ assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList");
+ }
+
+ @Test
+ public void update_with_differentDbJsonSettings() {
+ PlainBeanDirtyAware contentBean1 = new PlainBeanDirtyAware("x", 42);
+ PlainBeanDirtyAware contentBean2 = new PlainBeanDirtyAware("y", 43);
+ PlainBeanDirtyAware contentBean3 = new PlainBeanDirtyAware("z", 44);
+
+ EBasicJsonJackson3 bean = new EBasicJsonJackson3();
+ bean.setName("b1");
+ bean.setPlainValue(contentBean1);
+ bean.setPlainValue2(contentBean2);
+ bean.setPlainValue3(contentBean3);
+
+ BeanState state = DB.getBeanState(bean);
+ // a new bean is not considered as dirty (thus have no changed props)
+ assertThat(state.isDirty()).isFalse();
+ assertThat(state.isNewOrDirty()).isTrue();
+ assertThat(state.getChangedProps()).isEmpty();
+
+ bean.save();
+
+ bean = DB.find(EBasicJsonJackson3.class, bean.getId());
+ state = DB.getBeanState(bean);
+ // a fresh loaded bean is also not considered as dirty
+ assertThat(state.isDirty()).isFalse();
+ assertThat(state.isNewOrDirty()).isFalse();
+ assertThat(state.getChangedProps()).isEmpty();
+
+ bean.getPlainValue().setName("a"); // has SOURCE
+
+ assertThat(state.isDirty()).isTrue();
+ assertThat(state.getChangedProps()).containsExactly("plainValue");
+
+ bean.getPlainValue2().setName("b");
+ assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainValue", "plainValue2");
+
+ bean.getPlainValue3().setName("c"); // has mutationDetection = NONE
+
+ Map dirtyValues = state.getDirtyValues();
+ assertThat(dirtyValues).hasSize(2).containsKeys("plainValue", "plainValue2");
+
+ assertThat(dirtyValues.get("plainValue")).hasToString("name:a,name:x"); // SOURCE -> origValue present
+ assertThat(dirtyValues.get("plainValue2")).hasToString("name:b,null"); // without SOURCE no origValue present
+
+ LoggedSql.start();
+ bean.save();
+ expectedSql(0, "update ebasic_json_jackson3 set plain_value=?, plain_value2=?, version=? where id=?");
+
+ bean = DB.find(EBasicJsonJackson3.class, bean.getId());
+ LoggedSql.collect(); // ignore the select
+ assertThat(bean.getPlainValue().getName()).isEqualTo("a");
+ assertThat(bean.getPlainValue2().getName()).isEqualTo("b");
+ assertThat(bean.getPlainValue3().getName()).isEqualTo("z"); // value is not updated
+
+ bean.getPlainValue3().setName("c");
+ bean.getPlainValue3().setMarkedDirty(true); // This is ignored because it is MutationDetection.NONE
+ bean.save();
+ // no update as plainValue3 has MutationDetection.NONE (ModifyAwareType = NONE isn't an expected combination to me)
+ assertThat(LoggedSql.collect()).isEmpty();
+
+ bean.getPlainValue2().setName("b2"); // effectively HASH mode mutation detection
+ bean.save();
+ expectedSql(0, "update ebasic_json_jackson3 set plain_value2=?, version=? where id=? and version=?");
+
+ 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/json/TestDbJson_List.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java
index 28fd78221..647aa6eff 100644
--- a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java
+++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java
@@ -76,10 +76,11 @@ public class TestDbJson_List extends BaseTestCase {
update_when_dirty();
update_when_dirty_flags();
update_when_dirty_SetListMap();
+
+ DB.delete(found);
}
- //@Test//(dependsOnMethods = "insert")
- public void json_parse_format() {
+ private void json_parse_format() {
String asJson = DB.json().toJson(found);
assertThat(asJson).contains("\"tags\":[\"one\",\"two\"]");
@@ -104,8 +105,7 @@ public class TestDbJson_List extends BaseTestCase {
assertThat(fromJson.getBeanMap()).hasSize(2);
}
- //@Test//(dependsOnMethods = "insert")
- public void update_when_notDirty() {
+ private void update_when_notDirty() {
found.setName("mod");
LoggedSqlCollector.start();
@@ -113,10 +113,11 @@ public class TestDbJson_List extends BaseTestCase {
List sql = LoggedSqlCollector.stop();
// we don't update the phone numbers (as they are not dirty)
- assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, plain_bean=?, version=? where");
+ // plain_bean=?, no longer included with MD5 dirty detection
+ assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, version=? where");
}
- public void update_when_dirty() {
+ private void update_when_dirty() {
//found.setName("modAgain");
found.getTags().add("three");
@@ -126,10 +127,11 @@ public class TestDbJson_List extends BaseTestCase {
List sql = LoggedSqlCollector.stop();
// we don't update the phone numbers (as they are not dirty)
- assertSql(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, tags=?, version=? where id=? and version=?");
+ // plain_bean=? not included using MD5 dirty detection
+ assertSql(sql.get(0)).contains("update ebasic_json_list set tags=?, version=? where id=? and version=?");
}
- public void update_when_dirty_flags() {
+ private void update_when_dirty_flags() {
//found.setName("modAgain");
found.getFlags().remove(42L);
@@ -139,10 +141,11 @@ public class TestDbJson_List extends BaseTestCase {
List sql = LoggedSqlCollector.stop();
// we don't update the phone numbers (as they are not dirty)
- assertSql(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, flags=?, version=? where id=? and version=?;");
+ // plain_bean=? not included with MD5 dirty detection
+ assertSql(sql.get(0)).contains("update ebasic_json_list set flags=?, version=? where id=? and version=?;");
}
- public void update_when_dirty_SetListMap() {
+ private void update_when_dirty_SetListMap() {
//found.setName("modAgain");
found.getBeanSet().clear();
@@ -154,7 +157,8 @@ public class TestDbJson_List extends BaseTestCase {
List sql = LoggedSqlCollector.stop();
// we don't update the phone numbers (as they are not dirty)
- assertSql(sql.get(0)).contains("update ebasic_json_list set beans=?, bean_list=?, bean_map=?, plain_bean=?, version=? where id=? and version=?");
+ // plain_bean=? not included with MD5 dirty detection
+ assertSql(sql.get(0)).contains("update ebasic_json_list set beans=?, bean_list=?, bean_map=?, version=? where id=? and version=?");
}
@Test
@@ -221,4 +225,19 @@ public class TestDbJson_List extends BaseTestCase {
DB.delete(bean);
}
+
+ @Test
+ public void testNullToEmpty() {
+ EBasicJsonList bean = new EBasicJsonList();
+ bean.setFlags(null);
+ bean.setTags(null);
+ bean.setBeanMap(null);
+ DB.save(bean);
+
+ bean = DB.find(EBasicJsonList.class).setId(bean.getId()).findOne();
+
+ assertThat(bean.getFlags()).isEmpty();
+ assertThat(bean.getTags()).isEmpty();
+ assertThat(bean.getBeanMap()).isEmpty();
+ }
}
diff --git a/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java b/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java
index 1f12f85c8..3a50fa547 100644
--- a/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java
+++ b/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java
@@ -1,14 +1,15 @@
package org.tests.m2m;
import io.ebean.BaseTestCase;
-import io.ebean.Ebean;
+import io.ebean.DB;
+import org.junit.Test;
import org.tests.model.basic.MRole;
import org.tests.model.basic.MUser;
-import org.junit.Assert;
-import org.junit.Test;
import java.util.List;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class TestM2MModifyTest extends BaseTestCase {
@Test
@@ -19,8 +20,8 @@ public class TestM2MModifyTest extends BaseTestCase {
MRole r1 = new MRole("r1");
// Save r1 and r2
- Ebean.save(r0);
- Ebean.save(r1);
+ DB.save(r0);
+ DB.save(r1);
// Create a new user
MUser u0 = new MUser("usr0");
@@ -28,32 +29,27 @@ public class TestM2MModifyTest extends BaseTestCase {
u0.addRole(r1);
// Save the user
- Ebean.save(u0);
+ DB.save(u0);
List roles = u0.getRoles();
- Assert.assertTrue(roles.size() == 2);
+ assertThat(roles).hasSize(2);
- u0 = Ebean.find(MUser.class, u0.getUserid());
+ u0 = DB.find(MUser.class, u0.getUserid());
roles = u0.getRoles();
- int nrRoles = roles.size();
-
- Assert.assertTrue(nrRoles == 2);
+ assertThat(roles).hasSize(2);
roles.clear();
roles.add(r0);
roles.add(r1);
roles.remove(r1);
- Ebean.save(u0);
+ DB.save(u0);
- u0 = Ebean.find(MUser.class, u0.getUserid());
+ u0 = DB.find(MUser.class, u0.getUserid());
roles = u0.getRoles();
-
- nrRoles = roles.size();
-
- Assert.assertTrue(nrRoles == 1);
+ assertThat(roles).hasSize(1);
}
}
diff --git a/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java b/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java
index 3ba05ee68..3ccad9fb8 100644
--- a/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java
+++ b/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java
@@ -28,7 +28,7 @@ public class TestM2mDeleteObject extends BaseTestCase {
List sqlMetrics = sqlMetrics();
assertThat(sqlMetrics).hasSize(1);
- assertThat(sqlMetrics.get(0).getName()).isEqualTo("orm.update.deleteAllPermissions");
+ assertThat(sqlMetrics.get(0).name()).isEqualTo("orm.update.deleteAllPermissions");
Tenant t = new Tenant("tenant");
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;
+ }
}
diff --git a/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java b/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java
index c76728fca..79db60023 100644
--- a/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java
+++ b/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java
@@ -37,7 +37,6 @@ public class CustomerFinder extends Finder {
}
public List byNameStatus(String nameStartsWith, Customer.Status status) {
-
return query("where status = :status and name istartsWith :name order by name")
.setParameter("status", status)
.setParameter("name", nameStartsWith)
@@ -45,7 +44,6 @@ public class CustomerFinder extends Finder {
}
public List