mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#919 - io.ebean package initial
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wrapper of the list of Id's.
|
||||
*/
|
||||
public class BeanIdList {
|
||||
|
||||
private final List<Object> idList;
|
||||
|
||||
private boolean hasMore;
|
||||
|
||||
public BeanIdList(List<Object> idList) {
|
||||
this.idList = idList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an Id to the list.
|
||||
*/
|
||||
public void add(Object id) {
|
||||
idList.add(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of Id's.
|
||||
*/
|
||||
public List<Object> getIdList() {
|
||||
return idList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if max rows was hit and there is more rows to fetch.
|
||||
*/
|
||||
public boolean isHasMore() {
|
||||
return hasMore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true when max rows is hit and there are more rows to fetch.
|
||||
*/
|
||||
public void setHasMore(boolean hasMore) {
|
||||
this.hasMore = hasMore;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Parameters used for binding to a statement.
|
||||
* <p>
|
||||
* Supports ordered or named parameters.
|
||||
* </p>
|
||||
*/
|
||||
public class BindParams implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4541081933302086285L;
|
||||
|
||||
private final List<Param> positionedParameters = new ArrayList<>();
|
||||
|
||||
private final Map<String, Param> namedParameters = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* This is the sql. For named parameters this is the sql after the named
|
||||
* parameters have been replaced with question mark place holders and the
|
||||
* parameters have been ordered by addNamedParamInOrder().
|
||||
*/
|
||||
private String preparedSql;
|
||||
|
||||
/**
|
||||
* Bind hash and count used to detect when the bind values have changed such
|
||||
* that the generated SQL (with named parameters) needs to be recalculated.
|
||||
*/
|
||||
private int[] bindHash;
|
||||
|
||||
public BindParams() {
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (Param positionedParameter : positionedParameters) {
|
||||
hc = hc * 92821 + positionedParameter.hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash that should be included with the query plan.
|
||||
* <p>
|
||||
* This is to handle binding collections to in clauses. The number of values
|
||||
* in the collection effects the query (number of bind values) and so must be
|
||||
* taken into account when calculating the query hash.
|
||||
* </p>
|
||||
*/
|
||||
public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
|
||||
int[] vals = calcQueryPlanHash();
|
||||
builder.add(vals[0]).bind(vals[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and return a query plan bind hash with total bind count.
|
||||
*/
|
||||
public int[] calcQueryPlanHash() {
|
||||
int tempBindCount;
|
||||
int bc = 0;
|
||||
int hc = 92821;
|
||||
for (Param param : positionedParameters) {
|
||||
tempBindCount = param.queryBindCount();
|
||||
bc += tempBindCount;
|
||||
hc = hc * 92821 + tempBindCount;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
tempBindCount = entry.getValue().queryBindCount();
|
||||
bc += tempBindCount;
|
||||
hc = hc * 92821 + entry.getKey().hashCode();
|
||||
hc = hc * 92821 + tempBindCount;
|
||||
}
|
||||
|
||||
return new int[]{hc, bc};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a deep copy of the BindParams.
|
||||
*/
|
||||
public BindParams copy() {
|
||||
BindParams copy = new BindParams();
|
||||
for (Param p : positionedParameters) {
|
||||
copy.positionedParameters.add(p.copy());
|
||||
}
|
||||
for (Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no bind parameters.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return positionedParameters.isEmpty() && namedParameters.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Natural Key bind param if supported.
|
||||
*/
|
||||
public NaturalKeyBindParam getNaturalKeyBindParam() {
|
||||
if (positionedParameters != null) {
|
||||
return null;
|
||||
}
|
||||
if (namedParameters != null && namedParameters.size() == 1) {
|
||||
Entry<String, Param> e = namedParameters.entrySet().iterator().next();
|
||||
return new NaturalKeyBindParam(e.getKey(), e.getValue().getInValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return positionedParameters.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if named parameters are being used and they have not yet been
|
||||
* ordered. The sql needs to be prepared (named replaced with ?) and the
|
||||
* parameters ordered.
|
||||
*/
|
||||
public boolean requiresNamedParamsPrepare() {
|
||||
return !namedParameters.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a null parameter using position.
|
||||
*/
|
||||
public void setNullParameter(int position, int jdbcType) {
|
||||
Param p = getParam(position);
|
||||
p.setInNullType(jdbcType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an In Out parameter using position.
|
||||
*/
|
||||
public void setParameter(int position, Object value, int outType) {
|
||||
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using position set the In value of a parameter. Note that for nulls you
|
||||
* must use setNullParameter.
|
||||
*/
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the parameter as an Out parameter using position.
|
||||
*/
|
||||
public void registerOut(int position, int outType) {
|
||||
Param p = getParam(position);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
|
||||
private Param getParam(String name) {
|
||||
Param p = namedParameters.get(name);
|
||||
if (p == null) {
|
||||
p = new Param();
|
||||
namedParameters.put(name, p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
private Param getParam(int position) {
|
||||
int more = position - positionedParameters.size();
|
||||
if (more > 0) {
|
||||
for (int i = 0; i < more; i++) {
|
||||
positionedParameters.add(new Param());
|
||||
}
|
||||
}
|
||||
return positionedParameters.get(position - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a named In Out parameter.
|
||||
*/
|
||||
public void setParameter(String name, Object value, int outType) {
|
||||
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a named In parameter that is null.
|
||||
*/
|
||||
public void setNullParameter(String name, int jdbcType) {
|
||||
Param p = getParam(name);
|
||||
p.setInNullType(jdbcType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a named In parameter that is not null.
|
||||
*/
|
||||
public Param setParameter(String name, Object value) {
|
||||
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an encryption key as a bind value.
|
||||
* <p>
|
||||
* Needs special treatment as the value should not be included in a log.
|
||||
* </p>
|
||||
*/
|
||||
public Param setEncryptionKey(String name, Object value) {
|
||||
Param p = getParam(name);
|
||||
p.setEncryptionKey(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the named parameter as an Out parameter.
|
||||
*/
|
||||
public void registerOut(String name, int outType) {
|
||||
Param p = getParam(name);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Parameter for a given position.
|
||||
*/
|
||||
public Param getParameter(int position) {
|
||||
// Used to read Out value by CallableSql
|
||||
return getParam(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named parameter.
|
||||
*/
|
||||
public Param getParameter(String name) {
|
||||
return getParam(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the values of ordered parameters.
|
||||
*/
|
||||
public List<Param> positionedParameters() {
|
||||
return positionedParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sql with named parameters replaced with place holder ?.
|
||||
*/
|
||||
public void setPreparedSql(String preparedSql) {
|
||||
this.preparedSql = preparedSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql with ? place holders (named parameters have been processed
|
||||
* and ordered).
|
||||
*/
|
||||
public String getPreparedSql() {
|
||||
return preparedSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bind hash and count has not changed.
|
||||
*/
|
||||
public boolean isSameBindHash() {
|
||||
|
||||
if (bindHash == null) {
|
||||
bindHash = calcQueryPlanHash();
|
||||
return false;
|
||||
}
|
||||
int[] oldPlan = bindHash;
|
||||
bindHash = calcQueryPlanHash();
|
||||
return bindHash[0] == oldPlan[0] && bindHash[1] == oldPlan[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new positioned parameters orderedList.
|
||||
*/
|
||||
public OrderedList createOrderedList() {
|
||||
positionedParameters.clear();
|
||||
return new OrderedList(positionedParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* The bind parameters in the correct binding order.
|
||||
* <p>
|
||||
* This is the result of converting sql with named parameters
|
||||
* into sql with ? and ordered parameters.
|
||||
* </p>
|
||||
*/
|
||||
public static final class OrderedList {
|
||||
|
||||
private final List<Param> paramList;
|
||||
|
||||
private final StringBuilder preparedSql;
|
||||
|
||||
public OrderedList() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
public OrderedList(List<Param> paramList) {
|
||||
this.paramList = paramList;
|
||||
this.preparedSql = new StringBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a parameter in the correct binding order.
|
||||
*/
|
||||
public void add(Param param) {
|
||||
paramList.add(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of bind parameters in this list.
|
||||
*/
|
||||
public int size() {
|
||||
return paramList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ordered list of bind parameters.
|
||||
*/
|
||||
public List<Param> list() {
|
||||
return paramList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append parsedSql that has named parameters converted into ?.
|
||||
*/
|
||||
public void appendSql(String parsedSql) {
|
||||
preparedSql.append(parsedSql);
|
||||
}
|
||||
|
||||
public String getPreparedSql() {
|
||||
return preparedSql.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A In Out capable parameter for the CallableStatement.
|
||||
*/
|
||||
public static final class Param implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean encryptionKey;
|
||||
|
||||
private boolean isInParam;
|
||||
|
||||
private boolean isOutParam;
|
||||
|
||||
private int type;
|
||||
|
||||
private Object inValue;
|
||||
|
||||
private Object outValue;
|
||||
|
||||
/**
|
||||
* Construct a Parameter.
|
||||
*/
|
||||
public Param() {
|
||||
}
|
||||
|
||||
public int queryBindCount() {
|
||||
if (inValue == null) {
|
||||
return 0;
|
||||
}
|
||||
if (inValue instanceof Collection<?>) {
|
||||
return ((Collection<?>) inValue).size();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy of the Param.
|
||||
*/
|
||||
public Param copy() {
|
||||
Param copy = new Param();
|
||||
copy.isInParam = isInParam;
|
||||
copy.isOutParam = isOutParam;
|
||||
copy.type = type;
|
||||
copy.inValue = inValue;
|
||||
copy.outValue = outValue;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = getClass().hashCode();
|
||||
hc = hc * 92821 + (isInParam ? 0 : 1);
|
||||
hc = hc * 92821 + (isOutParam ? 0 : 1);
|
||||
hc = hc * 92821 + (type);
|
||||
hc = hc * 92821 + (inValue == null ? 0 : inValue.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an In parameter that needs to be bound before
|
||||
* execution.
|
||||
*/
|
||||
public boolean isInParam() {
|
||||
return isInParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an out parameter that needs to be registered
|
||||
* before execution.
|
||||
*/
|
||||
public boolean isOutParam() {
|
||||
return isOutParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the jdbc type of this parameter. Used for registering Out
|
||||
* parameters and setting NULL In parameters.
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Out parameter type.
|
||||
*/
|
||||
public void setOutType(int type) {
|
||||
this.type = type;
|
||||
this.isOutParam = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the In value.
|
||||
*/
|
||||
public void setInValue(Object in) {
|
||||
this.inValue = in;
|
||||
this.isInParam = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an encryption key (which can not be logged).
|
||||
*/
|
||||
public void setEncryptionKey(Object in) {
|
||||
this.inValue = in;
|
||||
this.isInParam = true;
|
||||
this.encryptionKey = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the In parameter is NULL and the specific type that it
|
||||
* is.
|
||||
*/
|
||||
public void setInNullType(int type) {
|
||||
this.type = type;
|
||||
this.inValue = null;
|
||||
this.isInParam = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OUT value that was retrieved. This value is set after
|
||||
* CallableStatement was executed.
|
||||
*/
|
||||
public Object getOutValue() {
|
||||
return outValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the In value. If this is null, then the type should be used to
|
||||
* specify the type of the null.
|
||||
*/
|
||||
public Object getInValue() {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OUT value returned by a CallableStatement after it has
|
||||
* executed.
|
||||
*/
|
||||
public void setOutValue(Object out) {
|
||||
this.outValue = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Key used for caching query plans for ORM and RawSql queries.
|
||||
*/
|
||||
public interface CQueryPlanKey {
|
||||
|
||||
/**
|
||||
* Used by read audit such that we can log read audit entries without the full sql
|
||||
* (which would make the read audit logs verbose).
|
||||
*/
|
||||
String getPartialKey();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
|
||||
/**
|
||||
* Helper to find classes taking into account the context class loader.
|
||||
*/
|
||||
public class ClassUtil {
|
||||
|
||||
/**
|
||||
* Return a new instance of the class using the default constructor.
|
||||
*/
|
||||
public static Object newInstance(String className) {
|
||||
|
||||
try {
|
||||
Class<?> cls = forName(className);
|
||||
return cls.newInstance();
|
||||
} catch (Exception e) {
|
||||
String msg = "Error constructing " + className;
|
||||
throw new IllegalArgumentException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a class taking into account a context class loader (if present).
|
||||
*/
|
||||
public static Class<?> forName(String name) throws ClassNotFoundException {
|
||||
return new ClassLoadContext().forName(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to wrap the context and caller classLoaders (to use/try both).
|
||||
*/
|
||||
static class ClassLoadContext {
|
||||
|
||||
private final ClassLoader contextLoader;
|
||||
|
||||
private final ClassLoader callerLoader;
|
||||
|
||||
ClassLoadContext() {
|
||||
this.callerLoader = ClassUtil.class.getClassLoader();
|
||||
this.contextLoader = contextLoader();
|
||||
}
|
||||
|
||||
ClassLoader contextLoader() {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
return (loader != null) ? loader : callerLoader;
|
||||
}
|
||||
|
||||
public Class<?> forName(String name) throws ClassNotFoundException {
|
||||
|
||||
try {
|
||||
return Class.forName(name, true, contextLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (callerLoader == contextLoader) {
|
||||
throw e;
|
||||
} else {
|
||||
return Class.forName(name, true, callerLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Optimistic concurrency mode used for updates and deletes.
|
||||
*/
|
||||
public enum ConcurrencyMode {
|
||||
|
||||
/**
|
||||
* No concurrency checking.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Use a version column.
|
||||
*/
|
||||
VERSION
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash key for a query including both the query plan and bind values.
|
||||
*/
|
||||
public class HashQuery {
|
||||
|
||||
private final CQueryPlanKey planHash;
|
||||
|
||||
private final int bindHash;
|
||||
|
||||
/**
|
||||
* Create the HashQuery.
|
||||
*/
|
||||
public HashQuery(CQueryPlanKey planHash, int bindHash) {
|
||||
this.planHash = planHash;
|
||||
this.bindHash = bindHash;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "HashQuery@" + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 92821 * planHash.hashCode();
|
||||
hc = 92821 * hc + bindHash;
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQuery)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQuery e = (HashQuery) obj;
|
||||
return e.bindHash == bindHash && e.planHash.equals(planHash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash for a query plan.
|
||||
*/
|
||||
public class HashQueryPlan {
|
||||
|
||||
private final String rawSql;
|
||||
|
||||
private final int planHash;
|
||||
|
||||
private final int bindCount;
|
||||
|
||||
public HashQueryPlan(String rawSql, int planHash, int bindCount) {
|
||||
this.rawSql = rawSql;
|
||||
this.planHash = planHash;
|
||||
this.bindCount = bindCount;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash + ":" + bindCount + (rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as a partial key. For rawSql hash the sql is part of the key and as such
|
||||
* needs to be included in order to have a complete key. Typically the MD5 of the sql
|
||||
* can be used as a shot form proxy for the actual sql.
|
||||
*/
|
||||
public String getPartialKey() {
|
||||
return planHash + "_" + bindCount;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = planHash;
|
||||
hc = hc * 92821 + bindCount;
|
||||
hc = hc * 92821 + (rawSql == null ? 0 : rawSql.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQueryPlan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQueryPlan e = (HashQueryPlan) obj;
|
||||
//noinspection StringEquality
|
||||
return e.planHash == planHash
|
||||
&& e.bindCount == bindCount
|
||||
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
public class HashQueryPlanBuilder {
|
||||
|
||||
private int planHash;
|
||||
|
||||
private int bindCount;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 92821;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash + ":" + bindCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 92821 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 92821 + (object == null ? 0 : object.hashCode());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the set with order being important.
|
||||
*/
|
||||
public HashQueryPlanBuilder addOrdered(Set<?> set) {
|
||||
if (set == null) {
|
||||
add(false);
|
||||
} else {
|
||||
add(true);
|
||||
for (Object o : set) {
|
||||
add(o);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 92821 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 92821 + (booleanValue ? 92821 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a number to the bind count for the hash.
|
||||
*/
|
||||
public void bind(int extraBindCount) {
|
||||
bindCount += extraBindCount;
|
||||
}
|
||||
|
||||
public void bindIfNotNull(Object someValue) {
|
||||
if (someValue != null) {
|
||||
bindCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public String build() {
|
||||
return planHash + "_" + bindCount;
|
||||
}
|
||||
|
||||
public int getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
public int getBindCount() {
|
||||
return bindCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.TxScope;
|
||||
|
||||
/**
|
||||
* Helper object to make AOP generated code simpler.
|
||||
*/
|
||||
public class HelpScopeTrans {
|
||||
|
||||
/**
|
||||
* Create a ScopeTrans for a given methods TxScope.
|
||||
*/
|
||||
public static ScopeTrans createScopeTrans(TxScope txScope) {
|
||||
|
||||
EbeanServer server = Ebean.getServer(txScope.getServerName());
|
||||
SpiEbeanServer iserver = (SpiEbeanServer) server;
|
||||
return iserver.createScopeTrans(txScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exiting the method in an expected fashion.
|
||||
* <p>
|
||||
* That is returning successfully or via a caught exception.
|
||||
* Unexpected exceptions are caught via the Thread uncaughtExceptionHandler.
|
||||
* </p>
|
||||
*
|
||||
* @param returnOrThrowable the return or throwable object
|
||||
* @param opCode the opcode for ATHROW or ARETURN etc
|
||||
* @param scopeTrans the scoped transaction the method was run with.
|
||||
*/
|
||||
public static void onExitScopeTrans(Object returnOrThrowable, int opCode, ScopeTrans scopeTrans) {
|
||||
|
||||
scopeTrans.onExit(returnOrThrowable, opCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A buffer of beans for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadBeanBuffer {
|
||||
|
||||
int getBatchSize();
|
||||
|
||||
List<EntityBeanIntercept> getBatch();
|
||||
|
||||
BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
String getFullPath();
|
||||
|
||||
void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
|
||||
/**
|
||||
* Controls the loading of ManyToOne and OneToOne relationships.
|
||||
*/
|
||||
public interface LoadBeanContext extends LoadSecondaryQuery {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Request for loading ManyToOne and OneToOne relationships.
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoadBeanRequest.class);
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanBuffer loadBuffer;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
/**
|
||||
* Construct for lazy load request.
|
||||
*/
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, String lazyLoadProperty, boolean loadCache) {
|
||||
this(LoadBuffer, null, true, lazyLoadProperty, loadCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for secondary query.
|
||||
*/
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest) {
|
||||
this(LoadBuffer, parentRequest, false, null, false);
|
||||
}
|
||||
|
||||
private LoadBeanRequest(LoadBeanBuffer loadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy,
|
||||
String lazyLoadProperty, boolean loadCache) {
|
||||
|
||||
super(parentRequest, lazy);
|
||||
this.loadBuffer = loadBuffer;
|
||||
this.batch = loadBuffer.getBatch();
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
return loadBuffer.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + loadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch of beans to actually load.
|
||||
*/
|
||||
public List<EntityBeanIntercept> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanBuffer getLoadContext() {
|
||||
return loadBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
public int getBatchSize() {
|
||||
return getLoadContext().getBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of Id values for the beans in the lazy load buffer.
|
||||
*/
|
||||
public List<Object> getIdList(int batchSize) {
|
||||
|
||||
List<Object> idList = new ArrayList<>(batchSize);
|
||||
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
for (EntityBeanIntercept ebi : batch) {
|
||||
EntityBean bean = ebi.getOwner();
|
||||
idList.add(desc.getId(bean));
|
||||
}
|
||||
|
||||
if (!idList.isEmpty()) {
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
// for performance make up the Id's to the batch size
|
||||
// so we get the same query (for Ebean and the db)
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
// just add the first Id again
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the query for lazy loading execution.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query, List<Object> idList) {
|
||||
|
||||
query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN);
|
||||
query.setPersistenceContext(loadBuffer.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, getDescription());
|
||||
|
||||
if (isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(getBatchSize());
|
||||
}
|
||||
|
||||
loadBuffer.configureQuery(query, lazyLoadProperty);
|
||||
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the beans into the L2 cache if that is requested and check for load failures due to deletes.
|
||||
*/
|
||||
public void postLoad(List<?> list) {
|
||||
|
||||
Set<Object> loadedIds = new HashSet<>();
|
||||
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
// collect Ids and maybe load bean cache
|
||||
for (Object aList : list) {
|
||||
EntityBean loadedBean = (EntityBean) aList;
|
||||
loadedIds.add(desc.getId(loadedBean));
|
||||
if (isLoadCache()) {
|
||||
desc.cacheBeanPut(loadedBean);
|
||||
}
|
||||
}
|
||||
|
||||
if (lazyLoadProperty != null) {
|
||||
for (EntityBeanIntercept ebi : batch) {
|
||||
// check if the underlying row in DB was deleted. Mark the bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
Object id = desc.getId(ebi.getOwner());
|
||||
if (!loadedIds.contains(id)) {
|
||||
if (desc.isSoftDelete()) {
|
||||
// assume this is logically deleted (hence not found)
|
||||
desc.setSoftDeleteValue(ebi.getOwner());
|
||||
} else {
|
||||
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
|
||||
ebi.setLazyLoadFailure(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
|
||||
/**
|
||||
* Controls the loading of reference objects for a query instance.
|
||||
*/
|
||||
public interface LoadContext {
|
||||
|
||||
/**
|
||||
* Return the minimum batch size when using QueryIterator with query joins.
|
||||
*/
|
||||
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
|
||||
|
||||
/**
|
||||
* Execute any secondary (+query) queries if there are any defined.
|
||||
*
|
||||
* @param parentRequest the originating query request
|
||||
* @param forEach set true when using findEach iteration
|
||||
*/
|
||||
void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, boolean forEach);
|
||||
|
||||
/**
|
||||
* Return the node for a given path which is used by AutoTune profiling.
|
||||
*/
|
||||
ObjectGraphNode getObjectGraphNode(String path);
|
||||
|
||||
/**
|
||||
* Return the persistence context used by this query and future lazy loading.
|
||||
*/
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Set the persistence context used by this query and future lazy loading.
|
||||
* <p>
|
||||
* Used by query iterator when processing large result sets.
|
||||
* </p>
|
||||
*/
|
||||
void resetPersistenceContext(PersistenceContext persistenceContext);
|
||||
|
||||
/**
|
||||
* Register a Bean for lazy loading.
|
||||
*/
|
||||
void register(String path, EntityBeanIntercept ebi);
|
||||
|
||||
/**
|
||||
* Register a collection for lazy loading.
|
||||
*/
|
||||
void register(String path, BeanCollection<?> bc);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A buffer of bean collections for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadManyBuffer {
|
||||
|
||||
int getBatchSize();
|
||||
|
||||
List<BeanCollection<?>> getBatch();
|
||||
|
||||
BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
String getFullPath();
|
||||
|
||||
void configureQuery(SpiQuery<?> query);
|
||||
|
||||
boolean isUseDocStore();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
|
||||
/**
|
||||
* Controls the loading of OneToMany and ManyToMany relationships.
|
||||
*/
|
||||
public interface LoadManyContext extends LoadSecondaryQuery {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.lib.util.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request for loading Associated Many Beans.
|
||||
*/
|
||||
public class LoadManyRequest extends LoadRequest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoadManyRequest.class);
|
||||
|
||||
private final List<BeanCollection<?>> batch;
|
||||
|
||||
private final LoadManyBuffer loadContext;
|
||||
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
/**
|
||||
* Construct for lazy loading.
|
||||
*/
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, boolean onlyIds, boolean loadCache) {
|
||||
this(loadContext, null, true, onlyIds, loadCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for secondary query.
|
||||
*/
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest) {
|
||||
this(loadContext, parentRequest, false, false, false);
|
||||
}
|
||||
|
||||
private LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, boolean lazy, boolean onlyIds, boolean loadCache) {
|
||||
super(parentRequest, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = loadContext.getBatch();
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
return loadContext.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch of collections to actually load.
|
||||
*/
|
||||
public List<BeanCollection<?>> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyBuffer getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading should only load the id values.
|
||||
* <p>
|
||||
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
|
||||
* generally makes sense to only fetch the Id values as the other property information is not
|
||||
* used.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isOnlyIds() {
|
||||
return onlyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we should load the Collection ids into the cache.
|
||||
*/
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size used for this load context.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return loadContext.getBatchSize();
|
||||
}
|
||||
|
||||
private List<Object> getParentIdList(int batchSize) {
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<>(batchSize);
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
for (BeanCollection<?> bc : batch) {
|
||||
idList.add(many.getParentId(bc.getOwnerBean()));
|
||||
}
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
private BeanPropertyAssocMany<?> getMany() {
|
||||
return loadContext.getBeanProperty();
|
||||
}
|
||||
|
||||
public SpiQuery<?> createQuery(EbeanServer server, int batchSize) {
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
query.orderBy(orderBy);
|
||||
}
|
||||
|
||||
String extraWhere = many.getExtraWhere();
|
||||
if (extraWhere != null) {
|
||||
// replace special ${ta} placeholder with the base table alias
|
||||
// which is always t0 and add the extra where clause
|
||||
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
|
||||
query.where().raw(ew);
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
|
||||
List<Object> idList = getParentIdList(batchSize);
|
||||
many.addWhereParentIdIn(query, idList, loadContext.isUseDocStore());
|
||||
|
||||
query.setPersistenceContext(loadContext.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, getDescription());
|
||||
|
||||
if (isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(getBatchSize());
|
||||
}
|
||||
|
||||
// potentially changes the joins and selected properties
|
||||
loadContext.configureQuery(query);
|
||||
|
||||
if (isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* After the query execution check for empty collections and load L2 cache if desired.
|
||||
*/
|
||||
public void postLoad() {
|
||||
|
||||
BeanDescriptor<?> desc = loadContext.getBeanDescriptor();
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
// check for BeanCollection's that where never processed
|
||||
// in the +query or +lazy load due to no rows (predicates)
|
||||
for (BeanCollection<?> bc : batch) {
|
||||
if (bc.checkEmptyLazyLoad()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
Object parentId = desc.getId(ownerBean);
|
||||
logger.debug("BeanCollection after lazy load was empty. type:" + ownerBean.getClass().getName() + " id:" + parentId + " owner:" + ownerBean);
|
||||
}
|
||||
} else if (isLoadCache()) {
|
||||
Object parentId = desc.getId(bc.getOwnerBean());
|
||||
desc.cacheManyPropPut(many, bc, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Transaction;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
|
||||
/**
|
||||
* Request for loading Associated One Beans.
|
||||
*/
|
||||
public abstract class LoadRequest {
|
||||
|
||||
protected final OrmQueryRequest<?> parentRequest;
|
||||
|
||||
protected final Transaction transaction;
|
||||
|
||||
protected final boolean lazy;
|
||||
|
||||
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
|
||||
|
||||
this.parentRequest = parentRequest;
|
||||
this.transaction = parentRequest == null ? null : parentRequest.getTransaction();
|
||||
this.lazy = lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated bean type for this load request.
|
||||
*/
|
||||
public abstract Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
|
||||
* true. This is for testing purposes to confirm the secondary query executes etc.
|
||||
*/
|
||||
public void logSecondaryQuery(SpiQuery<?> query) {
|
||||
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
|
||||
parentRequest.getQuery().logSecondaryQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a lazy load and false if it is a secondary query.
|
||||
*/
|
||||
public boolean isLazy() {
|
||||
return lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction to use if this is a secondary query.
|
||||
* <p>
|
||||
* Lazy loading queries run in their own transaction.
|
||||
* </p>
|
||||
*/
|
||||
public Transaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the parent query is a findIterate() type query.
|
||||
* So one of - findIterate(), findEach(), findEachWhile() or findVisit().
|
||||
*/
|
||||
public boolean isParentFindIterate() {
|
||||
return parentRequest != null && parentRequest.getQuery().getType() == SpiQuery.Type.ITERATE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
|
||||
/**
|
||||
* Defines the method for executing secondary queries.
|
||||
* <p>
|
||||
* That is +query nodes in a orm query get executed after
|
||||
* the initial query as 'secondary' queries.
|
||||
* </p>
|
||||
*/
|
||||
public interface LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Execute the secondary query with a given batch size.
|
||||
*/
|
||||
void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, boolean forEach);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.query.SplitName;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Holds the joins needs to support the many where predicates.
|
||||
* These joins are independent of any 'fetch' joins on the many.
|
||||
*/
|
||||
public class ManyWhereJoins implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6490181101871795417L;
|
||||
|
||||
private final TreeMap<String, PropertyJoin> joins = new TreeMap<>();
|
||||
|
||||
private StringBuilder formulaProperties = new StringBuilder();
|
||||
|
||||
private boolean formulaWithJoin;
|
||||
|
||||
private boolean aggregation;
|
||||
|
||||
/**
|
||||
* 'Mode' indicating that joins added while this is true are required to be outer joins.
|
||||
*/
|
||||
private boolean requireOuterJoins;
|
||||
|
||||
/**
|
||||
* Return the current 'mode' indicating if outer joins are currently required or not.
|
||||
*/
|
||||
public boolean isRequireOuterJoins() {
|
||||
return requireOuterJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'mode' to be that joins added are required to be outer joins.
|
||||
* This is set during the evaluation of disjunction predicates.
|
||||
*/
|
||||
public void setRequireOuterJoins(boolean requireOuterJoins) {
|
||||
this.requireOuterJoins = requireOuterJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a many where join.
|
||||
*/
|
||||
public void add(ElPropertyDeploy elProp) {
|
||||
|
||||
String join = elProp.getElPrefix();
|
||||
BeanProperty p = elProp.getBeanProperty();
|
||||
if (p instanceof BeanPropertyAssocMany<?>) {
|
||||
join = addManyToJoin(join, p.getName());
|
||||
}
|
||||
if (join != null) {
|
||||
addJoin(join);
|
||||
if (p != null) {
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
addJoin(join + "." + secondaryTableJoinPrefix);
|
||||
}
|
||||
}
|
||||
addParentJoins(join);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For 'many' properties we also need to add the name of the
|
||||
* many property to get the full logical name of the join.
|
||||
*/
|
||||
private String addManyToJoin(String join, String manyPropName) {
|
||||
if (join == null) {
|
||||
return manyPropName;
|
||||
} else {
|
||||
return join + "." + manyPropName;
|
||||
}
|
||||
}
|
||||
|
||||
private void addParentJoins(String join) {
|
||||
String[] split = SplitName.split(join);
|
||||
if (split[0] != null) {
|
||||
addJoin(split[0]);
|
||||
addParentJoins(split[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void addJoin(String property) {
|
||||
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER : SqlJoinType.INNER;
|
||||
joins.put(property, new PropertyJoin(property, joinType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an aggregation query or if there are no extra many where joins.
|
||||
*/
|
||||
public boolean requireSqlDistinct() {
|
||||
return !aggregation && !joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of many where joins.
|
||||
*/
|
||||
public Collection<PropertyJoin> getPropertyJoins() {
|
||||
return joins.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of property names for the many where joins.
|
||||
*/
|
||||
public TreeSet<String> getPropertyNames() {
|
||||
|
||||
TreeSet<String> propertyNames = new TreeSet<>();
|
||||
for (PropertyJoin join : joins.values()) {
|
||||
propertyNames.add(join.getProperty());
|
||||
}
|
||||
return propertyNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* In findRowCount query found a formula property with a join clause so building a select clause
|
||||
* specifically for the findRowCount query.
|
||||
*/
|
||||
public void addFormulaWithJoin(String propertyName) {
|
||||
if (formulaWithJoin) {
|
||||
formulaProperties.append(",");
|
||||
} else {
|
||||
formulaProperties = new StringBuilder();
|
||||
formulaWithJoin = true;
|
||||
}
|
||||
formulaProperties.append(propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query select includes a formula with join.
|
||||
*/
|
||||
public boolean isFormulaWithJoin() {
|
||||
return formulaWithJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formula properties to build the select clause for a findRowCount query.
|
||||
*/
|
||||
public String getFormulaProperties() {
|
||||
return formulaProperties.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this as part of an aggregation query (so using group by clause).
|
||||
*/
|
||||
public void setAggregation() {
|
||||
aggregation = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we have the join required to support the aggregation properties.
|
||||
*/
|
||||
public void addAggregationJoin(String property) {
|
||||
this.aggregation = true;
|
||||
joins.put(property, new PropertyJoin(property, SqlJoinType.INNER));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Object used as a synchronization monitor that is serializable.
|
||||
*/
|
||||
public class Monitor implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2741687226680981940L;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Represents a join required for a given property and whether than needs to be an outer join.
|
||||
*/
|
||||
public class PropertyJoin {
|
||||
|
||||
/**
|
||||
* The property name.
|
||||
*/
|
||||
private final String property;
|
||||
|
||||
/**
|
||||
* Set to true if the property needs to be an outer join.
|
||||
*/
|
||||
private final SqlJoinType joinType;
|
||||
|
||||
public PropertyJoin(String property, SqlJoinType joinType) {
|
||||
this.property = property;
|
||||
this.joinType = joinType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that should be joined.
|
||||
*/
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this join is required to be an outer join.
|
||||
*/
|
||||
public SqlJoinType getSqlJoinType() {
|
||||
return joinType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.PersistBatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Used internally to handle the scoping of transactions for methods.
|
||||
*/
|
||||
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static final int OPCODE_ATHROW = 191;
|
||||
|
||||
private final SpiTransactionScopeManager scopeMgr;
|
||||
|
||||
/**
|
||||
* The suspended transaction (can be null).
|
||||
*/
|
||||
private final SpiTransaction suspendedTransaction;
|
||||
|
||||
/**
|
||||
* The transaction in scope (can be null).
|
||||
*/
|
||||
private final SpiTransaction transaction;
|
||||
|
||||
/**
|
||||
* If true by default rollback on Checked exceptions.
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
|
||||
/**
|
||||
* True if the transaction was created and hence should be committed
|
||||
* on finally if it hasn't already been rolled back.
|
||||
*/
|
||||
private final boolean created;
|
||||
|
||||
/**
|
||||
* Explicit set of Exceptions that DO NOT cause a rollback to occur.
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> noRollbackFor;
|
||||
|
||||
/**
|
||||
* Explicit set of Exceptions that DO cause a rollback to occur.
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
private PersistBatch restoreBatch;
|
||||
|
||||
private PersistBatch restoreBatchOnCascade;
|
||||
|
||||
private int restoreBatchSize;
|
||||
|
||||
private Boolean restoreBatchGeneratedKeys;
|
||||
|
||||
/**
|
||||
* Flag set when a rollback has occurred.
|
||||
*/
|
||||
private boolean rolledBack;
|
||||
|
||||
|
||||
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
|
||||
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
|
||||
|
||||
this.rollbackOnChecked = rollbackOnChecked;
|
||||
this.created = created;
|
||||
this.transaction = transaction;
|
||||
this.suspendedTransaction = suspendedTransaction;
|
||||
this.scopeMgr = scopeMgr;
|
||||
|
||||
this.noRollbackFor = txScope.getNoRollbackFor();
|
||||
this.rollbackFor = txScope.getRollbackFor();
|
||||
|
||||
if (transaction != null) {
|
||||
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
|
||||
restoreBatch = transaction.getBatch();
|
||||
restoreBatchOnCascade = transaction.getBatchOnCascade();
|
||||
restoreBatchSize = transaction.getBatchSize();
|
||||
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatch(txScope.getBatch());
|
||||
}
|
||||
if (txScope.isBatchOnCascadeSet()) {
|
||||
transaction.setBatchOnCascade(txScope.getBatchOnCascade());
|
||||
}
|
||||
if (txScope.isBatchSizeSet()) {
|
||||
transaction.setBatchSize(txScope.getBatchSize());
|
||||
}
|
||||
if (txScope.isSkipGeneratedKeys()) {
|
||||
transaction.setBatchGetGeneratedKeys(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current/active transaction.
|
||||
*/
|
||||
protected SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Thread catches any uncaught exception.
|
||||
* For example, an unexpected NullPointerException or Error.
|
||||
*/
|
||||
public void uncaughtException(Thread thread, Throwable e) {
|
||||
|
||||
// rollback transaction if required
|
||||
caughtThrowable(e);
|
||||
|
||||
// reinstate suspended transaction
|
||||
onFinally();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned via RETURN or expected Exception from the method.
|
||||
*
|
||||
* @param returnOrThrowable the return value or Throwable
|
||||
* @param opCode indicates
|
||||
*/
|
||||
public void onExit(Object returnOrThrowable, int opCode) {
|
||||
|
||||
if (opCode == OPCODE_ATHROW) {
|
||||
// exited with a Throwable
|
||||
caughtThrowable((Throwable) returnOrThrowable);
|
||||
}
|
||||
onFinally();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commit if the transaction exists and has not already been rolled back.
|
||||
* Also reinstate the suspended transaction if there was one.
|
||||
*/
|
||||
public void onFinally() {
|
||||
|
||||
try {
|
||||
if (!rolledBack) {
|
||||
commitTransaction();
|
||||
}
|
||||
} finally {
|
||||
restoreSuspended();
|
||||
}
|
||||
}
|
||||
|
||||
protected void restoreSuspended() {
|
||||
if (suspendedTransaction != null) {
|
||||
// put the previously suspended transaction
|
||||
// back onto the ThreadLocal or equivalent
|
||||
scopeMgr.replace(suspendedTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
protected void commitTransaction() {
|
||||
if (created) {
|
||||
transaction.commit();
|
||||
} else {
|
||||
if (restoreBatch != null) {
|
||||
transaction.setBatch(restoreBatch);
|
||||
}
|
||||
if (restoreBatchOnCascade != null) {
|
||||
transaction.setBatchOnCascade(restoreBatchOnCascade);
|
||||
}
|
||||
if (restoreBatchSize > 0) {
|
||||
transaction.setBatchSize(restoreBatchSize);
|
||||
}
|
||||
if (restoreBatchGeneratedKeys != null) {
|
||||
transaction.setBatchGetGeneratedKeys(restoreBatchGeneratedKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Error was caught and this ALWAYS causes a rollback to occur.
|
||||
* Returns the error and this should be thrown by the calling code.
|
||||
*/
|
||||
public Error caughtError(Error e) {
|
||||
rollback(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the underlying transaction as rollback only.
|
||||
*/
|
||||
public void setRollbackOnly() {
|
||||
if (transaction != null) {
|
||||
transaction.setRollbackOnly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Exception was caught and may or may not cause a rollback to occur.
|
||||
* Returns the exception and this should be thrown by the calling code.
|
||||
*/
|
||||
public <T extends Throwable> T caughtThrowable(T e) {
|
||||
|
||||
if (isRollbackThrowable(e)) {
|
||||
rollback(e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
protected void rollback(Throwable e) {
|
||||
if (transaction != null && transaction.isActive()) {
|
||||
// transaction is null for NOT_SUPPORTED and sometimes SUPPORTS
|
||||
// and Inactive (already rolled back) if nested REQUIRED
|
||||
transaction.rollback(e);
|
||||
}
|
||||
rolledBack = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this throwable should cause a rollback to occur.
|
||||
*/
|
||||
private boolean isRollbackThrowable(Throwable e) {
|
||||
|
||||
if (e instanceof Error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (noRollbackFor != null) {
|
||||
for (Class<? extends Throwable> aNoRollbackFor : noRollbackFor) {
|
||||
if (aNoRollbackFor.equals(e.getClass())) {
|
||||
|
||||
// explicit no rollback for this one
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rollbackFor != null) {
|
||||
for (Class<? extends Throwable> aRollbackFor : rollbackFor) {
|
||||
if (aRollbackFor.equals(e.getClass())) {
|
||||
// explicit rollback for this one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (e instanceof RuntimeException) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// checked exceptions...
|
||||
// EJB defaults this to false which is not intuitive IMO
|
||||
// Ebean makes this configurable (default to true)
|
||||
return rollbackOnChecked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.PersistBatch;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* Wrapper of a ScopeTrans request and it's underlying transaction.
|
||||
*/
|
||||
public class ScopedTransaction implements SpiTransaction {
|
||||
|
||||
final ScopeTrans scopeTrans;
|
||||
|
||||
final SpiTransaction transaction;
|
||||
|
||||
boolean committed;
|
||||
|
||||
public ScopedTransaction(ScopeTrans scopeTrans) {
|
||||
this.scopeTrans = scopeTrans;
|
||||
this.transaction = scopeTrans.getTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() throws RollbackException {
|
||||
transaction.commitAndContinue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws RollbackException {
|
||||
scopeTrans.commitTransaction();
|
||||
committed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws PersistenceException {
|
||||
scopeTrans.rollback(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Throwable e) throws PersistenceException {
|
||||
scopeTrans.rollback(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollbackIfActive() {
|
||||
transaction.rollbackIfActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
scopeTrans.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRollbackOnly() {
|
||||
return transaction.isRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() throws PersistenceException {
|
||||
try {
|
||||
if (!committed) {
|
||||
scopeTrans.rollback(null);
|
||||
}
|
||||
} finally {
|
||||
scopeTrans.restoreSuspended();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTenantId(Object tenantId) {
|
||||
transaction.setTenantId(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTenantId() {
|
||||
return transaction.getTenantId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocStoreTransaction getDocStoreTransaction() {
|
||||
return transaction.getDocStoreTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocStoreMode getDocStoreMode() {
|
||||
return transaction.getDocStoreMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocStoreMode(DocStoreMode mode) {
|
||||
transaction.setDocStoreMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDocStoreBatchSize() {
|
||||
return transaction.getDocStoreBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocStoreBatchSize(int batchSize) {
|
||||
transaction.setDocStoreBatchSize(batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogPrefix() {
|
||||
return transaction.getLogPrefix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSummary() {
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
transaction.logSql(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
transaction.logSummary(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkipCache(boolean skipCache) {
|
||||
transaction.setSkipCache(skipCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
return transaction.isSkipCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBeanChange(BeanChange beanChange) {
|
||||
transaction.addBeanChange(beanChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChangeLog(ChangeSet changes) {
|
||||
transaction.sendChangeLog(changes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDeferred(PersistDeferredRelationship derived) {
|
||||
transaction.registerDeferred(derived);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDeleteBean(Integer hash) {
|
||||
transaction.registerDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDeleteBean(Integer hash) {
|
||||
transaction.unregisterDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisteredDeleteBean(Integer hash) {
|
||||
return transaction.isRegisteredDeleteBean(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterBean(Object bean) {
|
||||
transaction.unregisterBean(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisteredBean(Object bean) {
|
||||
return transaction.isRegisteredBean(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return transaction.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(TransactionCallback callback) {
|
||||
transaction.register(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return transaction.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
transaction.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transaction.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistCascade(boolean persistCascade) {
|
||||
transaction.setPersistCascade(persistCascade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
|
||||
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isUpdateAllLoadedProperties() {
|
||||
return transaction.isUpdateAllLoadedProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchMode(boolean useBatch) {
|
||||
transaction.setBatchMode(useBatch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatch(PersistBatch persistBatchMode) {
|
||||
transaction.setBatch(persistBatchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistBatch getBatch() {
|
||||
return transaction.getBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
|
||||
transaction.setBatchOnCascade(batchOnCascadeMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistBatch getBatchOnCascade() {
|
||||
return transaction.getBatchOnCascade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchSize(int batchSize) {
|
||||
transaction.setBatchSize(batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return transaction.getBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
|
||||
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBatchGetGeneratedKeys() {
|
||||
return transaction.getBatchGetGeneratedKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
|
||||
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
|
||||
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchFlushOnQuery() {
|
||||
return transaction.isBatchFlushOnQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBatch() throws PersistenceException {
|
||||
transaction.flushBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
return transaction.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
|
||||
transaction.addModification(tableName, inserts, updates, deletes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putUserObject(String name, Object value) {
|
||||
transaction.putUserObject(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUserObject(String name) {
|
||||
return transaction.getUserObject(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void depth(int diff) {
|
||||
transaction.depth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int depth() {
|
||||
return transaction.depth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicit() {
|
||||
return transaction.isExplicit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionEvent getEvent() {
|
||||
return transaction.getEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPersistCascade() {
|
||||
return transaction.isPersistCascade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchThisRequest(PersistRequest.Type type) {
|
||||
return transaction.isBatchThisRequest(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchControl getBatchControl() {
|
||||
return transaction.getBatchControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchControl(BatchControl control) {
|
||||
transaction.setBatchControl(control);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
return transaction.getPersistenceContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext context) {
|
||||
transaction.setPersistenceContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getInternalConnection() {
|
||||
return transaction.getInternalConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
|
||||
return transaction.isSaveAssocManyIntersection(intersectionTable, beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request) {
|
||||
return transaction.checkBatchEscalationOnCascade(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBatchOnCascade() {
|
||||
transaction.flushBatchOnCascade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBatchOnRollback() {
|
||||
transaction.flushBatchOnRollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markNotQueryOnly() {
|
||||
transaction.markNotQueryOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkBatchEscalationOnCollection() {
|
||||
transaction.checkBatchEscalationOnCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBatchOnCollection() {
|
||||
transaction.flushBatchOnCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
transaction.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
|
||||
/**
|
||||
* Internal Extension to BackgroundExecutor with shutdown.
|
||||
*/
|
||||
public interface SpiBackgroundExecutor extends BackgroundExecutor {
|
||||
|
||||
/**
|
||||
* Shutdown any associated thread pools.
|
||||
*/
|
||||
void shutdown();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.CallableSql;
|
||||
|
||||
public interface SpiCallableSql extends CallableSql {
|
||||
|
||||
BindParams getBindParams();
|
||||
|
||||
TransactionEventTable getTransactionEventTable();
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.BeanLoader;
|
||||
import io.ebean.bean.CallStack;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.query.CQuery;
|
||||
import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Service Provider extension to EbeanServer.
|
||||
*/
|
||||
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
|
||||
|
||||
/**
|
||||
* For internal use, shutdown of the server invoked by JVM Shutdown.
|
||||
*/
|
||||
void shutdownManaged();
|
||||
|
||||
/**
|
||||
* Return true if query origins should be collected.
|
||||
*/
|
||||
boolean isCollectQueryOrigins();
|
||||
|
||||
/**
|
||||
* Return true if updates in JDBC batch should include all columns if unspecified on the transaction.
|
||||
*/
|
||||
boolean isUpdateAllPropertiesInBatch();
|
||||
|
||||
/**
|
||||
* Return the current Tenant Id.
|
||||
*/
|
||||
Object currentTenantId();
|
||||
|
||||
/**
|
||||
* Return the server configuration.
|
||||
*/
|
||||
ServerConfig getServerConfig();
|
||||
|
||||
/**
|
||||
* Return the DatabasePlatform for this server.
|
||||
*/
|
||||
DatabasePlatform getDatabasePlatform();
|
||||
|
||||
/**
|
||||
* Create an object to represent the current CallStack.
|
||||
* <p>
|
||||
* Typically used to identify the origin of queries for AutoTune and object
|
||||
* graph costing.
|
||||
* </p>
|
||||
*/
|
||||
CallStack createCallStack();
|
||||
|
||||
/**
|
||||
* Return the PersistenceContextScope to use defined at query or server level.
|
||||
*/
|
||||
PersistenceContextScope getPersistenceContextScope(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Clear the query execution statistics.
|
||||
*/
|
||||
void clearQueryStatistics();
|
||||
|
||||
/**
|
||||
* Return all the descriptors.
|
||||
*/
|
||||
List<BeanDescriptor<?>> getBeanDescriptors();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for a given type of bean.
|
||||
*/
|
||||
<T> BeanDescriptor<T> getBeanDescriptor(Class<T> type);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptor using it's unique id.
|
||||
*/
|
||||
BeanDescriptor<?> getBeanDescriptorById(String className);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptor using it's unique doc store queueId.
|
||||
*/
|
||||
BeanDescriptor<?> getBeanDescriptorByQueueId(String queueId);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptors mapped to this table.
|
||||
*/
|
||||
List<BeanDescriptor<?>> getBeanDescriptors(String tableName);
|
||||
|
||||
/**
|
||||
* Process committed changes from another framework.
|
||||
* <p>
|
||||
* This notifies this instance of the framework that beans have been committed
|
||||
* externally to it. Either by another framework or clustered server. It uses
|
||||
* this to maintain its cache and text indexes appropriately.
|
||||
* </p>
|
||||
*/
|
||||
void externalModification(TransactionEventTable event);
|
||||
|
||||
/**
|
||||
* Create a ServerTransaction.
|
||||
* <p>
|
||||
* To specify to use the default transaction isolation use a value of -1.
|
||||
* </p>
|
||||
*/
|
||||
SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel);
|
||||
|
||||
/**
|
||||
* Return the current transaction or null if there is no current transaction.
|
||||
*/
|
||||
SpiTransaction getCurrentServerTransaction();
|
||||
|
||||
/**
|
||||
* Create a ScopeTrans for a method for the given scope definition.
|
||||
*/
|
||||
ScopeTrans createScopeTrans(TxScope txScope);
|
||||
|
||||
/**
|
||||
* Create a ServerTransaction for query purposes.
|
||||
*
|
||||
* @param tenantId For multi-tenant lazy loading provide the tenantId to use.
|
||||
*/
|
||||
SpiTransaction createQueryTransaction(Object tenantId);
|
||||
|
||||
/**
|
||||
* An event from another server in the cluster used to notify local
|
||||
* BeanListeners of remote inserts updates and deletes.
|
||||
*/
|
||||
void remoteTransactionEvent(RemoteTransactionEvent event);
|
||||
|
||||
/**
|
||||
* Compile a query.
|
||||
*/
|
||||
<T> CQuery<T> compileQuery(Query<T> query, Transaction t);
|
||||
|
||||
/**
|
||||
* Execute the findId's query but without copying the query.
|
||||
* <p>
|
||||
* Used so that the list of Id's can be made accessible to client code before
|
||||
* the query has finished (if executing in a background thread).
|
||||
* </p>
|
||||
*/
|
||||
<A, T> List<A> findIdsWithCopy(Query<T> query, Transaction t);
|
||||
|
||||
/**
|
||||
* Execute the findRowCount query but without copying the query.
|
||||
*/
|
||||
<T> int findRowCountWithCopy(Query<T> query, Transaction t);
|
||||
|
||||
/**
|
||||
* Load a batch of Associated One Beans.
|
||||
*/
|
||||
void loadBean(LoadBeanRequest loadRequest);
|
||||
|
||||
/**
|
||||
* Lazy load a batch of Many's.
|
||||
*/
|
||||
void loadMany(LoadManyRequest loadRequest);
|
||||
|
||||
/**
|
||||
* Return the default batch size for lazy loading.
|
||||
*/
|
||||
int getLazyLoadBatchSize();
|
||||
|
||||
/**
|
||||
* Return true if the type is known as an Entity or Xml type or a List Set or
|
||||
* Map of known bean types.
|
||||
*/
|
||||
boolean isSupportedType(java.lang.reflect.Type genericType);
|
||||
|
||||
/**
|
||||
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
|
||||
*/
|
||||
void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger to use for logging all read audit events.
|
||||
*/
|
||||
ReadAuditLogger getReadAuditLogger();
|
||||
|
||||
/**
|
||||
* Return the ReadAuditPrepare used to populate the read audit events with
|
||||
* user context information (user id, user ip address etc).
|
||||
*/
|
||||
ReadAuditPrepare getReadAuditPrepare();
|
||||
|
||||
/**
|
||||
* Return the DataTimeZone to use when reading/writing timestamps via JDBC.
|
||||
*/
|
||||
DataTimeZone getDataTimeZone();
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.expression.DocQueryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* An expression that becomes part of a Where clause or Having clause.
|
||||
*/
|
||||
public interface SpiExpression extends Expression {
|
||||
|
||||
/**
|
||||
* Simplify nested expressions if possible.
|
||||
*/
|
||||
void simplify();
|
||||
|
||||
/**
|
||||
* Write the expression as an elastic search expression.
|
||||
*/
|
||||
void writeDocQuery(DocQueryContext context) throws IOException;
|
||||
|
||||
/**
|
||||
* Return the nested path for this expression.
|
||||
*/
|
||||
String nestedPath(BeanDescriptor<?> desc);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
|
||||
|
||||
/**
|
||||
* Prepare the expression. For example, compile sub-query expressions etc.
|
||||
*/
|
||||
void prepareExpression(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash value used to identify a query for AutoTune tuning.
|
||||
* <p>
|
||||
* That is, if the hash changes then the query will be considered different
|
||||
* from an AutoTune perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
void queryPlanHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Return the hash value for the values that will be bound.
|
||||
*/
|
||||
int queryBindHash();
|
||||
|
||||
/**
|
||||
* Return true if the expression is the same without taking into account bind values.
|
||||
*/
|
||||
boolean isSameByPlan(SpiExpression other);
|
||||
|
||||
/**
|
||||
* Return true if the expression is the same with respect to bind values.
|
||||
*/
|
||||
boolean isSameByBind(SpiExpression other);
|
||||
|
||||
/**
|
||||
* Add some sql to the query.
|
||||
* <p>
|
||||
* This will contain ? as a place holder for each associated bind values.
|
||||
* </p>
|
||||
* <p>
|
||||
* The 'sql' added to the query can contain object property names rather
|
||||
* than db tables and columns. This 'sql' is later parsed converting the
|
||||
* logical property names to their full database column names.
|
||||
* </p>
|
||||
*
|
||||
* @param request the associated request.
|
||||
*/
|
||||
void addSql(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Add the parameter values to be set against query. For each ? place holder
|
||||
* there should be a corresponding value that is added to the bindList.
|
||||
*
|
||||
* @param request the associated request.
|
||||
*/
|
||||
void addBindValues(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Validate all the properties/paths associated with this expression.
|
||||
*/
|
||||
void validate(SpiExpressionValidation validation);
|
||||
|
||||
/**
|
||||
* Return a copy of the expression for use in the query plan key.
|
||||
*/
|
||||
SpiExpression copyForPlanKey();
|
||||
|
||||
/**
|
||||
* Return the bind Id value if this is a "equal to" expression for the id property.
|
||||
*/
|
||||
Object getIdEqualTo(String idName);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.ExpressionFactory;
|
||||
|
||||
public interface SpiExpressionFactory extends ExpressionFactory {
|
||||
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
ExpressionFactory createExpressionFactory();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.Junction;
|
||||
import io.ebeaninternal.server.expression.DocQueryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Internal extension of ExpressionList.
|
||||
*/
|
||||
public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
|
||||
|
||||
/**
|
||||
* Return the expression list as a Junction (for ElasticSearch).
|
||||
*/
|
||||
Junction<T> toJunction();
|
||||
|
||||
/**
|
||||
* Return the underlying list of expressions.
|
||||
*/
|
||||
List<SpiExpression> getUnderlyingList();
|
||||
|
||||
/**
|
||||
* Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
|
||||
*/
|
||||
SpiExpressionList<?> trimPath(int prefixTrim);
|
||||
|
||||
/**
|
||||
* Return true if this list is empty.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Write the top level where expressions taking into account possible extra idEquals expression.
|
||||
*/
|
||||
void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.core.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request object used for gathering expression sql and bind values.
|
||||
*/
|
||||
public interface SpiExpressionRequest {
|
||||
|
||||
/**
|
||||
* Return the DB specific handler for JSON and ARRAY expressions.
|
||||
*/
|
||||
DbExpressionHandler getDbPlatformHandler();
|
||||
|
||||
/**
|
||||
* Parse the logical property name to the deployment name.
|
||||
*/
|
||||
String parseDeploy(String logicalProp);
|
||||
|
||||
/**
|
||||
* Return the bean descriptor for the root type.
|
||||
*/
|
||||
BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the associated QueryRequest.
|
||||
*/
|
||||
SpiOrmQueryRequest<?> getQueryRequest();
|
||||
|
||||
/**
|
||||
* Append to the expression sql.
|
||||
*/
|
||||
SpiExpressionRequest append(String sql);
|
||||
|
||||
/**
|
||||
* Add an encryption key to bind to this request.
|
||||
*/
|
||||
void addBindEncryptKey(Object encryptKey);
|
||||
|
||||
/**
|
||||
* Add a bind value to this request.
|
||||
*/
|
||||
void addBindValue(Object bindValue);
|
||||
|
||||
/**
|
||||
* Return the accumulated expression sql for all expressions in this request.
|
||||
*/
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the ordered list of bind values for all expressions in this request.
|
||||
*/
|
||||
List<Object> getBindValues();
|
||||
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
*/
|
||||
int nextParameter();
|
||||
|
||||
/**
|
||||
* Append a DB Like clause.
|
||||
*/
|
||||
void appendLike();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.plugin.BeanType;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Property expression validation request for a given root bean type.
|
||||
*/
|
||||
public class SpiExpressionValidation {
|
||||
|
||||
private final BeanType<?> desc;
|
||||
|
||||
private final LinkedHashSet<String> unknown = new LinkedHashSet<>();
|
||||
|
||||
public SpiExpressionValidation(BeanType<?> desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the property expression (path) is valid.
|
||||
*/
|
||||
public void validate(String propertyName) {
|
||||
if (!desc.isValidExpression(propertyName)) {
|
||||
unknown.add(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of properties considered as having unknown paths.
|
||||
*/
|
||||
public Set<String> getUnknownProperties() {
|
||||
return unknown;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Junction;
|
||||
import io.ebeaninternal.server.expression.DocQueryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* SPI methods for Junction.
|
||||
*/
|
||||
public interface SpiJunction<T> extends Junction<T> {
|
||||
|
||||
/**
|
||||
* Write the Junction taking into account it is implied.
|
||||
*/
|
||||
void writeDocQueryJunction(DocQueryContext context) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
public interface SpiNamedParam {
|
||||
|
||||
Object getValue();
|
||||
}
|
||||
@@ -0,0 +1,757 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.OrderBy;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.bean.CallStack;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
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.OrmUpdateProperties;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Object Relational query - Internal extension to Query object.
|
||||
*/
|
||||
public interface SpiQuery<T> extends Query<T> {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
|
||||
Mode(boolean loadContextBean) {
|
||||
this.loadContextBean = loadContextBean;
|
||||
}
|
||||
|
||||
private final boolean loadContextBean;
|
||||
|
||||
public boolean isLoadContextBean() {
|
||||
return loadContextBean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of query result.
|
||||
*/
|
||||
enum Type {
|
||||
|
||||
/**
|
||||
* Find by Id or unique returning a single bean.
|
||||
*/
|
||||
BEAN,
|
||||
|
||||
/**
|
||||
* Find iterate type query - findEach(), findIterate() etc.
|
||||
*/
|
||||
ITERATE,
|
||||
|
||||
/**
|
||||
* Find returning a List.
|
||||
*/
|
||||
LIST,
|
||||
|
||||
/**
|
||||
* Find returning a Set.
|
||||
*/
|
||||
SET,
|
||||
|
||||
/**
|
||||
* Find returning a Map.
|
||||
*/
|
||||
MAP,
|
||||
|
||||
/**
|
||||
* Find the Id's.
|
||||
*/
|
||||
ID_LIST,
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
*/
|
||||
ATTRIBUTE,
|
||||
|
||||
/**
|
||||
* Find rowCount.
|
||||
*/
|
||||
ROWCOUNT,
|
||||
|
||||
/**
|
||||
* A subquery used as part of a where clause.
|
||||
*/
|
||||
SUBQUERY,
|
||||
|
||||
/**
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE,
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE,
|
||||
}
|
||||
|
||||
enum TemporalMode {
|
||||
/**
|
||||
* Includes soft deletes rows in the result.
|
||||
*/
|
||||
SOFT_DELETED,
|
||||
|
||||
/**
|
||||
* Query runs against draft tables.
|
||||
*/
|
||||
DRAFT,
|
||||
|
||||
/**
|
||||
* Query runs against current data (normal).
|
||||
*/
|
||||
CURRENT,
|
||||
|
||||
/**
|
||||
* Query runs potentially returning many versions of the same bean.
|
||||
*/
|
||||
VERSIONS,
|
||||
|
||||
/**
|
||||
* Query runs 'As Of' a given date time.
|
||||
*/
|
||||
AS_OF;
|
||||
|
||||
/**
|
||||
* Return the mode of the query of if null return CURRENT mode.
|
||||
*/
|
||||
public static TemporalMode of(SpiQuery<?> query) {
|
||||
return (query != null) ? query.getTemporalMode() : TemporalMode.CURRENT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
void checkIdEqualTo();
|
||||
|
||||
/**
|
||||
* Return true if AutoTune should be attempted on this query.
|
||||
*/
|
||||
boolean isAutoTunable();
|
||||
|
||||
/**
|
||||
* Return true if this is a native sql query.
|
||||
*/
|
||||
boolean isNativeSql();
|
||||
|
||||
/**
|
||||
* Return the unmodified native sql query (with named params etc).
|
||||
*/
|
||||
String getNativeSql();
|
||||
|
||||
/**
|
||||
* Return the bean descriptor for this query.
|
||||
*/
|
||||
BeanDescriptor<T> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return true if this query should be executed against the doc store.
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
|
||||
/**
|
||||
* For doc store query return the document index name to search against.
|
||||
* This is for partitioned indexes (like daily logstash indexes etc).
|
||||
*/
|
||||
String getDocIndexName();
|
||||
|
||||
/**
|
||||
* Return the PersistenceContextScope that this query should use.
|
||||
* <p>
|
||||
* This can be null and in that case use the default scope.
|
||||
* </p>
|
||||
*/
|
||||
PersistenceContextScope getPersistenceContextScope();
|
||||
|
||||
/**
|
||||
* Return the origin key.
|
||||
*/
|
||||
String getOriginKey();
|
||||
|
||||
/**
|
||||
* Return the default lazy load batch size.
|
||||
*/
|
||||
int getLazyLoadBatchSize();
|
||||
|
||||
/**
|
||||
* Return true if select all properties was used to ensure the property
|
||||
* invoking a lazy load was included in the query.
|
||||
*/
|
||||
boolean selectAllForLazyLoadProperty();
|
||||
|
||||
/**
|
||||
* Set the query mode.
|
||||
*/
|
||||
void setMode(Mode m);
|
||||
|
||||
/**
|
||||
* Return the query mode.
|
||||
*/
|
||||
Mode getMode();
|
||||
|
||||
/**
|
||||
* Return the Temporal mode for the query.
|
||||
*/
|
||||
TemporalMode getTemporalMode();
|
||||
|
||||
/**
|
||||
* Return true if this is a find versions between query.
|
||||
*/
|
||||
boolean isVersionsBetween();
|
||||
|
||||
/**
|
||||
* Return the find versions start timestamp.
|
||||
*/
|
||||
Timestamp getVersionStart();
|
||||
|
||||
/**
|
||||
* Return the find versions end timestamp.
|
||||
*/
|
||||
Timestamp getVersionEnd();
|
||||
|
||||
/**
|
||||
* Return true if this is a 'As Of' query.
|
||||
*/
|
||||
boolean isAsOfQuery();
|
||||
|
||||
/**
|
||||
* Return true if this is a 'As Draft' query.
|
||||
*/
|
||||
boolean isAsDraft();
|
||||
|
||||
/**
|
||||
* Return true if this query includes soft deleted rows.
|
||||
*/
|
||||
boolean isIncludeSoftDeletes();
|
||||
|
||||
/**
|
||||
* Return the asOf Timestamp which the query should run as.
|
||||
*/
|
||||
Timestamp getAsOf();
|
||||
|
||||
/**
|
||||
* Return true if the base table is using history.
|
||||
*/
|
||||
boolean isAsOfBaseTable();
|
||||
|
||||
/**
|
||||
* Set when the base table is using history.
|
||||
*/
|
||||
void setAsOfBaseTable();
|
||||
|
||||
/**
|
||||
* Increment the counter of tables used in 'As Of' query.
|
||||
*/
|
||||
void incrementAsOfTableCount();
|
||||
|
||||
/**
|
||||
* Return the table alias used for the base table.
|
||||
*/
|
||||
int getAsOfTableCount();
|
||||
|
||||
void addSoftDeletePredicate(String softDeletePredicate);
|
||||
|
||||
List<String> getSoftDeletePredicates();
|
||||
|
||||
/**
|
||||
* Return a copy of the query.
|
||||
*/
|
||||
SpiQuery<T> copy();
|
||||
|
||||
/**
|
||||
* Return a copy of the query attaching to a different EbeanServer.
|
||||
*/
|
||||
SpiQuery<T> copy(EbeanServer server);
|
||||
|
||||
/**
|
||||
* Return the type of query (List, Set, Map, Bean, rowCount etc).
|
||||
*/
|
||||
Type getType();
|
||||
|
||||
/**
|
||||
* Set the query type (List, Set etc).
|
||||
*/
|
||||
void setType(Type type);
|
||||
|
||||
/**
|
||||
* Return a more detailed description of the lazy or query load.
|
||||
*/
|
||||
String getLoadDescription();
|
||||
|
||||
/**
|
||||
* Return the load mode (+lazy or +query).
|
||||
*/
|
||||
String getLoadMode();
|
||||
|
||||
/**
|
||||
* This becomes a lazy loading query for a many relationship.
|
||||
*/
|
||||
void setLazyLoadForParents(BeanPropertyAssocMany<?> many);
|
||||
|
||||
/**
|
||||
* Return the lazy loading 'many' property.
|
||||
*/
|
||||
BeanPropertyAssocMany<?> getLazyLoadMany();
|
||||
|
||||
/**
|
||||
* Set the load mode (+lazy or +query) and the load description.
|
||||
*/
|
||||
void setLoadDescription(String loadMode, String loadDescription);
|
||||
|
||||
/**
|
||||
* Check that the named parameters have had their values set.
|
||||
*/
|
||||
void checkNamedParameters();
|
||||
|
||||
/**
|
||||
* Create a named parameter placeholder.
|
||||
*/
|
||||
SpiNamedParam createNamedParameter(String parameterName);
|
||||
|
||||
/**
|
||||
* Return the joins required to support predicates on the many properties.
|
||||
*/
|
||||
ManyWhereJoins getManyWhereJoins();
|
||||
|
||||
/**
|
||||
* Return a Natural Key bind parameter if supported by this query.
|
||||
*/
|
||||
NaturalKeyBindParam getNaturalKeyBindParam();
|
||||
|
||||
/**
|
||||
* Prepare the query for docstore execution with nested paths.
|
||||
*/
|
||||
void prepareDocNested();
|
||||
|
||||
/**
|
||||
* Set the query to be a delete query.
|
||||
*/
|
||||
void setDelete();
|
||||
|
||||
/**
|
||||
* Set the query to select the id property only.
|
||||
*/
|
||||
void setSelectId();
|
||||
|
||||
/**
|
||||
* Mark the query as selecting a single attribute.
|
||||
*/
|
||||
void setSingleAttribute();
|
||||
|
||||
/**
|
||||
* Return true if this is singleAttribute query.
|
||||
*/
|
||||
boolean isSingleAttribute();
|
||||
|
||||
/**
|
||||
* Return true if the query should include the Id property.
|
||||
* <p>
|
||||
* distinct and single attribute queries exclude the Id property.
|
||||
* </p>
|
||||
*/
|
||||
boolean isWithId();
|
||||
|
||||
/**
|
||||
* Set a filter to a join path.
|
||||
*/
|
||||
void setFilterMany(String prop, ExpressionList<?> filterMany);
|
||||
|
||||
/**
|
||||
* Set the tenantId to use for lazy loading.
|
||||
*/
|
||||
void setTenantId(Object tenantId);
|
||||
|
||||
/**
|
||||
* Return the tenantId to use for lazy loading.
|
||||
*/
|
||||
Object getTenantId();
|
||||
|
||||
/**
|
||||
* Set the path of the many when +query/+lazy loading query is executed.
|
||||
*/
|
||||
void setLazyLoadManyPath(String lazyLoadManyPath);
|
||||
|
||||
/**
|
||||
* Convert joins as necessary to query joins etc.
|
||||
*/
|
||||
SpiQuerySecondary convertJoins();
|
||||
|
||||
/**
|
||||
* Return the TransactionContext.
|
||||
* <p>
|
||||
* If no TransactionContext is present on the query then the
|
||||
* TransactionContext from the Transaction is used (transaction scoped
|
||||
* persistence context).
|
||||
* </p>
|
||||
*/
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Set an explicit TransactionContext (typically for a refresh query).
|
||||
* <p>
|
||||
* If no TransactionContext is present on the query then the
|
||||
* TransactionContext from the Transaction is used (transaction scoped
|
||||
* persistence context).
|
||||
* </p>
|
||||
*/
|
||||
void setPersistenceContext(PersistenceContext transactionContext);
|
||||
|
||||
/**
|
||||
* Return true if the query detail has neither select or joins specified.
|
||||
*/
|
||||
boolean isDetailEmpty();
|
||||
|
||||
/**
|
||||
* Return explicit AutoTune setting or null. If null then not explicitly
|
||||
* set so we use the default behaviour.
|
||||
*/
|
||||
Boolean isAutoTune();
|
||||
|
||||
/**
|
||||
* Set to true if you want to capture executed secondary queries.
|
||||
*/
|
||||
void setLogSecondaryQuery(boolean logSecondaryQuery);
|
||||
|
||||
/**
|
||||
* Return true if executed secondary queries should be captured.
|
||||
*/
|
||||
boolean isLogSecondaryQuery();
|
||||
|
||||
/**
|
||||
* Return the list of secondary queries that were executed.
|
||||
*/
|
||||
List<SpiQuery<?>> getLoggedSecondaryQueries();
|
||||
|
||||
/**
|
||||
* Log an executed secondary query.
|
||||
*/
|
||||
void logSecondaryQuery(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* If return null then no profiling for this query. If a ProfilingListener is
|
||||
* returned this implies that profiling is turned on for this query (and all
|
||||
* the objects this query creates).
|
||||
*/
|
||||
ProfilingListener getProfilingListener();
|
||||
|
||||
/**
|
||||
* This has the effect of turning on profiling for this query.
|
||||
*/
|
||||
void setProfilingListener(ProfilingListener manager);
|
||||
|
||||
/**
|
||||
* Return the origin point for the query.
|
||||
* <p>
|
||||
* This MUST be call prior to a query being changed via tuning. This is
|
||||
* because the queryPlanHash is used to identify the query point.
|
||||
* </p>
|
||||
*/
|
||||
ObjectGraphNode setOrigin(CallStack callStack);
|
||||
|
||||
/**
|
||||
* Set the profile point of the bean or collection that is lazy loading.
|
||||
* <p>
|
||||
* This enables use to hook this back to the original 'root' query by the
|
||||
* queryPlanHash and stackPoint.
|
||||
* </p>
|
||||
*/
|
||||
void setParentNode(ObjectGraphNode node);
|
||||
|
||||
/**
|
||||
* Set the property that invoked the lazy load and MUST be included in the
|
||||
* lazy loading query.
|
||||
*/
|
||||
void setLazyLoadProperty(String lazyLoadProperty);
|
||||
|
||||
/**
|
||||
* Return the property that invoked lazy load.
|
||||
*/
|
||||
String getLazyLoadProperty();
|
||||
|
||||
/**
|
||||
* Used to hook back a lazy loading query to the original query (query
|
||||
* point).
|
||||
* <p>
|
||||
* This will return null or an "original" query.
|
||||
* </p>
|
||||
*/
|
||||
ObjectGraphNode getParentNode();
|
||||
|
||||
/**
|
||||
* Return false when this is a lazy load or refresh query for a bean.
|
||||
* <p>
|
||||
* We just take/copy the data from those beans and don't collect AutoTune
|
||||
* usage profiling on those lazy load or refresh beans.
|
||||
* </p>
|
||||
*/
|
||||
boolean isUsageProfiling();
|
||||
|
||||
/**
|
||||
* Set to false if this query should not be included in the AutoTune usage
|
||||
* profiling information.
|
||||
*/
|
||||
void setUsageProfiling(boolean usageProfiling);
|
||||
|
||||
/**
|
||||
* Prepare the query which prepares sub-query expressions and calculates
|
||||
* and returns the query plan key.
|
||||
* <p>
|
||||
* The query plan excludes actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
CQueryPlanKey prepare(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
* <p>
|
||||
* Combined with queryPlanHash() to return getQueryHash (a unique hash for a
|
||||
* query).
|
||||
* </p>
|
||||
*/
|
||||
int queryBindHash();
|
||||
|
||||
/**
|
||||
* Identifies queries that are exactly the same including bind variables.
|
||||
*/
|
||||
HashQuery queryHash();
|
||||
|
||||
/**
|
||||
* Return true if this is a RawSql query.
|
||||
*/
|
||||
boolean isRawSql();
|
||||
|
||||
/**
|
||||
* Return the Order By clause or null if there is none defined.
|
||||
*/
|
||||
OrderBy<T> getOrderBy();
|
||||
|
||||
/**
|
||||
* Can return null if no expressions where added to the where clause.
|
||||
*/
|
||||
SpiExpressionList<T> getWhereExpressions();
|
||||
|
||||
/**
|
||||
* Can return null if no expressions where added to the having clause.
|
||||
*/
|
||||
SpiExpressionList<T> getHavingExpressions();
|
||||
|
||||
/**
|
||||
* Return the text expressions.
|
||||
*/
|
||||
SpiExpressionList<T> getTextExpression();
|
||||
|
||||
/**
|
||||
* Returns true if either firstRow or maxRows has been set.
|
||||
*/
|
||||
boolean hasMaxRowsOrFirstRow();
|
||||
|
||||
/**
|
||||
* Return true if the bean cache should be exclude for query or lazy loading.
|
||||
*/
|
||||
boolean isExcludeBeanCache();
|
||||
|
||||
/**
|
||||
* Return true if this query should use the bean cache.
|
||||
* It is not skipped and bean caching is supported.
|
||||
*/
|
||||
boolean isUseBeanCache();
|
||||
|
||||
/**
|
||||
* Return true if this query should use/check the query cache.
|
||||
*/
|
||||
boolean isUseQueryCache();
|
||||
|
||||
/**
|
||||
* Return true if the beans from this query should be loaded into the bean
|
||||
* cache.
|
||||
*/
|
||||
boolean isLoadBeanCache();
|
||||
|
||||
/**
|
||||
* Return true if the beans returned by this query should be read only.
|
||||
*/
|
||||
Boolean isReadOnly();
|
||||
|
||||
/**
|
||||
* Return the query timeout.
|
||||
*/
|
||||
int getTimeout();
|
||||
|
||||
/**
|
||||
* Return the bind parameters.
|
||||
*/
|
||||
BindParams getBindParams();
|
||||
|
||||
/**
|
||||
* Replace the query detail. This is used by the AutoTune feature to as a
|
||||
* fast way to set the query properties and joins.
|
||||
* <p>
|
||||
* Note care must be taken to keep the where, orderBy, firstRows and maxRows
|
||||
* held in the detail attributes.
|
||||
* </p>
|
||||
*/
|
||||
void setDetail(OrmQueryDetail detail);
|
||||
|
||||
/**
|
||||
* AutoTune tune the detail specifying properties to select on already defined joins
|
||||
* and adding extra joins where they are missing.
|
||||
*/
|
||||
boolean tuneFetchProperties(OrmQueryDetail detail);
|
||||
|
||||
/**
|
||||
* If this is a RawSql based entity set the default RawSql if not set.
|
||||
*/
|
||||
void setDefaultRawSqlIfRequired();
|
||||
|
||||
/**
|
||||
* Set to true if this query has been tuned by autoTune.
|
||||
*/
|
||||
void setAutoTuned(boolean autoTuned);
|
||||
|
||||
/**
|
||||
* Return the query detail.
|
||||
*/
|
||||
OrmQueryDetail getDetail();
|
||||
|
||||
/**
|
||||
* Return the extra join for a M2M lazy load.
|
||||
*/
|
||||
TableJoin getM2mIncludeJoin();
|
||||
|
||||
/**
|
||||
* Set the extra join for a M2M lazy load.
|
||||
*/
|
||||
void setM2MIncludeJoin(TableJoin includeTableJoin);
|
||||
|
||||
/**
|
||||
* Return the property used to specify keys for a map.
|
||||
*/
|
||||
String getMapKey();
|
||||
|
||||
/**
|
||||
* Return the maximum number of rows to return in the query.
|
||||
*/
|
||||
int getMaxRows();
|
||||
|
||||
/**
|
||||
* Return the index of the first row to return in the query.
|
||||
*/
|
||||
int getFirstRow();
|
||||
|
||||
/**
|
||||
* Return true if lazy loading has been disabled on the query.
|
||||
*/
|
||||
boolean isDisableLazyLoading();
|
||||
|
||||
/**
|
||||
* Internally set by Ebean when this query must use the DISTINCT keyword.
|
||||
* <p>
|
||||
* This does not exclude/remove the use of the id property.
|
||||
*/
|
||||
void setSqlDistinct(boolean sqlDistinct);
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
|
||||
*/
|
||||
boolean isDistinctQuery();
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user to use DISTINCT.
|
||||
*/
|
||||
boolean isDistinct();
|
||||
|
||||
/**
|
||||
* Set default select clauses where none have been explicitly defined.
|
||||
*/
|
||||
void setDefaultSelectClause();
|
||||
|
||||
/**
|
||||
* Set the generated sql for debug purposes.
|
||||
*/
|
||||
void setGeneratedSql(String generatedSql);
|
||||
|
||||
/**
|
||||
* Set the JDBC fetchSize buffer hint if not explicitly set.
|
||||
*/
|
||||
void setDefaultFetchBuffer(int fetchSize);
|
||||
|
||||
/**
|
||||
* Return the hint for Statement.setFetchSize().
|
||||
*/
|
||||
int getBufferFetchSizeHint();
|
||||
|
||||
/**
|
||||
* Return true if read auditing is disabled on this query.
|
||||
*/
|
||||
boolean isDisableReadAudit();
|
||||
|
||||
/**
|
||||
* Return true if this is a query executing in the background.
|
||||
*/
|
||||
boolean isFutureFetch();
|
||||
|
||||
/**
|
||||
* Set to true to indicate the query is executing in a background thread
|
||||
* asynchronously.
|
||||
*/
|
||||
void setFutureFetch(boolean futureFetch);
|
||||
|
||||
/**
|
||||
* Set the readEvent for future queries (as prepared in foreground thread).
|
||||
*/
|
||||
void setFutureFetchAudit(ReadEvent event);
|
||||
|
||||
/**
|
||||
* Read the readEvent for future queries (null otherwise).
|
||||
*/
|
||||
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 root table alias set by {@link #alias(String)} command.
|
||||
*/
|
||||
String getAlias();
|
||||
|
||||
/**
|
||||
* Validate the query returning the set of properties with unknown paths.
|
||||
*/
|
||||
Set<String> validate(BeanType<T> desc);
|
||||
|
||||
/**
|
||||
* Return the properties for an update query.
|
||||
*/
|
||||
OrmUpdateProperties getUpdateProperties();
|
||||
|
||||
/**
|
||||
* Simplify nested expression lists where possible.
|
||||
*/
|
||||
void simplifyExpressions();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The secondary query paths for 'query joins' and 'lazy loading'.
|
||||
*/
|
||||
public interface SpiQuerySecondary {
|
||||
|
||||
/**
|
||||
* Return a list of path/properties that are query join loaded.
|
||||
*/
|
||||
List<OrmQueryProperties> getQueryJoins();
|
||||
|
||||
/**
|
||||
* Return the list of path/properties that are lazy loaded.
|
||||
*/
|
||||
List<OrmQueryProperties> getLazyJoins();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.SqlQuery;
|
||||
|
||||
/**
|
||||
* SQL query - Internal extension to SqlQuery.
|
||||
*/
|
||||
public interface SpiSqlQuery extends SqlQuery {
|
||||
|
||||
/**
|
||||
* Return the named or positioned parameters.
|
||||
*/
|
||||
BindParams getBindParams();
|
||||
|
||||
/**
|
||||
* return the query.
|
||||
*/
|
||||
String getQuery();
|
||||
|
||||
/**
|
||||
* Return the first row to fetch.
|
||||
*/
|
||||
int getFirstRow();
|
||||
|
||||
/**
|
||||
* Return the maximum number of rows to fetch.
|
||||
*/
|
||||
int getMaxRows();
|
||||
|
||||
/**
|
||||
* Return the query timeout.
|
||||
*/
|
||||
int getTimeout();
|
||||
|
||||
/**
|
||||
* Return the hint for Statement.setFetchSize().
|
||||
*/
|
||||
int getBufferFetchSizeHint();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
|
||||
public interface SpiSqlUpdate extends SqlUpdate {
|
||||
|
||||
BindParams getBindParams();
|
||||
|
||||
void setGeneratedSql(String sql);
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* Extends Transaction with additional API required on server.
|
||||
* <p>
|
||||
* Provides support for batching and TransactionContext.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiTransaction extends Transaction {
|
||||
|
||||
/**
|
||||
* Return the string prefix with the transaction id and label used in logging.
|
||||
*/
|
||||
String getLogPrefix();
|
||||
|
||||
/**
|
||||
* Return true if generated SQL and Bind values should be logged to the
|
||||
* transaction log.
|
||||
*/
|
||||
boolean isLogSql();
|
||||
|
||||
/**
|
||||
* Return true if summary level events should be logged to the transaction
|
||||
* log.
|
||||
*/
|
||||
boolean isLogSummary();
|
||||
|
||||
/**
|
||||
* Log a message to the SQL logger.
|
||||
*/
|
||||
void logSql(String msg);
|
||||
|
||||
/**
|
||||
* Log a message to the SUMMARY logger.
|
||||
*/
|
||||
void logSummary(String msg);
|
||||
|
||||
/**
|
||||
* Register a "Deferred Relationship" that requires an additional update later.
|
||||
*/
|
||||
void registerDeferred(PersistDeferredRelationship derived);
|
||||
|
||||
/**
|
||||
* Add a deleting bean to the registered list.
|
||||
* <p>
|
||||
* This is to handle bi-directional relationships where both sides Cascade.
|
||||
* </p>
|
||||
*/
|
||||
void registerDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Unregister the hash of the bean.
|
||||
*/
|
||||
void unregisterDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been saved/deleted.
|
||||
*/
|
||||
boolean isRegisteredDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Unregister the persisted bean.
|
||||
*/
|
||||
void unregisterBean(Object bean);
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been persisted in the
|
||||
* current recursive save request. The goal is to stop recursively saving
|
||||
* the bean when cascade persist is on both sides of a relationship).
|
||||
* <p>
|
||||
* This will register the bean if it is not already.
|
||||
* </p>
|
||||
*/
|
||||
boolean isRegisteredBean(Object bean);
|
||||
|
||||
/**
|
||||
* Returns a String used to identify the transaction. This id is used for
|
||||
* Transaction logging.
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Return true if this transaction has updateAllLoadedProperties set.
|
||||
* If null is returned the server default is used (set on ServerConfig).
|
||||
*/
|
||||
Boolean isUpdateAllLoadedProperties();
|
||||
|
||||
/**
|
||||
* Return the batchSize specifically set for this transaction or 0.
|
||||
* <p>
|
||||
* Returning 0 implies to use the system wide default batch size.
|
||||
* </p>
|
||||
*/
|
||||
DocStoreMode getDocStoreMode();
|
||||
|
||||
/**
|
||||
* Return the batch size to us for ElasticSearch Bulk API calls
|
||||
* as a result of this transaction.
|
||||
*/
|
||||
int getDocStoreBatchSize();
|
||||
|
||||
/**
|
||||
* Return the batchSize specifically set for this transaction or 0.
|
||||
* <p>
|
||||
* Returning 0 implies to use the system wide default batch size.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
int getBatchSize();
|
||||
|
||||
/**
|
||||
* Return the getGeneratedKeys setting for this transaction.
|
||||
*/
|
||||
Boolean getBatchGetGeneratedKeys();
|
||||
|
||||
/**
|
||||
* Modify and return the current 'depth' of the transaction.
|
||||
* <p>
|
||||
* As we cascade save or delete we traverse the object graph tree. Going up
|
||||
* to Assoc Ones the depth decreases and going down to Assoc Manys the depth
|
||||
* increases.
|
||||
* </p>
|
||||
* <p>
|
||||
* The depth is used for ordering batching statements. The lowest depth get
|
||||
* executed first during save.
|
||||
* </p>
|
||||
*/
|
||||
void depth(int diff);
|
||||
|
||||
/**
|
||||
* Return the current depth.
|
||||
*/
|
||||
int depth();
|
||||
|
||||
/**
|
||||
* Return true if this transaction was created explicitly via
|
||||
* <code>Ebean.beginTransaction()</code>.
|
||||
*/
|
||||
boolean isExplicit();
|
||||
|
||||
/**
|
||||
* Get the object that holds the event details.
|
||||
* <p>
|
||||
* This information is used maintain the table state, cache and text
|
||||
* indexes. On commit the Table modifications this generates is broadcast
|
||||
* around the cluster (if you have a cluster).
|
||||
* </p>
|
||||
*/
|
||||
TransactionEvent getEvent();
|
||||
|
||||
/**
|
||||
* Whether persistCascade is on for save and delete.
|
||||
*/
|
||||
boolean isPersistCascade();
|
||||
|
||||
/**
|
||||
* Return true if this request should be batched. Conversely returns false
|
||||
* if this request should be executed immediately.
|
||||
*/
|
||||
boolean isBatchThisRequest(PersistRequest.Type type);
|
||||
|
||||
/**
|
||||
* Return the BatchControl used to batch up persist requests.
|
||||
*/
|
||||
BatchControl getBatchControl();
|
||||
|
||||
/**
|
||||
* Set the BatchControl used to batch up persist requests. There should only be one
|
||||
* PersistQueue set per transaction.
|
||||
*/
|
||||
void setBatchControl(BatchControl control);
|
||||
|
||||
/**
|
||||
* Return the persistence context associated with this transaction.
|
||||
* <p>
|
||||
* You may wish to hold onto this and set it against another transaction
|
||||
* later. This is along the lines of 'extended persistence context'
|
||||
* behaviour.
|
||||
* </p>
|
||||
*/
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Set the persistence context to this transaction.
|
||||
* <p>
|
||||
* This could be considered similar to 'EJB3 Extended Persistence Context'.
|
||||
* In that you can get the PersistenceContext from a transaction, hold onto
|
||||
* it, and then set it back later to a second transaction. In general there
|
||||
* is one PersistenceContext per Transaction. The getPersistenceContext()
|
||||
* and setPersistenceContext() enable a developer to reuse a single
|
||||
* PersistenceContext with multiple transactions.
|
||||
* </p>
|
||||
*/
|
||||
void setPersistenceContext(PersistenceContext context);
|
||||
|
||||
/**
|
||||
* Return the underlying Connection for internal use.
|
||||
* <p>
|
||||
* If the connection is made from Transaction and the user code calls
|
||||
* that method we can no longer trust the query only status of a
|
||||
* Transaction.
|
||||
* </p>
|
||||
*/
|
||||
Connection getInternalConnection();
|
||||
|
||||
/**
|
||||
* Rollback if the transaction is active. This provides an internal
|
||||
* mechanism for rollback failures occur on commit().
|
||||
*/
|
||||
void rollbackIfActive();
|
||||
|
||||
/**
|
||||
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
|
||||
*/
|
||||
boolean isSaveAssocManyIntersection(String intersectionTable, String beanName);
|
||||
|
||||
/**
|
||||
* Return true if batch mode got escalated for this request (and associated cascades).
|
||||
*/
|
||||
boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request);
|
||||
|
||||
/**
|
||||
* If batch mode was turned on for the request then flush the batch.
|
||||
*/
|
||||
void flushBatchOnCascade();
|
||||
|
||||
/**
|
||||
* If batch was on then effectively clear the batch such that we can handle exceptions and continue.
|
||||
*/
|
||||
void flushBatchOnRollback();
|
||||
|
||||
/**
|
||||
* Mark the transaction explicitly as not being query only.
|
||||
*/
|
||||
void markNotQueryOnly();
|
||||
|
||||
/**
|
||||
* Potentially escalate batch mode on saving or deleting a collection.
|
||||
*/
|
||||
void checkBatchEscalationOnCollection();
|
||||
|
||||
/**
|
||||
* Flush batch if we escalated batch mode on saving or deleting a collection.
|
||||
*/
|
||||
void flushBatchOnCollection();
|
||||
|
||||
/**
|
||||
* Add a bean change to the change log.
|
||||
*/
|
||||
void addBeanChange(BeanChange beanChange);
|
||||
|
||||
/**
|
||||
* Send the change set to be prepared and then logged.
|
||||
*/
|
||||
void sendChangeLog(ChangeSet changeSet);
|
||||
|
||||
/**
|
||||
* Return a document store transaction.
|
||||
*/
|
||||
DocStoreTransaction getDocStoreTransaction();
|
||||
|
||||
/**
|
||||
* Set the current Tenant Id.
|
||||
*/
|
||||
void setTenantId(Object tenantId);
|
||||
|
||||
/**
|
||||
* Return the current Tenant Id.
|
||||
*/
|
||||
Object getTenantId();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
public interface SpiTransactionScopeManager {
|
||||
|
||||
void replace(SpiTransaction t);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Update;
|
||||
|
||||
/**
|
||||
* Internal extension to the Update interface.
|
||||
*/
|
||||
public interface SpiUpdate<T> extends Update<T> {
|
||||
|
||||
/**
|
||||
* The type of the update request.
|
||||
*/
|
||||
enum OrmUpdateType {
|
||||
INSERT {
|
||||
public String toString() {
|
||||
return "Insert";
|
||||
}
|
||||
},
|
||||
UPDATE {
|
||||
public String toString() {
|
||||
return "Update";
|
||||
}
|
||||
},
|
||||
DELETE {
|
||||
public String toString() {
|
||||
return "Delete";
|
||||
}
|
||||
},
|
||||
UNKNOWN {
|
||||
public String toString() {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of bean being updated.
|
||||
*/
|
||||
Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return the type of this - insert, update or delete.
|
||||
*/
|
||||
OrmUpdateType getOrmUpdateType();
|
||||
|
||||
/**
|
||||
* Return the name of the table being modified.
|
||||
*/
|
||||
String getBaseTable();
|
||||
|
||||
/**
|
||||
* Return the update statement. This could be either sql or an orm update with bean types and property names.
|
||||
*/
|
||||
String getUpdateStatement();
|
||||
|
||||
/**
|
||||
* Return the timeout in seconds.
|
||||
*/
|
||||
int getTimeout();
|
||||
|
||||
/**
|
||||
* Return true if the cache should be notified to invalidate objects.
|
||||
*/
|
||||
boolean isNotifyCache();
|
||||
|
||||
/**
|
||||
* Return the bind parameters.
|
||||
*/
|
||||
BindParams getBindParams();
|
||||
|
||||
/**
|
||||
* Set the generated sql used.
|
||||
*/
|
||||
void setGeneratedSql(String sql);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.persist.dml.DmlHandler;
|
||||
import io.ebeaninternal.server.persist.dmlbind.Bindable;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* A plan for executing bean updates for a given set of changed properties.
|
||||
* <p>
|
||||
* This is a cachable plan with the purpose of being being able to skip some
|
||||
* phases of the update bean processing.
|
||||
* </p>
|
||||
* <p>
|
||||
* The plans are cached by the BeanDescriptors.
|
||||
* </>
|
||||
*/
|
||||
public interface SpiUpdatePlan {
|
||||
|
||||
/**
|
||||
* Return true if the set clause has no columns.
|
||||
* <p>
|
||||
* Can occur when the only columns updated have a updatable=false in their
|
||||
* deployment.
|
||||
* </p>
|
||||
*/
|
||||
boolean isEmptySetClause();
|
||||
|
||||
/**
|
||||
* Bind given the request and bean. The bean could be the oldValues bean
|
||||
* when binding a update or delete where clause with ALL concurrency mode.
|
||||
*/
|
||||
void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return the time this plan was created.
|
||||
*/
|
||||
long getTimeCreated();
|
||||
|
||||
/**
|
||||
* Return the time this plan was last used.
|
||||
*/
|
||||
long getTimeLastUsed();
|
||||
|
||||
/**
|
||||
* Return the hash key for this plan.
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
/**
|
||||
* Return the concurrency mode for this plan.
|
||||
*/
|
||||
ConcurrencyMode getMode();
|
||||
|
||||
/**
|
||||
* Return the update SQL statement.
|
||||
*/
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the set of bindable update properties.
|
||||
*/
|
||||
Bindable getSet();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.transaction.DeleteByIdMap;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds information for a transaction. There is one TransactionEvent instance
|
||||
* per Transaction instance.
|
||||
* <p>
|
||||
* When the associated Transaction commits or rollback this information is sent
|
||||
* to the TransactionEventManager.
|
||||
* </p>
|
||||
*/
|
||||
public class TransactionEvent implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7230903304106097120L;
|
||||
|
||||
/**
|
||||
* Flag indicating this is a local transaction (not from another server in
|
||||
* the cluster).
|
||||
*/
|
||||
private final transient boolean local;
|
||||
|
||||
private TransactionEventTable eventTables;
|
||||
|
||||
private transient TransactionEventBeans eventBeans;
|
||||
|
||||
private transient DeleteByIdMap deleteByIdMap;
|
||||
|
||||
/**
|
||||
* Create the TransactionEvent, one per Transaction.
|
||||
*/
|
||||
public TransactionEvent() {
|
||||
this.local = true;
|
||||
}
|
||||
|
||||
public void addDeleteById(BeanDescriptor<?> desc, Object id) {
|
||||
if (deleteByIdMap == null) {
|
||||
deleteByIdMap = new DeleteByIdMap();
|
||||
}
|
||||
deleteByIdMap.add(desc, id);
|
||||
}
|
||||
|
||||
public void addDeleteByIdList(BeanDescriptor<?> desc, List<Object> idList) {
|
||||
if (deleteByIdMap == null) {
|
||||
deleteByIdMap = new DeleteByIdMap();
|
||||
}
|
||||
deleteByIdMap.addList(desc, idList);
|
||||
}
|
||||
|
||||
public DeleteByIdMap getDeleteByIdMap() {
|
||||
return deleteByIdMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this was a local transaction. Returns false if this
|
||||
* transaction originated on another server in the cluster.
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return local;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of PersistRequestBean's for this transaction.
|
||||
*/
|
||||
public List<PersistRequestBean<?>> getPersistRequestBeans() {
|
||||
return (eventBeans == null) ? null : eventBeans.getRequests();
|
||||
}
|
||||
|
||||
public TransactionEventTable getEventTables() {
|
||||
return eventTables;
|
||||
}
|
||||
|
||||
public void add(String tableName, boolean inserts, boolean updates, boolean deletes) {
|
||||
if (eventTables == null) {
|
||||
eventTables = new TransactionEventTable();
|
||||
}
|
||||
eventTables.add(tableName, inserts, updates, deletes);
|
||||
}
|
||||
|
||||
public void add(TransactionEventTable table) {
|
||||
if (eventTables == null) {
|
||||
eventTables = new TransactionEventTable();
|
||||
}
|
||||
eventTables.add(table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a inserted updated or deleted bean to the event.
|
||||
*/
|
||||
public void add(PersistRequestBean<?> request) {
|
||||
|
||||
if (request.isNotify()) {
|
||||
// either a BeanListener or Cache is interested
|
||||
if (eventBeans == null) {
|
||||
eventBeans = new TransactionEventBeans();
|
||||
}
|
||||
eventBeans.add(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the cache changeSet.
|
||||
*/
|
||||
public CacheChangeSet buildCacheChanges(boolean viewInvalidation) {
|
||||
CacheChangeSet changeSet = new CacheChangeSet(viewInvalidation);
|
||||
if (eventBeans != null) {
|
||||
eventBeans.notifyCache(changeSet);
|
||||
}
|
||||
if (deleteByIdMap != null) {
|
||||
deleteByIdMap.notifyCache(changeSet);
|
||||
}
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any relevant PersistRequestBean's to DocStoreUpdates for later processing.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
|
||||
List<PersistRequestBean<?>> persistRequestBeans = getPersistRequestBeans();
|
||||
if (persistRequestBeans != null) {
|
||||
for (PersistRequestBean<?> persistRequestBean : persistRequestBeans) {
|
||||
persistRequestBean.addDocStoreUpdates(docStoreUpdates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Lists of inserted updated and deleted beans that have a BeanPersistListener.
|
||||
* <p>
|
||||
* These beans will be sent to the appropriate BeanListeners after a successful
|
||||
* commit of the transaction.
|
||||
* </p>
|
||||
*/
|
||||
public class TransactionEventBeans {
|
||||
|
||||
final ArrayList<PersistRequestBean<?>> requests = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Return the list of PersistRequests that BeanListeners are interested in.
|
||||
*/
|
||||
public List<PersistRequestBean<?>> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bean for BeanListener notification.
|
||||
*/
|
||||
public void add(PersistRequestBean<?> request) {
|
||||
|
||||
requests.add(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the cache changes.
|
||||
*/
|
||||
public void notifyCache(CacheChangeSet changeSet) {
|
||||
for (PersistRequestBean<?> request : requests) {
|
||||
request.notifyCache(changeSet);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.event.BulkTableEvent;
|
||||
import io.ebeaninternal.server.cluster.BinaryMessage;
|
||||
import io.ebeaninternal.server.cluster.BinaryMessageList;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class TransactionEventTable implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2236555729767483264L;
|
||||
|
||||
private final Map<String, TableIUD> map = new HashMap<>();
|
||||
|
||||
public String toString() {
|
||||
return "TransactionEventTable " + map.values();
|
||||
}
|
||||
|
||||
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
|
||||
|
||||
for (TableIUD tableIud : map.values()) {
|
||||
tableIud.writeBinaryMessage(msgList);
|
||||
}
|
||||
}
|
||||
|
||||
public void readBinaryMessage(DataInput dataInput) throws IOException {
|
||||
|
||||
TableIUD tableIud = TableIUD.readBinaryMessage(dataInput);
|
||||
map.put(tableIud.getTableName(), tableIud);
|
||||
}
|
||||
|
||||
|
||||
public void add(TransactionEventTable table) {
|
||||
|
||||
for (TableIUD iud : table.values()) {
|
||||
add(iud);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(String table, boolean insert, boolean update, boolean delete) {
|
||||
|
||||
table = table.toUpperCase();
|
||||
|
||||
add(new TableIUD(table, insert, update, delete));
|
||||
}
|
||||
|
||||
public void add(TableIUD newTableIUD) {
|
||||
|
||||
TableIUD existingTableIUD = map.put(newTableIUD.getTableName(), newTableIUD);
|
||||
if (existingTableIUD != null) {
|
||||
newTableIUD.add(existingTableIUD);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
public Collection<TableIUD> values() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
public static class TableIUD implements Serializable, BulkTableEvent {
|
||||
|
||||
private static final long serialVersionUID = -1958317571064162089L;
|
||||
|
||||
private final String table;
|
||||
private boolean insert;
|
||||
private boolean update;
|
||||
private boolean delete;
|
||||
|
||||
public TableIUD(String table, boolean insert, boolean update, boolean delete) {
|
||||
this.table = table;
|
||||
this.insert = insert;
|
||||
this.update = update;
|
||||
this.delete = delete;
|
||||
}
|
||||
|
||||
public static TableIUD readBinaryMessage(DataInput dataInput) throws IOException {
|
||||
|
||||
String table = dataInput.readUTF();
|
||||
boolean insert = dataInput.readBoolean();
|
||||
boolean update = dataInput.readBoolean();
|
||||
boolean delete = dataInput.readBoolean();
|
||||
|
||||
return new TableIUD(table, insert, update, delete);
|
||||
}
|
||||
|
||||
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
|
||||
|
||||
BinaryMessage msg = new BinaryMessage(table.length() + 10);
|
||||
DataOutputStream os = msg.getOs();
|
||||
os.writeInt(BinaryMessage.TYPE_TABLEIUD);
|
||||
os.writeUTF(table);
|
||||
os.writeBoolean(insert);
|
||||
os.writeBoolean(update);
|
||||
os.writeBoolean(delete);
|
||||
|
||||
msgList.add(msg);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "TableIUD " + table + " i:" + insert + " u:" + update + " d:" + delete;
|
||||
}
|
||||
|
||||
private void add(TableIUD other) {
|
||||
if (other.insert) {
|
||||
insert = true;
|
||||
}
|
||||
if (other.update) {
|
||||
update = true;
|
||||
}
|
||||
if (other.delete) {
|
||||
delete = true;
|
||||
}
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return table;
|
||||
}
|
||||
|
||||
public boolean isInsert() {
|
||||
return insert;
|
||||
}
|
||||
|
||||
public boolean isUpdate() {
|
||||
return update;
|
||||
}
|
||||
|
||||
public boolean isDelete() {
|
||||
return delete;
|
||||
}
|
||||
|
||||
public boolean isUpdateOrDelete() {
|
||||
return update || delete;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package io.ebeaninternal.api;
|
||||
Reference in New Issue
Block a user