mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
87
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee615836ba | ||
|
|
fea7de42d5 | ||
|
|
4787303160 | ||
|
|
801d419f51 | ||
|
|
3579557200 | ||
|
|
202d785710 | ||
|
|
7ca72a39b1 | ||
|
|
f34e0f5f2b | ||
|
|
d98a31fa6b | ||
|
|
9a82dbb3e6 | ||
|
|
a76442641b | ||
|
|
2043fdf7e1 | ||
|
|
a06f7bea41 | ||
|
|
db34afe867 | ||
|
|
589ee7a5ea | ||
|
|
4ba26ee7b7 | ||
|
|
71223895ac | ||
|
|
928b90122f | ||
|
|
d601a88d82 | ||
|
|
443e826f61 | ||
|
|
2691221168 | ||
|
|
e62bb3dc6b | ||
|
|
604099429e | ||
|
|
11dc7f64ba | ||
|
|
ceea705a76 | ||
|
|
d9f7531e81 | ||
|
|
7bd7640c63 | ||
|
|
ca1636ff9e | ||
|
|
ea741d0169 | ||
|
|
c43e2e6956 | ||
|
|
03004452ac | ||
|
|
a470877017 | ||
|
|
777d322222 | ||
|
|
1f0ac1f1c1 | ||
|
|
1fad6e910c | ||
|
|
822fb7325a | ||
|
|
20e0eb1021 | ||
|
|
20becf951d | ||
|
|
5aa8557168 | ||
|
|
c66d248e96 | ||
|
|
0d8e7c852b | ||
|
|
991c6d6b5c | ||
|
|
2bc5d3d325 | ||
|
|
5ad8e7ead4 | ||
|
|
8832bbc57f | ||
|
|
86483ff947 | ||
|
|
92d7a7562f | ||
|
|
52fe3cf3c8 | ||
|
|
94fb6414fa | ||
|
|
d0270dbc6a | ||
|
|
11a047bf8e | ||
|
|
eb70c7e196 | ||
|
|
b497b8635b | ||
|
|
5e595ec5f8 | ||
|
|
49b86d07d3 | ||
|
|
11eed4cfe9 | ||
|
|
4aef0ad459 | ||
|
|
21cd74d0ca | ||
|
|
6c262f1df5 | ||
|
|
f32ace4e5a | ||
|
|
e54b7052e0 | ||
|
|
b40d448955 | ||
|
|
33bccd3bcb | ||
|
|
705e4f4098 | ||
|
|
1642833fdb | ||
|
|
492bb98e0e | ||
|
|
a8456f90db | ||
|
|
103b75172d | ||
|
|
0baa3a0eac | ||
|
|
f26e4b5381 | ||
|
|
3d4de4dee5 | ||
|
|
c621435a28 | ||
|
|
7408483a43 | ||
|
|
4d15535381 | ||
|
|
787c2f90f2 | ||
|
|
e590a2149d | ||
|
|
8c77740aa7 | ||
|
|
29582b57f7 | ||
|
|
2763f333d4 | ||
|
|
8840628d34 | ||
|
|
6fb28d7ded | ||
|
|
be810aa568 | ||
|
|
824cfa27be | ||
|
|
66cb6d5e7d | ||
|
|
4977aa2ad1 | ||
|
|
dd6a473008 | ||
|
|
ef1143bb70 |
+2
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
@@ -50,7 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>7.0</version>
|
||||
<version>7.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* Defines a cancelable query.
|
||||
* <p>
|
||||
* Typically holds a representation of the PreparedStatement to perform the
|
||||
* actual cancel.
|
||||
* </p>
|
||||
*/
|
||||
public interface CancelableQuery {
|
||||
|
||||
/**
|
||||
* Cancel the query.
|
||||
* <p>
|
||||
* For JDBC this translates to calling cancel on the PreparedStatement.
|
||||
* </p>
|
||||
*/
|
||||
void cancel();
|
||||
}
|
||||
@@ -38,7 +38,7 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface DtoQuery<T> {
|
||||
public interface DtoQuery<T> extends CancelableQuery {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
|
||||
@@ -67,7 +67,7 @@ public interface ExtendedServer {
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
<T> boolean exists(Query<?> ormQuery, Transaction transaction);
|
||||
<T> boolean exists(Query<T> ormQuery, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the number of 'top level' or 'root' entities this query should return.
|
||||
|
||||
@@ -45,6 +45,14 @@ public interface ProfileLocation {
|
||||
*/
|
||||
String label();
|
||||
|
||||
/**
|
||||
* Return a hash of the location that intentionally excludes the line number.
|
||||
* <p>
|
||||
* The hash is expected to be stable regardless of line number in the source file
|
||||
* so that is identifies the class and method location over a long time.
|
||||
*/
|
||||
long hash();
|
||||
|
||||
/**
|
||||
* Return the full location.
|
||||
*/
|
||||
|
||||
@@ -177,7 +177,7 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* @param <T> the type of Entity bean this query will fetch.
|
||||
*/
|
||||
public interface Query<T> {
|
||||
public interface Query<T> extends CancelableQuery {
|
||||
|
||||
/**
|
||||
* The lock type (strength) to use with query FOR UPDATE row locking.
|
||||
@@ -291,15 +291,6 @@ public interface Query<T> {
|
||||
*/
|
||||
UpdateQuery<T> asUpdate();
|
||||
|
||||
/**
|
||||
* Cancel the query execution if supported by the underlying database and
|
||||
* driver.
|
||||
* <p>
|
||||
* This must be called from a different thread to the query executor.
|
||||
* </p>
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* Return a copy of the query.
|
||||
* <p>
|
||||
|
||||
@@ -37,7 +37,7 @@ import java.util.function.Predicate;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface SqlQuery extends Serializable {
|
||||
public interface SqlQuery extends Serializable, CancelableQuery {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
|
||||
@@ -27,17 +27,6 @@ public interface EntityBean extends Serializable {
|
||||
throw new NotEnhancedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the enhancement marker value.
|
||||
* <p>
|
||||
* This is the class name of the enhanced class and used to check that all
|
||||
* entity classes are enhanced (specifically not just a super class).
|
||||
* </p>
|
||||
*/
|
||||
default String _ebean_getMarker() {
|
||||
throw new NotEnhancedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new entity bean instance.
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,11 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
*/
|
||||
private static final byte FLAG_ORIG_VALUE_SET = 8;
|
||||
|
||||
/**
|
||||
* Flags indicating if the mutable hash is set.
|
||||
*/
|
||||
private static final byte FLAG_MUTABLE_HASH_SET = 16;
|
||||
|
||||
private transient final ReentrantLock lock = new ReentrantLock();
|
||||
private transient NodeUsageCollector nodeUsageCollector;
|
||||
private transient PersistenceContext persistenceContext;
|
||||
@@ -91,6 +96,17 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private Object ownerId;
|
||||
private int sortOrder;
|
||||
|
||||
/**
|
||||
* Holds information of json loaded jackson beans (e.g. the original json or checksum).
|
||||
*/
|
||||
private MutableValueInfo[] mutableInfo;
|
||||
|
||||
/**
|
||||
* Holds json content determined at point of dirty check.
|
||||
* Stored here on dirty check such that we only convert to json once.
|
||||
*/
|
||||
private MutableValueNext[] mutableNext;
|
||||
|
||||
/**
|
||||
* Create a intercept with a given entity.
|
||||
*/
|
||||
@@ -228,6 +244,17 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* if any embedded beans are either new or dirty (and hence need saving).
|
||||
*/
|
||||
public boolean isDirty() {
|
||||
if (dirty) {
|
||||
return true;
|
||||
}
|
||||
if (mutableInfo != null) {
|
||||
for (int i = 0; i < mutableInfo.length; i++) {
|
||||
if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
|
||||
dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@@ -368,8 +395,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
this.owner._ebean_setEmbeddedLoaded();
|
||||
this.lazyLoadProperty = -1;
|
||||
this.origValues = null;
|
||||
this.mutableNext = null;
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET);
|
||||
flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET);
|
||||
}
|
||||
this.dirty = false;
|
||||
}
|
||||
@@ -442,6 +470,10 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Return the original value that was changed via an update.
|
||||
*/
|
||||
public Object getOrigValue(int propertyIndex) {
|
||||
if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) {
|
||||
// mutable hash set, but not ORIG_VALUE
|
||||
setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get());
|
||||
}
|
||||
if (origValues == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -476,7 +508,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Return the number of properties.
|
||||
*/
|
||||
public int getPropertyLength() {
|
||||
return owner._ebean_getPropertyNames().length;
|
||||
return flags.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -564,7 +596,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private void setOriginalValue(int propertyIndex, Object value) {
|
||||
if (origValues == null) {
|
||||
origValues = new Object[owner._ebean_getPropertyNames().length];
|
||||
origValues = new Object[flags.length];
|
||||
}
|
||||
if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
|
||||
flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
|
||||
@@ -577,7 +609,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
*/
|
||||
private void setOriginalValueForce(int propertyIndex, Object value) {
|
||||
if (origValues == null) {
|
||||
origValues = new Object[owner._ebean_getPropertyNames().length];
|
||||
origValues = new Object[flags.length];
|
||||
}
|
||||
origValues[propertyIndex] = value;
|
||||
}
|
||||
@@ -638,7 +670,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void addDirtyPropertyNames(Set<String> props, 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
|
||||
props.add((prefix == null ? getProperty(i) : prefix + getProperty(i)));
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
@@ -656,7 +688,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
String[] names = owner._ebean_getPropertyNames();
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
if (isChangedProp(i)) {
|
||||
if (propertyNames.contains(names[i])) {
|
||||
return true;
|
||||
}
|
||||
@@ -684,7 +716,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void addDirtyPropertyValues(Map<String, ValuePair> 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 +738,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 +773,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 +1144,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 +1170,62 @@ 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 .
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
final MutableValueNext next = mutableNext[propertyIndex];
|
||||
mutableInfo(propertyIndex, next.info());
|
||||
return next.content();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
/**
|
||||
* Holds information on mutable values (like plain beans stored as json).
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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<E> implements Serializable {
|
||||
/**
|
||||
* Deletions list for manyToMany persistence.
|
||||
*/
|
||||
private Set<E> modifyDeletions = new LinkedHashSet<>();
|
||||
private Map<E,Object> modifyDeletions = new IdentityHashMap<>();
|
||||
|
||||
/**
|
||||
* Additions list for manyToMany persistence.
|
||||
*/
|
||||
private Set<E> modifyAdditions = new LinkedHashSet<>();
|
||||
private Map<E,Object> 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<E> 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<E> getModifyAdditions() {
|
||||
return modifyAdditions;
|
||||
return modifyAdditions.keySet();
|
||||
}
|
||||
|
||||
Set<E> getModifyRemovals() {
|
||||
return modifyDeletions;
|
||||
return modifyDeletions.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
@@ -744,23 +741,21 @@ public class DatabaseConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if DbJson beans are assumed dirty by default.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2935,7 +2930,7 @@ 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);
|
||||
|
||||
runMigration = p.getBoolean("migration.run", runMigration);
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,17 +27,17 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaTimedMetric> getTimedMetrics() {
|
||||
public List<MetaTimedMetric> timedMetrics() {
|
||||
return timed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryMetric> getQueryMetrics() {
|
||||
public List<MetaQueryMetric> queryMetrics() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaCountMetric> getCountMetrics() {
|
||||
public List<MetaCountMetric> countMetrics() {
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,13 @@ public interface MetaMetric {
|
||||
/**
|
||||
* Return the metric name.
|
||||
*/
|
||||
String getName();
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Migrate to name().
|
||||
*/
|
||||
@Deprecated
|
||||
default String getName() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,52 @@ 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();
|
||||
|
||||
/**
|
||||
* Return the hash of the plan.
|
||||
* Migrate to sql().
|
||||
*/
|
||||
String getHash();
|
||||
@Deprecated
|
||||
default String getSql() {
|
||||
return sql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash of the sql.
|
||||
*/
|
||||
long sqlHash();
|
||||
|
||||
/**
|
||||
* Migrate to sqlHash().
|
||||
*/
|
||||
@Deprecated
|
||||
default long getSqlHash() {
|
||||
return sqlHash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
long sqlHash();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@@ -6,30 +6,79 @@ package io.ebean.meta;
|
||||
*/
|
||||
public interface MetaTimedMetric extends MetaMetric {
|
||||
|
||||
/**
|
||||
* Return the metric location hash if defined.
|
||||
* <p>
|
||||
* This hash excludes line number with the intention of being stable over time
|
||||
* as code changes move the source line (but the method is the same).
|
||||
*/
|
||||
long locationHash();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -6,7 +6,7 @@ package io.ebean.meta;
|
||||
public class MetricData {
|
||||
|
||||
private String name;
|
||||
private String hash;
|
||||
private long sqlHash;
|
||||
private String loc;
|
||||
private String sql;
|
||||
|
||||
@@ -14,6 +14,7 @@ public class MetricData {
|
||||
private Long mean;
|
||||
private Long max;
|
||||
private Long total;
|
||||
private long locHash;
|
||||
|
||||
public MetricData(String name) {
|
||||
this.name = name;
|
||||
@@ -30,12 +31,20 @@ public class MetricData {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
public void setSqlHash(long sqlHash) {
|
||||
this.sqlHash = sqlHash;
|
||||
}
|
||||
|
||||
public void setLocHash(long locHash) {
|
||||
this.locHash = locHash;
|
||||
}
|
||||
|
||||
public long getLocHash() {
|
||||
return locHash;
|
||||
}
|
||||
|
||||
public String getLoc() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -10,7 +10,7 @@ public class QueryPlanInit {
|
||||
|
||||
private boolean all;
|
||||
|
||||
private Set<String> hashes = new HashSet<>();
|
||||
private Set<Long> hashes = new HashSet<>();
|
||||
|
||||
private long thresholdMicros;
|
||||
|
||||
@@ -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,28 +40,28 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query plan should be initiated based on it's hash.
|
||||
*/
|
||||
public boolean includeHash(String hash) {
|
||||
return all || hashes.contains(hash);
|
||||
public boolean includeHash(long sqlHash) {
|
||||
return all || hashes.contains(sqlHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specific hashes that we want to collect query plans on.
|
||||
*/
|
||||
public Set<String> getHashes() {
|
||||
public Set<Long> sqlHashes() {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specific hashes that we want to collect query plans on.
|
||||
*/
|
||||
public void setHashes(Set<String> hashes) {
|
||||
public void sqlHashes(Set<Long> hashes) {
|
||||
this.hashes = hashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,39 @@ public interface ServerMetrics {
|
||||
/**
|
||||
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
|
||||
*/
|
||||
List<MetaTimedMetric> getTimedMetrics();
|
||||
List<MetaTimedMetric> timedMetrics();
|
||||
|
||||
/**
|
||||
* Migrate to timedMetrics().
|
||||
*/
|
||||
@Deprecated
|
||||
default List<MetaTimedMetric> getTimedMetrics() {
|
||||
return timedMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query metrics.
|
||||
*/
|
||||
List<MetaQueryMetric> getQueryMetrics();
|
||||
List<MetaQueryMetric> queryMetrics();
|
||||
|
||||
/**
|
||||
* Migrate to queryMetrics().
|
||||
*/
|
||||
@Deprecated
|
||||
default List<MetaQueryMetric> getQueryMetrics() {
|
||||
return queryMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Counter metrics.
|
||||
*/
|
||||
List<MetaCountMetric> getCountMetrics();
|
||||
List<MetaCountMetric> countMetrics();
|
||||
|
||||
/**
|
||||
* Migrate to countMetrics().
|
||||
*/
|
||||
@Deprecated
|
||||
default List<MetaCountMetric> getCountMetrics() {
|
||||
return countMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ public interface TimedMetricStats extends MetaTimedMetric {
|
||||
*/
|
||||
void setLocation(String location);
|
||||
|
||||
/**
|
||||
* Additionally set the location hash.
|
||||
*/
|
||||
void setLocationHash(long locationHash);
|
||||
|
||||
/**
|
||||
* Override the name based on profile location.
|
||||
*/
|
||||
|
||||
@@ -66,4 +66,17 @@ public class JdbcClose {
|
||||
logger.warn("Error on connection rollback", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the statement
|
||||
*/
|
||||
public static void cancel(Statement stmt) {
|
||||
try {
|
||||
if (stmt != null) {
|
||||
stmt.cancel();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Error on cancelling statement", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.9.3</tag>
|
||||
<tag>ebean-parent-12.11.0</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean autotune</name>
|
||||
@@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+19
-19
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -14,12 +14,12 @@
|
||||
|
||||
<properties>
|
||||
<ebean-ddl-runner.version>1.0</ebean-ddl-runner.version>
|
||||
<ebean-migration-auto.version>1.0</ebean-migration-auto.version>
|
||||
<ebean-migration.version>12.4.0</ebean-migration.version>
|
||||
<ebean-migration-auto.version>1.1</ebean-migration-auto.version>
|
||||
<ebean-migration.version>12.11.0</ebean-migration.version>
|
||||
<ebean-test-docker.version>4.1</ebean-test-docker.version>
|
||||
<ebean-datasource.version>7.0</ebean-datasource.version>
|
||||
<ebean-agent.version>12.9.0</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.9.1</ebean-maven-plugin.version>
|
||||
<ebean-agent.version>12.11.0</ebean-agent.version>
|
||||
<ebean-maven-plugin.version>12.11.0</ebean-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -81,88 +81,88 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<T> extends StringParser, StringFormatter, ScalarDataReader<T> {
|
||||
|
||||
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.
|
||||
|
||||
+9
-9
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.9.3</tag>
|
||||
<tag>ebean-parent-12.11.0</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -52,13 +52,13 @@
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>classpath-scanner</artifactId>
|
||||
<version>4.2</version>
|
||||
<version>6.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration-auto</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- keep testing in core using ebean-ddl-generator & ebean-migration -->
|
||||
@@ -72,7 +72,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.9.0</version>
|
||||
<version>12.9.4-RC1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -87,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.9.3</version>
|
||||
<version>12.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -302,7 +302,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>12.9.1</version>
|
||||
<version>12.10.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -119,7 +119,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
if (extraWhere != null) {
|
||||
// replace special ${ta} placeholder with the base table alias
|
||||
// which is always t0 and add the extra where clause
|
||||
query.where().raw(extraWhere.replace("${ta}", "t0"));
|
||||
query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
|
||||
/**
|
||||
* Cancellable query, that has a delegate.
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
public interface SpiCancelableQuery extends CancelableQuery {
|
||||
|
||||
/**
|
||||
* Checks if the query was cancelled.
|
||||
* @throws PersistenceException if query was cancelled.
|
||||
*/
|
||||
void checkCancelled();
|
||||
|
||||
/**
|
||||
* Set the underlying cancelable query (with the PreparedStatement).
|
||||
*/
|
||||
void setCancelableQuery(CancelableQuery cancelableQuery);
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
@@ -31,7 +30,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Object Relational query - Internal extension to Query object.
|
||||
*/
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes {
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes, SpiCancelableQuery {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
@@ -847,16 +846,6 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
ReadEvent getFutureFetchAudit();
|
||||
|
||||
/**
|
||||
* Set the underlying cancelable query (with the PreparedStatement).
|
||||
*/
|
||||
void setCancelableQuery(CancelableQuery cancelableQuery);
|
||||
|
||||
/**
|
||||
* Return true if this query has been cancelled.
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Return the base table to use if user defined on the query.
|
||||
*/
|
||||
|
||||
@@ -18,9 +18,9 @@ public interface SpiQueryPlan {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The hash for the query plan.
|
||||
* The hash of the sql.
|
||||
*/
|
||||
String getHash();
|
||||
long getSqlHash();
|
||||
|
||||
/**
|
||||
* The SQL for the query plan.
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
|
||||
/**
|
||||
* SQL query binding (for SqlQuery and DtoQuery).
|
||||
*/
|
||||
public interface SpiSqlBinding {
|
||||
public interface SpiSqlBinding extends SpiCancelableQuery {
|
||||
|
||||
/**
|
||||
* Return the named or positioned parameters.
|
||||
|
||||
+39
-17
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.*;
|
||||
@@ -12,11 +13,14 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Wraps the objects involved in executing a SQL / Relational Query.
|
||||
*/
|
||||
public abstract class AbstractSqlQueryRequest {
|
||||
public abstract class AbstractSqlQueryRequest implements CancelableQuery {
|
||||
|
||||
protected final SpiSqlBinding query;
|
||||
|
||||
@@ -36,6 +40,8 @@ public abstract class AbstractSqlQueryRequest {
|
||||
|
||||
protected long startNano;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the BeanFindRequest.
|
||||
*/
|
||||
@@ -43,6 +49,7 @@ public abstract class AbstractSqlQueryRequest {
|
||||
this.server = server;
|
||||
this.query = query;
|
||||
this.transaction = (SpiTransaction) t;
|
||||
this.query.setCancelableQuery(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,24 +144,30 @@ public abstract class AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
protected void executeAsSql(Binder binder) throws SQLException {
|
||||
prepareSql();
|
||||
Connection conn = transaction.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
prepareSql();
|
||||
Connection conn = transaction.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
BindParams bindParams = query.getBindParams();
|
||||
if (!bindParams.isEmpty()) {
|
||||
this.bindLog = binder.bind(bindParams, pstmt, conn);
|
||||
}
|
||||
if (isLogSql()) {
|
||||
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
BindParams bindParams = query.getBindParams();
|
||||
if (!bindParams.isEmpty()) {
|
||||
this.bindLog = binder.bind(bindParams, pstmt, conn);
|
||||
}
|
||||
if (isLogSql()) {
|
||||
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
setResultSet(pstmt.executeQuery(), null);
|
||||
query.checkCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,4 +177,13 @@ public abstract class AbstractSqlQueryRequest {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
|
||||
@@ -1283,14 +1284,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(Query<?> ormQuery, Transaction transaction) {
|
||||
Query<?> ormQueryCopy = ormQuery.copy();
|
||||
ormQueryCopy.setMaxRows(1);
|
||||
public <T> boolean exists(Query<T> ormQuery, Transaction transaction) {
|
||||
Query<T> ormQueryCopy = ormQuery.copy().setMaxRows(1);
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<Object> ids = request.findIds();
|
||||
return !ids.isEmpty();
|
||||
return !request.findIds().isEmpty();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
@@ -1382,7 +1381,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query.copy();
|
||||
spiQuery.setFutureFetch(true);
|
||||
// FutureList query always run in it's own persistence content
|
||||
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
|
||||
@@ -2329,13 +2328,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 +2351,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
|
||||
if (initRequest.isAll()) {
|
||||
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
|
||||
queryPlanManager.setDefaultThreshold(initRequest.thresholdMicros());
|
||||
}
|
||||
return beanDescriptorManager.queryPlanInit(initRequest);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
ormQuery.setType(type);
|
||||
ormQuery.setManualId();
|
||||
|
||||
query.setCancelableQuery(ormQuery);
|
||||
// execute the underlying ORM query returning the ResultSet
|
||||
SpiResultSet result = server.findResultSet(ormQuery, transaction);
|
||||
this.pstmt = result.getStatement();
|
||||
@@ -117,6 +118,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
public boolean next() throws SQLException {
|
||||
query.checkCancelled();
|
||||
return dataReader.next();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
|
||||
List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
|
||||
if (!countMetrics.isEmpty()) {
|
||||
out("\n-- Counters --");
|
||||
countMetrics.sort(SortMetric.COUNT_NAME);
|
||||
@@ -91,7 +91,7 @@ class DumpMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> 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,39 @@ class DumpMetrics {
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
if (dumpHash) {
|
||||
sb.append("\n hash:").append(metric.getHash());
|
||||
sb.append("\n sqlHash:").append(metric.sqlHash());
|
||||
}
|
||||
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);
|
||||
sb.append("\n locHash:").append(metric.locationHash());
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -31,9 +31,9 @@ class DumpMetricsData {
|
||||
|
||||
private void collect(ServerMetrics serverMetrics) {
|
||||
|
||||
final List<MetaTimedMetric> timedMetrics = serverMetrics.getTimedMetrics();
|
||||
final List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
|
||||
final List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
|
||||
final List<MetaTimedMetric> timedMetrics = serverMetrics.timedMetrics();
|
||||
final List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
|
||||
final List<MetaQueryMetric> 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,32 @@ class DumpMetricsData {
|
||||
private void add(MetaTimedMetric metric) {
|
||||
final MetricData data = create(metric);
|
||||
appendCounters(data, metric);
|
||||
data.setLoc(metric.getLocation());
|
||||
data.setLoc(metric.location());
|
||||
data.setLocHash(metric.locationHash());
|
||||
}
|
||||
|
||||
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.setSqlHash(metric.sqlHash());
|
||||
}
|
||||
|
||||
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
|
||||
data.setLoc(metric.getLocation());
|
||||
data.setSql(metric.getSql());
|
||||
data.setLocHash(metric.locationHash());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
|
||||
List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
|
||||
if (!countMetrics.isEmpty()) {
|
||||
if (sortBy != null) {
|
||||
countMetrics.sort(SortMetric.COUNT_NAME);
|
||||
@@ -98,7 +98,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
}
|
||||
}
|
||||
|
||||
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
|
||||
List<MetaQueryMetric> 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,8 @@ class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
metricStart(metric);
|
||||
appendTiming(metric);
|
||||
if (isIncludeDetail(metric)) {
|
||||
appendExtra("loc", metric.getLocation());
|
||||
keyVal("locHash", metric.locationHash());
|
||||
appendExtra("loc", metric.location());
|
||||
}
|
||||
metricEnd();
|
||||
}
|
||||
@@ -197,11 +198,12 @@ class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
metricStart(metric);
|
||||
appendTiming(metric);
|
||||
if (withHash) {
|
||||
appendExtra("hash", metric.getHash());
|
||||
keyVal("sqlHash", metric.sqlHash());
|
||||
keyVal("locHash", metric.locationHash());
|
||||
}
|
||||
if (isIncludeDetail(metric)) {
|
||||
appendExtra("loc", metric.getLocation());
|
||||
appendExtra("sql", metric.getSql());
|
||||
appendExtra("loc", metric.location());
|
||||
appendExtra("sql", metric.sql());
|
||||
}
|
||||
metricEnd();
|
||||
}
|
||||
@@ -218,13 +220,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());
|
||||
keyVal("count", timedMetric.count());
|
||||
keyVal("total", timedMetric.total());
|
||||
keyVal("mean", timedMetric.mean());
|
||||
keyVal("max", timedMetric.max());
|
||||
}
|
||||
|
||||
private void keyVal(String key, long value) throws IOException {
|
||||
key(key);
|
||||
val(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.QueryIterator;
|
||||
@@ -35,7 +36,6 @@ import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.loadcontext.DLoadContext;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -139,6 +139,7 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
@Override
|
||||
public boolean next() throws SQLException {
|
||||
query.checkCancelled();
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
} else {
|
||||
|
||||
@@ -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<Class<?>> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
|
||||
|
||||
private final List<Class<?>> embeddableList = new ArrayList<>();
|
||||
|
||||
private final List<Class<?>> entityList = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends ScalarType<?>>> scalarTypeList = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends ScalarTypeConverter<?, ?>>> scalarConverterList = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends AttributeConverter<?, ?>>> 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<Class<? extends IdGenerator>> idGeneratorCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends BeanPersistController>> beanPersistControllerCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends BeanPostLoad>> beanPostLoadCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends BeanPostConstructListener>> beanPostConstructListenerCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends BeanFindController>> beanFindControllerCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends BeanPersistListener>> beanPersistListenerCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends BeanQueryAdapter>> beanQueryAdapterCandidates = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends ServerConfigStartup>> serverConfigStartupCandidates = new ArrayList<>();
|
||||
|
||||
private final List<IdGenerator> idGeneratorInstances = new ArrayList<>();
|
||||
@@ -98,7 +86,7 @@ public class BootupClasses implements ClassFilter {
|
||||
public BootupClasses(List<Class<?>> 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
|
||||
|
||||
@@ -1558,8 +1558,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
|
||||
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (request.includeHash(queryPlan.getHash())) {
|
||||
queryPlan.queryPlanInit(request.getThresholdMicros());
|
||||
if (request.includeHash(queryPlan.getSqlHash())) {
|
||||
queryPlan.queryPlanInit(request.thresholdMicros());
|
||||
list.add(queryPlan.createMeta(null, null));
|
||||
}
|
||||
}
|
||||
@@ -1572,7 +1572,7 @@ public class BeanDescriptor<T> implements BeanType<T>, 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2016,6 +2016,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return owner.getBeanDescriptor(otherType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the table is managed (i.e. an existing m2m relation).
|
||||
*/
|
||||
public boolean isTableManaged(String tableName) {
|
||||
return owner.isTableManaged(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order column property.
|
||||
*/
|
||||
@@ -3191,9 +3198,9 @@ public class BeanDescriptor<T> implements BeanType<T>, 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);
|
||||
|
||||
@@ -445,6 +445,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTypeMana
|
||||
return tableToDescMap.get(tableName.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTableManaged(String tableName) {
|
||||
return tableToDescMap.get(tableName.toLowerCase()) != null
|
||||
|| tableToViewDescMap.get(tableName.toLowerCase()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate entity beans based on views via their dependent tables.
|
||||
*/
|
||||
|
||||
@@ -76,4 +76,10 @@ public interface BeanDescriptorMap {
|
||||
* Return true if Jackson core is present on the classpath.
|
||||
*/
|
||||
boolean isJacksonCorePresent();
|
||||
|
||||
/**
|
||||
* Returns true, if the given table (or view) is managed by ebean
|
||||
* (= an entity exists)
|
||||
*/
|
||||
boolean isTableManaged(String tableName);
|
||||
}
|
||||
|
||||
+1
-5
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -439,6 +439,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
@Override
|
||||
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
|
||||
boolean softDelete = targetDescriptor.isSoftDelete();
|
||||
boolean needsX2Table = softDelete || getExtraWhere() != null;
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
SpiQuery<?> query = request.getQueryRequest().getQuery();
|
||||
if (hasJoinTable()) {
|
||||
@@ -446,7 +447,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
} else {
|
||||
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
|
||||
}
|
||||
if (softDelete && hasJoinTable()) {
|
||||
if (needsX2Table && hasJoinTable()) {
|
||||
sb.append(" x join ");
|
||||
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
|
||||
sb.append(" x2 on ");
|
||||
@@ -461,6 +462,16 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
exportedProperties[i].appendWhere(sb, "x.", path);
|
||||
}
|
||||
if (getExtraWhere() != null) {
|
||||
sb.append(" and ");
|
||||
if (hasJoinTable()) {
|
||||
sb.append(getExtraWhere().replace("${ta}", "x2").replace("${mta}", "x"));
|
||||
} else {
|
||||
sb.append(getExtraWhere().replace("${ta}", "x"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (softDelete) {
|
||||
String alias = hasJoinTable() ? "x2" : "x";
|
||||
sb.append(" and ").append(targetDescriptor.getSoftDeletePredicate(alias));
|
||||
@@ -1061,4 +1072,16 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if we must create a m2m join table.
|
||||
*/
|
||||
public boolean createJoinTable() {
|
||||
if (hasJoinTable() && getMappedBy() == null) {
|
||||
// only create on other 'owning' side
|
||||
return !descriptor.isTableManaged(intersectionJoin.getTable());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,10 +94,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> 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<T> source, BeanPropertyOverride override) {
|
||||
super(source, override);
|
||||
primaryKeyExport = source.primaryKeyExport;
|
||||
primaryKeyJoin = source.primaryKeyJoin;
|
||||
@@ -255,12 +257,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentIdList(List<Object> 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<T> extends BeanPropertyAssoc<T> implements STr
|
||||
return targetDescriptor.getIdProperty();
|
||||
}
|
||||
|
||||
ScalarType getIdScalarType() {
|
||||
ScalarType<?> getIdScalarType() {
|
||||
return targetDescriptor.getIdProperty().scalarType;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ public interface DbSqlContext {
|
||||
/**
|
||||
* Add a join to the sql query.
|
||||
*/
|
||||
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
|
||||
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String extraWhere);
|
||||
|
||||
/**
|
||||
* Push the current table alias onto the stack.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -34,6 +34,8 @@ public final class TableJoin {
|
||||
private final int queryHash;
|
||||
|
||||
private final PropertyForeignKey foreignKey;
|
||||
|
||||
private final String extraWhere;
|
||||
|
||||
public TableJoin(DeployTableJoin deploy) {
|
||||
this(deploy, null);
|
||||
@@ -44,6 +46,7 @@ public final class TableJoin {
|
||||
*/
|
||||
public TableJoin(DeployTableJoin deploy, PropertyForeignKey foreignKey) {
|
||||
this.foreignKey = foreignKey;
|
||||
this.extraWhere = deploy.getExtraWhere();
|
||||
this.table = InternString.intern(deploy.getTable());
|
||||
this.type = deploy.getType();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
@@ -57,6 +60,7 @@ public final class TableJoin {
|
||||
|
||||
private TableJoin(TableJoin source, String overrideColumn) {
|
||||
this.foreignKey = null;
|
||||
this.extraWhere = source.extraWhere;
|
||||
this.table = source.table;
|
||||
this.type = source.type;
|
||||
this.inheritInfo = source.inheritInfo;
|
||||
@@ -146,7 +150,7 @@ public final class TableJoin {
|
||||
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
|
||||
String joinLiteral = joinType.getLiteral(type);
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2);
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2, extraWhere);
|
||||
return joinType.autoToOuter(type);
|
||||
}
|
||||
|
||||
|
||||
+17
-14
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -86,6 +86,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
* collection.
|
||||
*/
|
||||
public void setExtraWhere(String extraWhere) {
|
||||
this.tableJoin.setExtraWhere(extraWhere);
|
||||
this.extraWhere = extraWhere;
|
||||
}
|
||||
|
||||
|
||||
+12
-46
@@ -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<String, BeanProperty> propertyMap;
|
||||
|
||||
private BeanProperty id;
|
||||
|
||||
private final List<BeanProperty> local = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> mutable = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyAssocMany<?>> manys = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> nonManys = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> aggs = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyAssocOne<?>> ones = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyAssocOne<?>> onesImported = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyAssocOne<?>> embedded = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> baseScalar = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> transients = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> 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<BeanProperty> 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<BeanProperty> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ public class DeployTableJoin {
|
||||
private ArrayList<DeployTableJoinColumn> columns = new ArrayList<>(4);
|
||||
|
||||
private InheritInfo inheritInfo;
|
||||
|
||||
private String extraWhere;
|
||||
|
||||
/**
|
||||
* Create a DeployTableJoin.
|
||||
@@ -137,6 +139,18 @@ public class DeployTableJoin {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the clause of an extra @Where annotation.
|
||||
* @return
|
||||
*/
|
||||
public String getExtraWhere() {
|
||||
return extraWhere;
|
||||
}
|
||||
|
||||
public void setExtraWhere(String extraWhere) {
|
||||
this.extraWhere = extraWhere;
|
||||
}
|
||||
|
||||
public DeployTableJoin createInverse(String tableName) {
|
||||
|
||||
DeployTableJoin inverse = new DeployTableJoin();
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ class AnnotationAssocManys extends AnnotationAssoc {
|
||||
|
||||
Where where = prop.getMetaAnnotationWhere(platform);
|
||||
if (where != null) {
|
||||
prop.setExtraWhere(where.clause());
|
||||
prop.setExtraWhere(processFormula(where.clause()));
|
||||
}
|
||||
|
||||
FetchPreference fetchPreference = get(prop, FetchPreference.class);
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
Where where = prop.getMetaAnnotationWhere(platform);
|
||||
if (where != null) {
|
||||
// not expecting this to be used on assoc one properties
|
||||
prop.setExtraWhere(where.clause());
|
||||
prop.setExtraWhere(processFormula(where.clause()));
|
||||
}
|
||||
|
||||
PrimaryKeyJoinColumn primaryKeyJoin = get(prop, PrimaryKeyJoinColumn.class);
|
||||
|
||||
@@ -153,7 +153,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
Formula formula = prop.getMetaAnnotationFormula(platform);
|
||||
if (formula != null) {
|
||||
prop.setSqlFormula(formula.select(), formula.join());
|
||||
prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
|
||||
}
|
||||
|
||||
initWhoProperties(prop);
|
||||
@@ -334,7 +334,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
}
|
||||
Formula formula = prop.getMetaAnnotationFormula(platform);
|
||||
if (formula != null) {
|
||||
prop.setSqlFormula(formula.select(), formula.join());
|
||||
prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
|
||||
}
|
||||
|
||||
final Aggregation aggregation = prop.getMetaAnnotation(Aggregation.class);
|
||||
|
||||
@@ -129,4 +129,11 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any formula from @Formula or @Where.
|
||||
*/
|
||||
protected String processFormula(String source) {
|
||||
return source == null ? null : source.replace("${dbTableName}", descriptor.getBaseTable());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+42
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+8
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ final class BasicProfileLocation implements ProfileLocation {
|
||||
private final String fullLocation;
|
||||
private final String location;
|
||||
private final String label;
|
||||
private final long hash;
|
||||
|
||||
BasicProfileLocation(String fullLocation) {
|
||||
this.fullLocation = fullLocation;
|
||||
this.hash = UtilLocation.hash(fullLocation);
|
||||
this.location = shortDesc(fullLocation);
|
||||
this.label = UtilLocation.label(location);
|
||||
}
|
||||
@@ -42,6 +44,11 @@ final class BasicProfileLocation implements ProfileLocation {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long hash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fullLocation() {
|
||||
return fullLocation;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,14 @@ 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 long hash;
|
||||
|
||||
private final int lineNumber;
|
||||
|
||||
private int traceCount;
|
||||
|
||||
DProfileLocation() {
|
||||
@@ -53,6 +50,7 @@ class DProfileLocation implements ProfileLocation {
|
||||
label = UtilLocation.label(shortDesc);
|
||||
location = shortDesc;
|
||||
fullLocation = loc;
|
||||
hash = UtilLocation.hash(loc);
|
||||
initWith(label);
|
||||
return true;
|
||||
}
|
||||
@@ -71,6 +69,11 @@ class DProfileLocation implements ProfileLocation {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long hash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fullLocation() {
|
||||
return fullLocation;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebeaninternal.server.util.Md5;
|
||||
import io.ebeaninternal.server.util.Checksum;
|
||||
|
||||
class DQueryPlanMeta {
|
||||
|
||||
@@ -10,7 +10,7 @@ class DQueryPlanMeta {
|
||||
private final ProfileLocation profileLocation;
|
||||
private final String name;
|
||||
private final String sql;
|
||||
private final String hash;
|
||||
private final long sqlHash;
|
||||
|
||||
DQueryPlanMeta(Class<?> type, String label, ProfileLocation profileLocation, String sql) {
|
||||
this.type = type;
|
||||
@@ -22,23 +22,15 @@ 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());
|
||||
this.sqlHash = Checksum.checksum(sql);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -57,6 +49,10 @@ class DQueryPlanMeta {
|
||||
return (profileLocation == null) ? null : profileLocation.location();
|
||||
}
|
||||
|
||||
public long getLocationHash() {
|
||||
return (profileLocation == null) ? 0 : profileLocation.hash();
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -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,53 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return meta.getHash();
|
||||
public long sqlHash() {
|
||||
return meta.getSqlHash();
|
||||
}
|
||||
|
||||
@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 locationHash() {
|
||||
return meta.getLocationHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotal() {
|
||||
return stats.getTotal();
|
||||
public long count() {
|
||||
return stats.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMax() {
|
||||
return stats.getMax();
|
||||
public long total() {
|
||||
return stats.total();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMean() {
|
||||
return stats.getMean();
|
||||
public long max() {
|
||||
return stats.max();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long mean() {
|
||||
return stats.mean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,15 @@ 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;
|
||||
private long locationHash;
|
||||
|
||||
DTimeMetricStats(String name, boolean collected, long count, long total, long max) {
|
||||
this.name = name;
|
||||
this.collected = collected;
|
||||
@@ -40,6 +37,7 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
.append(" max:").append(max);
|
||||
if (location != null) {
|
||||
sb.append(" loc:").append(location);
|
||||
sb.append(" locHash:").append(locationHash);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -49,6 +47,11 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocationHash(long locationHash) {
|
||||
this.locationHash = locationHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initialCollection() {
|
||||
return !collected;
|
||||
@@ -60,20 +63,25 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
public String location() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long locationHash() {
|
||||
return locationHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of values collected.
|
||||
*/
|
||||
@Override
|
||||
public long getCount() {
|
||||
public long count() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -81,7 +89,7 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
* Return the total of all the values.
|
||||
*/
|
||||
@Override
|
||||
public long getTotal() {
|
||||
public long total() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -89,7 +97,7 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
* Return the Max value collected.
|
||||
*/
|
||||
@Override
|
||||
public long getMax() {
|
||||
public long max() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@@ -97,7 +105,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebeaninternal.server.util.Checksum;
|
||||
|
||||
final class UtilLocation {
|
||||
|
||||
/**
|
||||
* Return a hash of the full description excluding the source line number.
|
||||
*/
|
||||
static long hash(String full) {
|
||||
final int pos = full.lastIndexOf('(');
|
||||
if (pos > -1) {
|
||||
return Checksum.checksum(full.substring(0, pos));
|
||||
} else {
|
||||
return Checksum.checksum(full);
|
||||
}
|
||||
}
|
||||
|
||||
static String label(String shortDescription) {
|
||||
int pos = shortDescription.indexOf("(");
|
||||
if (pos == -1) {
|
||||
@@ -21,5 +35,4 @@ final class UtilLocation {
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.Version;
|
||||
import io.ebean.bean.*;
|
||||
@@ -139,8 +140,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
*/
|
||||
private PreparedStatement pstmt;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
private String bindLog;
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
@@ -272,15 +271,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
this.cancelled = true;
|
||||
if (pstmt != null) {
|
||||
try {
|
||||
logger.debug("Cancelling query");
|
||||
pstmt.cancel();
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Error cancelling query", e);
|
||||
}
|
||||
}
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
@@ -312,9 +303,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
ResultSet prepareResultSet(boolean forwardOnlyHint) throws SQLException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (cancelled) {
|
||||
throw new SQLException("Query cancelled");
|
||||
}
|
||||
// cancelled before we started
|
||||
query.checkCancelled();
|
||||
startNano = System.nanoTime();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
@@ -342,10 +332,12 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
bindLog = predicates.bind(queryPlan.bindEncryptedProperties(pstmt, conn));
|
||||
return pstmt.executeQuery();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
ResultSet ret = pstmt.executeQuery();
|
||||
query.checkCancelled();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,7 +483,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
boolean hasNext() throws SQLException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (noMoreRows || cancelled) {
|
||||
query.checkCancelled();
|
||||
if (noMoreRows) {
|
||||
return false;
|
||||
}
|
||||
if (hasNextCache) {
|
||||
|
||||
@@ -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();
|
||||
@@ -616,11 +622,19 @@ class CQueryBuilder {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(")");
|
||||
}
|
||||
if (distinct && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
if (distinct && dbOrderBy != null) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
final OrderBy<?> orderBy = query.getOrderBy();
|
||||
if (orderBy != null && orderBy.supportsSelect()) {
|
||||
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
|
||||
String trimmed = DbOrderByTrim.trim(dbOrderBy);
|
||||
if (query.isSingleAttribute() && trimmed.equals(select.getSelectSql())) {
|
||||
// NOP, already in SQL
|
||||
// TODO: what to do if we select("id").orderBy("prop,id")?
|
||||
// Can we live with a query like "select t0.id, t0.prop, t0.id from"
|
||||
// or should we elliminate the second "t0.id" from select
|
||||
} else {
|
||||
sb.append(", ").append(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,11 +66,13 @@ public class CQueryEngine {
|
||||
|
||||
public <T> int delete(OrmQueryRequest<T> request) {
|
||||
CQueryUpdate query = queryBuilder.buildUpdateQuery(true, request);
|
||||
request.setCancelableQuery(query);
|
||||
return executeUpdate(request, query);
|
||||
}
|
||||
|
||||
public <T> int update(OrmQueryRequest<T> request) {
|
||||
CQueryUpdate query = queryBuilder.buildUpdateQuery(false, request);
|
||||
request.setCancelableQuery(query);
|
||||
return executeUpdate(request, query);
|
||||
}
|
||||
|
||||
@@ -97,6 +99,7 @@ public class CQueryEngine {
|
||||
public <A> List<A> findSingleAttributeList(OrmQueryRequest<?> request) {
|
||||
|
||||
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request);
|
||||
request.setCancelableQuery(rcQuery);
|
||||
return findAttributeList(request, rcQuery);
|
||||
}
|
||||
|
||||
@@ -151,6 +154,7 @@ public class CQueryEngine {
|
||||
public <A> List<A> findIds(OrmQueryRequest<?> request) {
|
||||
|
||||
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request);
|
||||
request.setCancelableQuery(rcQuery);
|
||||
return findAttributeList(request, rcQuery);
|
||||
}
|
||||
|
||||
@@ -164,6 +168,7 @@ public class CQueryEngine {
|
||||
public <T> int findCount(OrmQueryRequest<T> request) {
|
||||
|
||||
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
|
||||
request.setCancelableQuery(rcQuery);
|
||||
try {
|
||||
|
||||
int count = rcQuery.findCount();
|
||||
@@ -235,8 +240,10 @@ public class CQueryEngine {
|
||||
|
||||
} catch (SQLException e) {
|
||||
try {
|
||||
PersistenceException pex = cquery.createPersistenceException(e);
|
||||
// create exception before closing connection
|
||||
cquery.close();
|
||||
throw cquery.createPersistenceException(e);
|
||||
throw pex;
|
||||
} finally {
|
||||
request.rollbackTransIfRequired();
|
||||
}
|
||||
@@ -259,6 +266,7 @@ public class CQueryEngine {
|
||||
// order by lower sys period desc
|
||||
query.order().desc(sysPeriodLower);
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
try {
|
||||
cquery.prepareBindExecuteQuery();
|
||||
if (request.isLogSql()) {
|
||||
@@ -327,6 +335,7 @@ public class CQueryEngine {
|
||||
*/
|
||||
public <T> SpiResultSet findResultSet(OrmQueryRequest<T> request) {
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
try {
|
||||
boolean fwdOnly;
|
||||
if (request.isFindIterate()) {
|
||||
@@ -411,6 +420,7 @@ public class CQueryEngine {
|
||||
EntityBean bean = null;
|
||||
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
|
||||
try {
|
||||
cquery.prepareBindExecuteQuery();
|
||||
|
||||
+34
-14
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.CountedValue;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebean.util.JdbcClose;
|
||||
@@ -18,11 +19,12 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Base compiled query request for single attribute queries.
|
||||
*/
|
||||
class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, CancelableQuery {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class);
|
||||
|
||||
@@ -65,6 +67,8 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
private final boolean containsCounts;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
@@ -147,21 +151,27 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
}
|
||||
|
||||
private void prepareExecute() throws SQLException {
|
||||
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
SpiTransaction t = getTransaction();
|
||||
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();
|
||||
}
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
dataReader = new RsetDataReader(request.getDataTimeZone(), pstmt.executeQuery());
|
||||
query.checkCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,4 +204,14 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
Set<String> getDependentTables() {
|
||||
return queryPlan.getDependentTables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Checksum;
|
||||
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,32 @@ 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 long locationHash;
|
||||
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 long sqlHash;
|
||||
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<String> dependentTables;
|
||||
|
||||
private final SpiQueryBindCapture bindCapture;
|
||||
|
||||
/**
|
||||
@@ -106,9 +92,10 @@ 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.locationHash = (profileLocation == null) ? 0 : profileLocation.hash();
|
||||
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 +105,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this);
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCapture(query);
|
||||
this.hash = md5Hash();
|
||||
this.sqlHash = Checksum.checksum(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,9 +117,10 @@ 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.locationHash = (profileLocation == null) ? 0 : profileLocation.hash();
|
||||
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 +131,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this);
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCaptureRaw(sql, query);
|
||||
this.hash = md5Hash();
|
||||
this.sqlHash = Checksum.checksum(sql);
|
||||
}
|
||||
|
||||
private String deriveName(String label, SpiQuery.Type type, String simpleName) {
|
||||
@@ -169,10 +157,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);
|
||||
}
|
||||
@@ -193,8 +177,8 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,6 +203,10 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
return location;
|
||||
}
|
||||
|
||||
public long getLocationHash() {
|
||||
return locationHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanInit(long thresholdMicros) {
|
||||
bindCapture.queryPlanInit(thresholdMicros);
|
||||
@@ -226,7 +214,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput createMeta(String bind, String planString) {
|
||||
return new DQueryPlanOutput(getBeanType(), name, hash, sql, profileLocation, bind, planString);
|
||||
return new DQueryPlanOutput(getBeanType(), name, sqlHash, sql, profileLocation, bind, planString);
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
@@ -273,22 +261,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
|
||||
private String calcAuditQueryKey() {
|
||||
// rawSql needs to include the MD5 hash of the sql
|
||||
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";
|
||||
}
|
||||
return rawSql ? planKey.getPartialKey() + "_" + sqlHash : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
SqlTree getSqlTree() {
|
||||
|
||||
@@ -24,10 +24,10 @@ class CQueryPlanRequest {
|
||||
CQueryPlanRequest(Connection connection, QueryPlanRequest req, Iterator<CQueryBindCapture> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,56 +83,61 @@ 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 locationHash() {
|
||||
return queryPlan.getLocationHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotal() {
|
||||
return metrics.getTotal();
|
||||
public long count() {
|
||||
return metrics.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMax() {
|
||||
return metrics.getMax();
|
||||
public long total() {
|
||||
return metrics.total();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMean() {
|
||||
return metrics.getMean();
|
||||
public long max() {
|
||||
return metrics.max();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return queryPlan.getHash();
|
||||
public long mean() {
|
||||
return metrics.mean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
public long sqlHash() {
|
||||
return queryPlan.getSqlHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sql() {
|
||||
return queryPlan.getSql();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -13,11 +14,12 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Executes the select row count query.
|
||||
*/
|
||||
class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
@@ -57,6 +59,8 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
private int rowCount;
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
@@ -110,14 +114,22 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
rset = pstmt.executeQuery();
|
||||
query.checkCancelled();
|
||||
|
||||
if (!rset.next()) {
|
||||
throw new PersistenceException("Expecting 1 row but got none?");
|
||||
}
|
||||
@@ -161,4 +173,14 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
|
||||
Set<String> getDependentTables() {
|
||||
return queryPlan.getDependentTables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.CancelableQuery;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -10,11 +11,12 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Executes the delete query.
|
||||
* Executes the update query.
|
||||
*/
|
||||
class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
@@ -45,6 +47,8 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
|
||||
private long profileOffset;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
@@ -82,15 +86,22 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
SpiTransaction t = getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
lock.lock();
|
||||
try {
|
||||
query.checkCancelled();
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(pstmt, conn);
|
||||
rowCount = pstmt.executeUpdate();
|
||||
|
||||
query.checkCancelled();
|
||||
|
||||
long executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
request.slowQueryCheck(executionTimeMicros, rowCount);
|
||||
if (queryPlan.executionTime(executionTimeMicros)) {
|
||||
@@ -122,4 +133,14 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
|
||||
.profileStream()
|
||||
.addQueryEvent(query.profileEventId(), profileOffset, desc.getName(), rowCount, query.getProfileId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
lock.lock();
|
||||
try {
|
||||
JdbcClose.cancel(pstmt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
|
||||
private final String sql;
|
||||
private final String bind;
|
||||
private final String plan;
|
||||
|
||||
private String hash;
|
||||
private final long sqlHash;
|
||||
private long queryTimeMicros;
|
||||
private long captureCount;
|
||||
|
||||
DQueryPlanOutput(Class<?> beanType, String label, String hash, String sql, ProfileLocation profileLocation, String bind, String plan) {
|
||||
DQueryPlanOutput(Class<?> beanType, String label, long sqlHash, String sql, ProfileLocation profileLocation, String bind, String plan) {
|
||||
this.beanType = beanType;
|
||||
this.label = label;
|
||||
this.hash = hash;
|
||||
this.sqlHash = sqlHash;
|
||||
this.sql = sql;
|
||||
this.profileLocation = profileLocation;
|
||||
this.bind = bind;
|
||||
@@ -32,15 +31,15 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long sqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,13 +93,13 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
|
||||
* Return the total count of times bind capture has occurred.
|
||||
*/
|
||||
@Override
|
||||
public long getCaptureCount() {
|
||||
public long captureCount() {
|
||||
return captureCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + hash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
|
||||
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + sqlHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,11 +11,10 @@ import java.util.HashSet;
|
||||
class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
private static final String COMMA = ", ";
|
||||
|
||||
private static final String PERIOD = ".";
|
||||
private static final int STRING_BUILDER_INITIAL_CAPACITY = 140;
|
||||
|
||||
private static final String tableAliasPlaceHolder = "${ta}";
|
||||
private static final String tableAliasManyPlaceHolder = "${mta}";
|
||||
|
||||
private final String columnAliasPrefix;
|
||||
|
||||
@@ -111,27 +110,22 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2) {
|
||||
|
||||
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String extraWhere) {
|
||||
if (tableJoins == null) {
|
||||
tableJoins = new HashSet<>();
|
||||
}
|
||||
|
||||
String joinKey = table + "-" + a1 + "-" + a2;
|
||||
if (tableJoins.contains(joinKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tableJoins.add(joinKey);
|
||||
|
||||
sb.append(" ").append(type);
|
||||
boolean addAsOfOnClause = false;
|
||||
if (draftSupport != null) {
|
||||
appendTable(table, draftSupport.getDraftTable(table));
|
||||
|
||||
} else if (!historyQuery) {
|
||||
sb.append(" ").append(table).append(" ");
|
||||
|
||||
} else {
|
||||
// check if there is an associated history table and if so
|
||||
// use the unionAll view - we expect an additional predicate to match
|
||||
@@ -164,13 +158,17 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
if (addAsOfOnClause) {
|
||||
sb.append(" and ").append(historySupport.getAsOfPredicate(a2));
|
||||
}
|
||||
if (extraWhere != null && !extraWhere.isEmpty()) {
|
||||
sb.append(" and ");
|
||||
// we will also need a many-table alias here
|
||||
sb.append(extraWhere.replace(tableAliasPlaceHolder, a2).replace(tableAliasManyPlaceHolder, a1));
|
||||
}
|
||||
}
|
||||
|
||||
private void appendTable(String table, String draftTable) {
|
||||
if (draftTable != null) {
|
||||
// there is an associated history table and view so use that
|
||||
sb.append(" ").append(draftTable).append(" ");
|
||||
|
||||
} else {
|
||||
sb.append(" ").append(table).append(" ");
|
||||
}
|
||||
@@ -193,7 +191,6 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
@Override
|
||||
public String getRelativePrefix(String propName) {
|
||||
|
||||
return currentPrefix == null ? propName : currentPrefix + "." + propName;
|
||||
}
|
||||
|
||||
@@ -228,7 +225,6 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
// the same join has already been added.
|
||||
return;
|
||||
}
|
||||
|
||||
// we only want to add this join once
|
||||
formulaJoins.add(converted);
|
||||
sb.append(" ");
|
||||
@@ -263,13 +259,10 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
@Override
|
||||
public void appendHistorySysPeriod() {
|
||||
|
||||
String tableAlias = tableAliasStack.peek();
|
||||
|
||||
sb.append(COMMA);
|
||||
sb.append(historySupport.getSysPeriodLower(tableAlias));
|
||||
appendColumnAlias();
|
||||
|
||||
sb.append(COMMA);
|
||||
sb.append(historySupport.getSysPeriodUpper(tableAlias));
|
||||
appendColumnAlias();
|
||||
|
||||
@@ -29,7 +29,7 @@ public class DtoQueryEngine {
|
||||
}
|
||||
return rows;
|
||||
|
||||
} catch (Throwable e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
} finally {
|
||||
request.close();
|
||||
@@ -51,7 +51,7 @@ public class DtoQueryEngine {
|
||||
while (request.next()) {
|
||||
consumer.accept(request.readNextBean());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
} finally {
|
||||
request.close();
|
||||
@@ -88,9 +88,8 @@ public class DtoQueryEngine {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user