mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d72273dc3 | ||
|
|
cb9a5d6709 | ||
|
|
1b8aa682c6 | ||
|
|
ef39bfcf14 | ||
|
|
749f8d0d45 | ||
|
|
2fd221bbb5 | ||
|
|
ee96c4afa6 | ||
|
|
87a948223c | ||
|
|
acca803a80 | ||
|
|
470b27efe1 | ||
|
|
f287205421 | ||
|
|
6bd8c2bd05 | ||
|
|
05dcdf162b | ||
|
|
e97e77e2e0 | ||
|
|
e685e2f4ac | ||
|
|
c6019bf3c5 | ||
|
|
1a9a4c957c | ||
|
|
bd57a3b9c8 | ||
|
|
21a71c3a4f | ||
|
|
f66439a62e | ||
|
|
73452a5f68 | ||
|
|
5f4789f9ba | ||
|
|
eaac874ac0 | ||
|
|
4900a460d1 | ||
|
|
d16f33d26b | ||
|
|
664f08fd6f | ||
|
|
39416e933e | ||
|
|
74b118e8f0 | ||
|
|
43853b0357 | ||
|
|
f62bc9a35f | ||
|
|
9d8fe97de0 | ||
|
|
b896e55af1 | ||
|
|
bd75f2cf77 | ||
|
|
4f33fe087e | ||
|
|
ccf94bf1a4 | ||
|
|
0acabf820e | ||
|
|
1330fe8fb6 | ||
|
|
09ea5217dc | ||
|
|
236fb5541b | ||
|
|
1cafea20c6 |
@@ -8,7 +8,7 @@ Maven Dependency
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.2.5</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<version>3.3.1-RC1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -129,7 +129,7 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.15</version>
|
||||
<version>5.1.27</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -140,6 +140,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
@@ -147,6 +154,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -57,6 +56,9 @@ final class DRawSqlColumnsParser {
|
||||
split = tmp.toArray(new String[tmp.size()]);
|
||||
}
|
||||
|
||||
if (split.length == 0) {
|
||||
throw new PersistenceException("Huh? Not expecting length=0 when parsing column " + colInfo);
|
||||
}
|
||||
if (split.length == 1) {
|
||||
// default to column the same name as the property
|
||||
return new ColumnMapping.Column(indexPos++, split[0], null);
|
||||
@@ -64,17 +66,18 @@ final class DRawSqlColumnsParser {
|
||||
if (split.length == 2) {
|
||||
return new ColumnMapping.Column(indexPos++, split[0], split[1]);
|
||||
}
|
||||
if (split.length == 3) {
|
||||
if (!split[1].equalsIgnoreCase("as")) {
|
||||
String msg = "Expecting AS keyword parsing column " + colInfo;
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
return new ColumnMapping.Column(indexPos++, split[0], split[2]);
|
||||
// Ok, we now expect/require the AS keyword and it should be the
|
||||
// second to last word in the colInfo content
|
||||
if (!split[split.length - 2].equalsIgnoreCase("as")) {
|
||||
throw new PersistenceException("Expecting AS keyword as second to last word when parsing column " + colInfo);
|
||||
}
|
||||
|
||||
String msg = "Expecting Max 3 words parsing column " + colInfo + ". Got "
|
||||
+ Arrays.toString(split);
|
||||
throw new PersistenceException(msg);
|
||||
// build back the 'column formula' that precedes the AS keyword
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(split[0]);
|
||||
for (int i = 1; i < split.length-2; i++) {
|
||||
sb.append(" ").append(split[i]);
|
||||
}
|
||||
return new ColumnMapping.Column(indexPos++, sb.toString(), split[split.length - 1]);
|
||||
}
|
||||
|
||||
private int nextComma() {
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.persistence.OptimisticLockException;
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
|
||||
@@ -117,6 +118,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
public ExpressionFactory getExpressionFactory();
|
||||
|
||||
/**
|
||||
* Return the MetaInfoManager which is used to get meta data from the EbeanServer
|
||||
* such as query execution statistics.
|
||||
*/
|
||||
public MetaInfoManager getMetaInfoManager();
|
||||
|
||||
|
||||
/**
|
||||
* Return the BeanState for a given entity bean.
|
||||
* <p>
|
||||
|
||||
@@ -73,6 +73,14 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - value between two given properties.
|
||||
*/
|
||||
public static Expression between(String lowProperty, String highProperty, Object value) {
|
||||
|
||||
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater Than - property greater than the given value.
|
||||
*/
|
||||
|
||||
@@ -260,9 +260,11 @@ public interface ExpressionList<T> extends Serializable {
|
||||
public Query<T> setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
|
||||
* Set a QueryListener for bean by bean processing.
|
||||
*
|
||||
* @see Query#setListener(QueryListener)
|
||||
* @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* <pre class="code">
|
||||
* // Normal fetch join results in a single SQL query
|
||||
* List<Order> list = Ebean.find(Order.class).join("details").findList();
|
||||
* List<Order> list = Ebean.find(Order.class).fetch("details").findList();
|
||||
*
|
||||
* // Find Orders join details using a single SQL query
|
||||
* </pre>
|
||||
@@ -51,8 +51,8 @@ import java.io.Serializable;
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("details", new JoinConfig().query())
|
||||
* .fetch("customer", new JoinConfig().query(5))
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .fetch("customer", new FetchConfig().queryFirst(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
@@ -70,7 +70,7 @@ import java.io.Serializable;
|
||||
* .select("status, shipDate")
|
||||
* .fetch("details", "quantity, price", new FetchConfig().query())
|
||||
* .fetch("details.product", "sku, name")
|
||||
* .fetch("customer", "name", new FetchConfig().query(10))
|
||||
* .fetch("customer", "name", new FetchConfig().queryFirst(5))
|
||||
* .fetch("customer.contacts")
|
||||
* .fetch("customer.shippingAddress")
|
||||
* .findList();
|
||||
@@ -96,13 +96,13 @@ import java.io.Serializable;
|
||||
* <pre class="code">
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("customer", new FetchConfig().query(3).lazy(10))
|
||||
* .fetch("customer", new FetchConfig().query(10).lazy(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
* // query 2) find customer where id in (?,?,?) // first 3 customers
|
||||
* // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
|
||||
* // .. then if lazy loading of customers is invoked
|
||||
* // .. use a batch size of 10 to load the customers
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
@@ -128,8 +128,8 @@ import java.io.Serializable;
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* find customer (name)
|
||||
* fetch contact (contactName, phone, email)
|
||||
* fetch shippingAddress (*)
|
||||
* fetch customer.contacts (contactName, phone, email)
|
||||
* fetch customer.shippingAddress (*)
|
||||
* where id in (?,?,?,?,?)
|
||||
*
|
||||
* </pre>
|
||||
@@ -159,6 +159,7 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig lazy() {
|
||||
this.lazyBatchSize = 0;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -170,11 +171,12 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig lazy(int lazyBatchSize) {
|
||||
this.lazyBatchSize = lazyBatchSize;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be loaded as a separate query (rather than as
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* This will use the default batch size for separate query which is 100.
|
||||
@@ -187,14 +189,15 @@ public class FetchConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be loaded as a separate query (rather than as
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* The queryBatchSize is the number of parent id's that this separate query
|
||||
* will load per batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will load all beans on this path eagerly.
|
||||
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
|
||||
* is also used.
|
||||
* </p>
|
||||
*
|
||||
* @param queryBatchSize
|
||||
@@ -202,12 +205,14 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig query(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
this.queryAll = true;
|
||||
// queryAll true as long as a lazy batch size has not already been set
|
||||
this.queryAll = (lazyBatchSize == -1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #query(int)} but only fetches the first batch.
|
||||
* Eagerly fetch the first batch of beans on this path.
|
||||
* This is similar to {@link #query(int)} but only fetches the first batch.
|
||||
* <p>
|
||||
* If there are more parent beans than the batch size then they will not be
|
||||
* loaded eagerly but instead use lazy loading.
|
||||
@@ -228,7 +233,7 @@ public class FetchConfig implements Serializable {
|
||||
public int getLazyBatchSize() {
|
||||
return lazyBatchSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the batch size for separate query load.
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,6 @@ import java.util.List;
|
||||
* Typically you will not construct an OrderBy yourself but use one that exists
|
||||
* on the Query object.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public final class OrderBy<T> implements Serializable {
|
||||
|
||||
@@ -24,7 +22,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
private transient Query<T> query;
|
||||
|
||||
private List<Property> list;
|
||||
private final List<Property> list;
|
||||
|
||||
/**
|
||||
* Create an empty OrderBy with no associated query.
|
||||
@@ -32,6 +30,10 @@ public final class OrderBy<T> implements Serializable {
|
||||
public OrderBy() {
|
||||
this.list = new ArrayList<Property>(2);
|
||||
}
|
||||
|
||||
private OrderBy(List<Property> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an orderBy parsing the order by clause.
|
||||
@@ -81,6 +83,17 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this OrderBy with the path trimmed.
|
||||
*/
|
||||
public OrderBy<T> copyWithTrim(String path) {
|
||||
List<Property> newList = new ArrayList<Property>(list.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
newList.add(list.get(i).copyWithTrim(path));
|
||||
}
|
||||
return new OrderBy<T>(newList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties for this OrderBy.
|
||||
*/
|
||||
@@ -153,31 +166,23 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof OrderBy<?>) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
OrderBy<?> other = (OrderBy<?>) obj;
|
||||
return hashCode() == other.hashCode();
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash();
|
||||
if (!(obj instanceof OrderBy<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderBy<?> e = (OrderBy<?>) obj;
|
||||
return e.list.equals(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash value for this OrderBy. This can be to determine logical
|
||||
* equality for OrderBy clauses.
|
||||
*/
|
||||
public int hash() {
|
||||
int hc = OrderBy.class.getName().hashCode();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
hc = hc * 31 + list.get(i).hash();
|
||||
}
|
||||
return hc;
|
||||
public int hashCode() {
|
||||
return list.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,11 +201,31 @@ public final class OrderBy<T> implements Serializable {
|
||||
this.ascending = ascending;
|
||||
}
|
||||
|
||||
protected int hash() {
|
||||
/**
|
||||
* Return a copy of this Property with the path trimmed.
|
||||
*/
|
||||
public Property copyWithTrim(String path) {
|
||||
return new Property(property.substring(path.length() + 1), ascending);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = property.hashCode();
|
||||
hc = hc * 31 + (ascending ? 0 : 1);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Property e = (Property) obj;
|
||||
return e.ascending == ascending
|
||||
&& e.property.equals(property);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toStringFormat();
|
||||
|
||||
@@ -684,7 +684,10 @@ public interface Query<T> extends Serializable {
|
||||
public Query<T> setParameter(int position, Object value);
|
||||
|
||||
/**
|
||||
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
* <p>
|
||||
* Set a listener to process the query on a row by row basis.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use this when you want to process a large query and do not want to hold the
|
||||
* entire query result in memory.
|
||||
@@ -706,6 +709,7 @@ public interface Query<T> extends Serializable {
|
||||
* // list (emptyList) will be empty ...
|
||||
* List<Order> emtyList = query.findList();
|
||||
* </pre>
|
||||
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Deprecated, please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
* <p>
|
||||
* Provides a mechanism for processing a query one bean at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the query will return a large number of results and you
|
||||
* want to process the beans one at a time rather than whole all of the beans in
|
||||
@@ -23,6 +26,8 @@ package com.avaje.ebean;
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean
|
||||
* @deprecated Please migrate to using {@link #findIterate()} or
|
||||
* {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public interface QueryListener<T> {
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ package com.avaje.ebean;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.io.Closeable;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* The Transaction object. Typically representing a JDBC or JTA transaction.
|
||||
*/
|
||||
public interface Transaction {
|
||||
public interface Transaction extends Closeable {
|
||||
|
||||
/**
|
||||
* Read Committed transaction isolation. Same as
|
||||
|
||||
@@ -145,14 +145,19 @@ public interface BeanCollection<E> extends Serializable {
|
||||
public boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns the underlying details as an iterator.
|
||||
* <p>
|
||||
* Note that for maps this returns the entrySet as we need the keys of the
|
||||
* map.
|
||||
* </p>
|
||||
* Returns the underlying collection of beans from the Set, Map or List.
|
||||
*/
|
||||
public Collection<E> getActualDetails();
|
||||
|
||||
/**
|
||||
* Returns the underlying entries so for Maps this is a collection of
|
||||
* Map.Entry.
|
||||
* <p>
|
||||
* For maps this returns the entrySet as we need the keys of the map.
|
||||
* </p>
|
||||
*/
|
||||
public Collection<?> getActualEntries();
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Represent the call stack (stack trace elements).
|
||||
@@ -34,6 +35,25 @@ public final class CallStack implements Serializable {
|
||||
}
|
||||
this.pathHash = enc(hc);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 0;
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
hc = 31 * hc + callStack[i].hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CallStack)) {
|
||||
return false;
|
||||
}
|
||||
CallStack e = (CallStack) obj;
|
||||
return Arrays.equals(callStack, e.callStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first element of the call stack.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Identifies a unique node of an object graph.
|
||||
@@ -64,6 +65,25 @@ public final class ObjectGraphNode implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "origin:" + originQueryPoint + " " + ":" + path + ":" + path;
|
||||
return "origin:" + originQueryPoint + " path[" + path+"]";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * originQueryPoint.hashCode();
|
||||
hc = 31 * hc + Objects.hashCode(path);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ObjectGraphNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGraphNode e = (ObjectGraphNode) obj;
|
||||
return Objects.equals(e.path, path)
|
||||
&& e.originQueryPoint.equals(originQueryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public final class ObjectGraphOrigin implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 410937765287968707L;
|
||||
private static final long serialVersionUID = 410937765287968708L;
|
||||
|
||||
private final CallStack callStack;
|
||||
|
||||
private final String key;
|
||||
|
||||
private final String beanType;
|
||||
|
||||
private final int queryHash;
|
||||
|
||||
private final String key;
|
||||
|
||||
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
|
||||
this.callStack = callStack;
|
||||
this.beanType = beanType;
|
||||
this.queryHash = queryHash;
|
||||
this.key = callStack.getOriginKey(queryHash);
|
||||
}
|
||||
|
||||
@@ -55,7 +58,27 @@ public final class ObjectGraphOrigin implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return key + " " + beanType + " " + callStack.getFirstStackTraceElement();
|
||||
return "key["+ key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement()+" ";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * callStack.hashCode();
|
||||
hc = 31 * hc + beanType.hashCode();
|
||||
hc = 31 * hc + queryHash;
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ObjectGraphOrigin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
|
||||
return e.queryHash == queryHash
|
||||
&& e.beanType.equals(beanType)
|
||||
&& e.callStack.equals(callStack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ public interface PersistenceContext {
|
||||
*/
|
||||
public Object get(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Get the bean from the persistence context also checked to see if it had
|
||||
* been previously deleted (if so then you also can't hit the L2 cache to
|
||||
* fetch the bean for this particular persistence context).
|
||||
*/
|
||||
public WithOption getWithOption(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Clear all the references.
|
||||
*/
|
||||
@@ -44,9 +51,60 @@ public interface PersistenceContext {
|
||||
*/
|
||||
public void clear(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Clear the reference as a result of an entity being deleted.
|
||||
*/
|
||||
public void deleted(Class<?> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Return the number of beans of the given type in the persistence context.
|
||||
*/
|
||||
public int size(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Wrapper on a bean to also indicate if a bean has been deleted.
|
||||
* <p>
|
||||
* If a bean has been deleted then for the same persistence context is should
|
||||
* not be able to be fetched from persistence context or L2 cache.
|
||||
* </p>
|
||||
*/
|
||||
public static class WithOption {
|
||||
|
||||
/**
|
||||
* The bean was previously deleted from this persistence context (can't hit
|
||||
* L2 cache).
|
||||
*/
|
||||
public static WithOption DELETED = new WithOption(true);
|
||||
|
||||
private final boolean deleted;
|
||||
private final Object bean;
|
||||
|
||||
private WithOption(boolean deleted) {
|
||||
this.deleted = true;
|
||||
this.bean = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The bean exists in the persistence context (and not been previously deleted).
|
||||
*/
|
||||
public WithOption(Object bean) {
|
||||
this.deleted = false;
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean was deleted. This means you can't hit the L2
|
||||
* cache.
|
||||
*/
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean (from the persistence context).
|
||||
*/
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (list == null) {
|
||||
list = new ArrayList<E>();
|
||||
}
|
||||
list.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -106,6 +109,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
public Collection<E> getActualDetails() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying list.
|
||||
|
||||
@@ -37,6 +37,14 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
super(ebeanServer, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalPut(Object key, Object bean) {
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<K, E>();
|
||||
}
|
||||
map.put((K)key, (E)bean);
|
||||
}
|
||||
|
||||
public void internalAdd(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
@@ -104,14 +112,23 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map entrySet iterator.
|
||||
* Returns the collection of beans (map values).
|
||||
*/
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the map entrySet.
|
||||
* <p>
|
||||
* This is because the key values may need to be set against the details (so
|
||||
* they don't need to be set twice).
|
||||
* </p>
|
||||
*/
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
public Collection<?> getActualEntries() {
|
||||
return map.entrySet();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
/**
|
||||
* Set capable of lazy loading.
|
||||
*/
|
||||
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>,
|
||||
BeanCollectionAdd {
|
||||
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
|
||||
|
||||
/**
|
||||
* The underlying Set implementation.
|
||||
@@ -45,6 +44,9 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet<E>();
|
||||
}
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -113,6 +115,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying set.
|
||||
*/
|
||||
|
||||
@@ -36,25 +36,31 @@ public class DataSourceConfig {
|
||||
|
||||
private int heartbeatFreqSecs = 30;
|
||||
|
||||
private int heartbeatTimeoutSeconds = 3;
|
||||
|
||||
private boolean captureStackTrace;
|
||||
|
||||
private int maxStackTraceSize = 5;
|
||||
|
||||
private int leakTimeMinutes = 30;
|
||||
|
||||
private int maxInactiveTimeSecs = 900;
|
||||
private int maxInactiveTimeSecs = 720;
|
||||
|
||||
private int maxAgeMinutes = 0;
|
||||
|
||||
private int trimPoolFreqSecs = 59;
|
||||
|
||||
private int pstmtCacheSize = 20;
|
||||
|
||||
private int cstmtCacheSize = 20;
|
||||
|
||||
private int waitTimeoutMillis = 1000;
|
||||
|
||||
|
||||
private String poolListener;
|
||||
|
||||
private boolean offline;
|
||||
|
||||
Map<String, String> customProperties;
|
||||
|
||||
protected Map<String, String> customProperties;
|
||||
|
||||
/**
|
||||
* Return the connection URL.
|
||||
@@ -194,6 +200,20 @@ public class DataSourceConfig {
|
||||
public void setHeartbeatFreqSecs(int heartbeatFreqSecs) {
|
||||
this.heartbeatFreqSecs = heartbeatFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the heart beat timeout in seconds.
|
||||
*/
|
||||
public int getHeartbeatTimeoutSeconds() {
|
||||
return heartbeatTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the heart beat timeout in seconds.
|
||||
*/
|
||||
public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) {
|
||||
this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a stack trace should be captured when obtaining a connection
|
||||
@@ -309,6 +329,23 @@ public class DataSourceConfig {
|
||||
return maxInactiveTimeSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum age a connection is allowed to be before it is closed.
|
||||
* <p>
|
||||
* This can be used to close really old connections.
|
||||
* </p>
|
||||
*/
|
||||
public int getMaxAgeMinutes() {
|
||||
return maxAgeMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum age a connection can be in minutes.
|
||||
*/
|
||||
public void setMaxAgeMinutes(int maxAgeMinutes) {
|
||||
this.maxAgeMinutes = maxAgeMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in seconds a connection can be idle after which it can be
|
||||
* trimmed from the pool.
|
||||
@@ -321,6 +358,25 @@ public class DataSourceConfig {
|
||||
this.maxInactiveTimeSecs = maxInactiveTimeSecs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the minimum time gap between pool trim checks.
|
||||
* <p>
|
||||
* This defaults to 59 seconds meaning that the pool trim check will run every
|
||||
* minute assuming the heart beat check runs every 30 seconds.
|
||||
* </p>
|
||||
*/
|
||||
public int getTrimPoolFreqSecs() {
|
||||
return trimPoolFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum trim gap between pool trim checks.
|
||||
*/
|
||||
public void setTrimPoolFreqSecs(int trimPoolFreqSecs) {
|
||||
this.trimPoolFreqSecs = trimPoolFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pool listener.
|
||||
*/
|
||||
@@ -359,7 +415,7 @@ public class DataSourceConfig {
|
||||
public void setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a map of custom properties for the jdbc driver connection.
|
||||
*/
|
||||
@@ -388,18 +444,18 @@ public class DataSourceConfig {
|
||||
this.username = properties.get(prefix + "username", null);
|
||||
this.password = properties.get(prefix + "password", null);
|
||||
|
||||
String v;
|
||||
String dbDriver = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", dbDriver);
|
||||
|
||||
v = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", v);
|
||||
|
||||
v = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", v);
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
|
||||
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
|
||||
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
|
||||
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 900);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
|
||||
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
|
||||
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
|
||||
|
||||
this.minConnections = properties.getInt(prefix + "minConnections", 0);
|
||||
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
|
||||
@@ -409,6 +465,7 @@ public class DataSourceConfig {
|
||||
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
|
||||
|
||||
this.heartbeatSql = properties.get(prefix + "heartbeatSql", null);
|
||||
this.heartbeatTimeoutSeconds = properties.getInt(prefix + "heartbeatTimeoutSeconds", 3);
|
||||
this.poolListener = properties.get(prefix + "poolListener", null);
|
||||
this.offline = properties.getBoolean(prefix + "offline", false);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.BulkTableEventListener;
|
||||
import com.avaje.ebean.event.ServerConfigStartup;
|
||||
import com.avaje.ebean.event.TransactionEventListener;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
|
||||
/**
|
||||
@@ -191,6 +192,10 @@ public class ServerConfig {
|
||||
|
||||
private ServerCacheManager serverCacheManager;
|
||||
|
||||
private boolean collectQueryStatsByNode;
|
||||
|
||||
private boolean collectQueryOrigins;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an
|
||||
* EbeanServer.
|
||||
@@ -926,6 +931,51 @@ public class ServerConfig {
|
||||
public void setUpdateChangesOnly(boolean updateChangesOnly) {
|
||||
this.updateChangesOnly = updateChangesOnly;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the ebeanServer should collection query statistics by ObjectGraphNode.
|
||||
*/
|
||||
public boolean isCollectQueryStatsByNode() {
|
||||
return collectQueryStatsByNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to collection query execution statistics by ObjectGraphNode.
|
||||
* <p>
|
||||
* These statistics can be used to highlight code/query 'origin points' that result in lots of lazy loading.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is considered safe/fine to have this set to true for production.
|
||||
* </p>
|
||||
* <p>
|
||||
* This information can be later retrieved via {@link MetaInfoManager}.
|
||||
* </p>
|
||||
* @see MetaInfoManager
|
||||
*/
|
||||
public void setCollectQueryStatsByNode(boolean collectQueryStatsByNode) {
|
||||
this.collectQueryStatsByNode = collectQueryStatsByNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if query plans should also collect their 'origins'. This means for a given query plan you
|
||||
* can identify the code/origin points where this query resulted from including lazy loading origins.
|
||||
*/
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if query plans should collect their 'origin' points. This means for a given query plan you
|
||||
* can identify the code/origin points where this query resulted from including lazy loading origins.
|
||||
* <p>
|
||||
* This information can be later retrieved via {@link MetaInfoManager}.
|
||||
* </p>
|
||||
* @see MetaInfoManager
|
||||
*/
|
||||
public void setCollectQueryOrigins(boolean collectQueryOrigins) {
|
||||
this.collectQueryOrigins = collectQueryOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource directory.
|
||||
@@ -1183,6 +1233,9 @@ public class ServerConfig {
|
||||
packages = getSearchJarsPackages(packagesProp);
|
||||
}
|
||||
|
||||
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
|
||||
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
|
||||
|
||||
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
|
||||
|
||||
boolean batchMode = p.getBoolean("batch.mode", false);
|
||||
|
||||
@@ -17,29 +17,16 @@ public class DB2Platform extends DatabasePlatform {
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead for H2
|
||||
this.sqlLimiter = new Db2SqlLimiter();
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("smallint default 0"));
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer"));
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("bigint"));
|
||||
dbTypeMap.put(Types.REAL, new DbType("float"));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.SMALLINT, new DbType("smallint"));
|
||||
booleanDbType = Types.BOOLEAN;
|
||||
dbTypeMap.put(Types.REAL, new DbType("real"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 15));
|
||||
|
||||
this.dbDdlSyntax.setIdentity("generated by default as identity");
|
||||
|
||||
// this.dbDdlSyntax.setDropIfExists("if exists");
|
||||
// this.dbDdlSyntax.setDisableReferentialIntegrity("SET REFERENTIAL_INTEGRITY FALSE");
|
||||
// this.dbDdlSyntax.setEnableReferentialIntegrity("SET REFERENTIAL_INTEGRITY TRUE");
|
||||
// this.dbDdlSyntax.setForeignKeySuffix("on delete restrict on update restrict");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,12 @@ public class DatabasePlatform {
|
||||
|
||||
protected boolean selectCountWithAlias;
|
||||
|
||||
/**
|
||||
* If set then use the FORWARD ONLY hint when creating ResultSets for
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
@@ -199,6 +205,24 @@ public class DatabasePlatform {
|
||||
return idInExpandedForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ResultSet TYPE_FORWARD_ONLY Hint should be used on
|
||||
* findIterate() and findVisit() PreparedStatements.
|
||||
* <p>
|
||||
* This specifically is required for MySql when processing large results.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isForwardOnlyHintOnFindIterate() {
|
||||
return forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the ResultSet TYPE_FORWARD_ONLY Hint should be used by default on findIterate PreparedStatements.
|
||||
*/
|
||||
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
|
||||
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB identity/sequence features for this platform.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
public class Db2SqlLimiter implements SqlLimiter {
|
||||
|
||||
@Override
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
sb.append("select ");
|
||||
sb.append(request.getDbSql());
|
||||
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
sb.append(" ").append(NEW_LINE).append("FETCH FIRST ").append(maxRows).append(" ROWS ONLY");
|
||||
}
|
||||
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
return new SqlLimitResponse(sql, false);
|
||||
}
|
||||
}
|
||||
@@ -31,13 +31,12 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
|
||||
maxRows = maxRows + 1;
|
||||
}
|
||||
|
||||
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ");
|
||||
if (maxRows > 0) {
|
||||
sb.append(maxRows);
|
||||
}
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
if (maxRows > 0 || firstRow > 0) {
|
||||
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ").append(maxRows);
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
}
|
||||
}
|
||||
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
|
||||
@@ -31,6 +31,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
dbTypeMap.put(Types.BIT, new DbType("tinyint(1) default 0"));
|
||||
|
||||
@@ -101,13 +101,13 @@ public class MetaAutoFetchStatistic implements Serializable {
|
||||
|
||||
private final String path;
|
||||
|
||||
private final int exeCount;
|
||||
private final long exeCount;
|
||||
|
||||
private final int totalBeanLoaded;
|
||||
private final long totalBeanLoaded;
|
||||
|
||||
private final int totalMicros;
|
||||
private final long totalMicros;
|
||||
|
||||
public QueryStats(String path, int exeCount, int totalBeanLoaded, int totalMicros) {
|
||||
public QueryStats(String path, long exeCount, long totalBeanLoaded, long totalMicros) {
|
||||
this.path = path;
|
||||
this.exeCount = exeCount;
|
||||
this.totalBeanLoaded = totalBeanLoaded;
|
||||
@@ -125,21 +125,21 @@ public class MetaAutoFetchStatistic implements Serializable {
|
||||
/**
|
||||
* The number of queries executed.
|
||||
*/
|
||||
public int getExeCount() {
|
||||
public long getExeCount() {
|
||||
return exeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of beans loaded by the query.
|
||||
*/
|
||||
public int getTotalBeanLoaded() {
|
||||
public long getTotalBeanLoaded() {
|
||||
return totalBeanLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total time in microseconds of the queries.
|
||||
*/
|
||||
public int getTotalMicros() {
|
||||
public long getTotalMicros() {
|
||||
return totalMicros;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MetaBeanInfo {
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return the non-empty statistics.
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return all the statistics (include query plans that haven't had query executions).
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the meta data in EbeanServer such as query execution statistics.
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Return the MetaBeanInfo for a bean type.
|
||||
*/
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass);
|
||||
|
||||
/**
|
||||
* Return all the MetaBeanInfo.
|
||||
*/
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList();
|
||||
|
||||
/**
|
||||
* Collect and return the query plan statistics for all the beans.
|
||||
* <p>
|
||||
* Note that this excludes the query plan statistics where there has been no
|
||||
* executions (since the last collection with reset).
|
||||
* </p>
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect and return the ObjectGraphNode statistics.
|
||||
* <p>
|
||||
* These show query executions based on an origin point and relative path.
|
||||
* This is used to look at the amount of lazy loading occurring for a given
|
||||
* query origin point and highlight potential for tuning a query.
|
||||
* </p>
|
||||
*
|
||||
* @param reset
|
||||
* Set to true to reset the underlying statistics after collection.
|
||||
*/
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
|
||||
/**
|
||||
* Statistics for query execution based on object graph origin and paths.
|
||||
* <p>
|
||||
* These statistics can be used to identify origin queries that result in lots
|
||||
* of lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaInfoManager#collectNodeStatistics(boolean)
|
||||
*/
|
||||
public interface MetaObjectGraphNodeStats {
|
||||
|
||||
/**
|
||||
* Return the ObjectGraphNode which has the origin point and relative path.
|
||||
*/
|
||||
public ObjectGraphNode getNode();
|
||||
|
||||
/**
|
||||
* Return the startTime of statistics collection.
|
||||
*/
|
||||
public long getStartTime();
|
||||
|
||||
/**
|
||||
* Return the total count of queries executed for this node.
|
||||
*/
|
||||
public long getCount();
|
||||
|
||||
/**
|
||||
* Return the total time of queries executed for this node.
|
||||
*/
|
||||
public long getTotalTime();
|
||||
|
||||
/**
|
||||
* Return the total beans loaded by queries for this node.
|
||||
*/
|
||||
public long getTotalBeans();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
|
||||
/**
|
||||
* Holds a query 'origin' point and count for the number of queries executed for
|
||||
* this 'origin'.
|
||||
* <p>
|
||||
* This basically points to the bit of original code and query that results in
|
||||
* this query directly or via lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaQueryPlanStatistic
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanOriginCount {
|
||||
|
||||
/**
|
||||
* The 'origin' and path which this query belongs to.
|
||||
* <p>
|
||||
* For lazy loading queries this points to the original query and associated
|
||||
* navigation path that resulted in this query being executed.
|
||||
* </p>
|
||||
*/
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
/**
|
||||
* The number of times a query was fired for this node since the counter was
|
||||
* last reset.
|
||||
*/
|
||||
public long getCount();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanStatistic {
|
||||
|
||||
/**
|
||||
* Return the bean type this query plan is for.
|
||||
*/
|
||||
public Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by Autofetch.
|
||||
*/
|
||||
public boolean isAutofetchTuned();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
*/
|
||||
public String getQueryPlanHash();
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
public String getSql();
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
public long getExecutionCount();
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getTotalLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the total time taken by executions of this query.
|
||||
*/
|
||||
public long getTotalTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the max execution time for this query.
|
||||
*/
|
||||
public long getMaxTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the time collection started (or was last reset).
|
||||
*/
|
||||
public long getCollectionStart();
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
public long getLastQueryTime();
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getAvgTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getAvgLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the 'origin' points and paths that resulted in the query being
|
||||
* executed and the associated number of times the query was executed via that
|
||||
* path.
|
||||
* <p>
|
||||
* This includes direct and lazy loading paths.
|
||||
* </p>
|
||||
*/
|
||||
public List<MetaQueryPlanOriginCount> getOrigins();
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*/
|
||||
@Entity
|
||||
public class MetaQueryStatistic implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8746524372894472583L;
|
||||
|
||||
boolean autofetchTuned;
|
||||
|
||||
String beanType;
|
||||
|
||||
/**
|
||||
* The original query plan hash (calculated prior to autofetch tuning).
|
||||
*/
|
||||
int origQueryPlanHash;
|
||||
|
||||
/**
|
||||
* The final query plan hash (calculated after to autofetch tuning).
|
||||
*/
|
||||
int finalQueryPlanHash;
|
||||
|
||||
String sql;
|
||||
|
||||
int executionCount;
|
||||
|
||||
int totalLoadedBeans;
|
||||
|
||||
int totalTimeMicros;
|
||||
|
||||
long collectionStart;
|
||||
|
||||
long lastQueryTime;
|
||||
|
||||
int avgTimeMicros;
|
||||
|
||||
int avgLoadedBeans;
|
||||
|
||||
public MetaQueryStatistic() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MetaQueryStatistic.
|
||||
*/
|
||||
public MetaQueryStatistic(boolean autofetchTuned, String beanType, int plan, String sql,
|
||||
int executionCount, int totalLoadedBeans, int totalTimeMicros, long collectionStart,
|
||||
long lastQueryTime) {
|
||||
|
||||
this.autofetchTuned = autofetchTuned;
|
||||
this.beanType = beanType;
|
||||
this.finalQueryPlanHash = plan;
|
||||
this.sql = sql;
|
||||
this.executionCount = executionCount;
|
||||
this.totalLoadedBeans = totalLoadedBeans;
|
||||
this.totalTimeMicros = totalTimeMicros;
|
||||
this.collectionStart = collectionStart;
|
||||
|
||||
this.lastQueryTime = lastQueryTime;
|
||||
this.avgTimeMicros = executionCount == 0 ? 0 : totalTimeMicros / executionCount;
|
||||
this.avgLoadedBeans = executionCount == 0 ? 0 : totalLoadedBeans / executionCount;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "type=" + beanType + " tuned:" + autofetchTuned + " origHash=" + origQueryPlanHash
|
||||
+ " count=" + executionCount + " avgMicros=" + getAvgTimeMicros();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this query plan was built for Autofetch tuned queries.
|
||||
*/
|
||||
public boolean isAutofetchTuned() {
|
||||
return autofetchTuned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original query plan hash (calculated prior to autofetch tuning).
|
||||
* <p>
|
||||
* This will return 0 if there is no autofetch profiling or tuning on this
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public int getOrigQueryPlanHash() {
|
||||
return origQueryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the queryPlanHash value. This is unique for a given query plan.
|
||||
*/
|
||||
public int getFinalQueryPlanHash() {
|
||||
return finalQueryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean type.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
public int getExecutionCount() {
|
||||
return executionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalLoadedBeans() {
|
||||
return totalLoadedBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times this query was executed.
|
||||
*/
|
||||
public int getTotalTimeMicros() {
|
||||
return totalTimeMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time collection started.
|
||||
*/
|
||||
public long getCollectionStart() {
|
||||
return collectionStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
public long getLastQueryTime() {
|
||||
return lastQueryTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getAvgTimeMicros() {
|
||||
return avgTimeMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getAvgLoadedBeans() {
|
||||
return avgLoadedBeans;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Meta data that can be retrieved for the EbeanServer.
|
||||
*/
|
||||
package com.avaje.ebean.meta;
|
||||
@@ -1,17 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Entity Beans for getting "Meta" data from Ebean</title>
|
||||
</head>
|
||||
<body>
|
||||
Entity Beans for getting "Meta" data from Ebean
|
||||
<p>
|
||||
You can query these entity beans to get "meta" data from Ebean.
|
||||
This includes things like query execution statistics.
|
||||
</p>
|
||||
<pre class="code">
|
||||
// fetch the meta data that controls autoFetch query tuning
|
||||
Query<MetaAutoFetchTunedFetch> query = Ebean.createQuery(MetaAutoFetchTunedFetch.class);
|
||||
List<MetaAutoFetchTunedFetch> list = query.findList();
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,7 @@ package com.avaje.ebeaninternal.api;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -14,22 +13,16 @@ import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
/**
|
||||
* Parameters used for binding to a statement.
|
||||
* <p>
|
||||
* Used by FindByNativeSql and UpdateSql to support ordered and named
|
||||
* parameters. Note that you can use either ordered OR named parameters.
|
||||
* Supports ordered or named parameters.
|
||||
* </p>
|
||||
*/
|
||||
public class BindParams implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4541081933302086285L;
|
||||
|
||||
private ArrayList<Param> positionedParameters = new ArrayList<Param>();
|
||||
private List<Param> positionedParameters = new ArrayList<Param>();
|
||||
|
||||
private HashMap<String, Param> namedParameters = new HashMap<String, Param>();
|
||||
|
||||
/**
|
||||
* Need to create a hash when binding collection values (for in clauses).
|
||||
*/
|
||||
private int queryPlanHash = 1;
|
||||
private Map<String, Param> namedParameters = new LinkedHashMap<String, Param>();
|
||||
|
||||
/**
|
||||
* This is the sql. For named parameters this is the sql after the named
|
||||
@@ -38,6 +31,40 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
private String preparedSql;
|
||||
|
||||
public BindParams() {
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).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 hc = 31;
|
||||
for (Param param : positionedParameters) {
|
||||
hc = hc * 31 + param.queryBindCount();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
hc = hc * 31 + entry.getKey().hashCode();
|
||||
hc = hc * 31 + entry.getValue().queryBindCount();
|
||||
}
|
||||
|
||||
int bindCount = positionedParameters.size() + namedParameters.size();
|
||||
builder.add(hc).bind(bindCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a deep copy of the BindParams.
|
||||
*/
|
||||
@@ -46,45 +73,12 @@ public class BindParams implements Serializable {
|
||||
for (Param p : positionedParameters) {
|
||||
copy.positionedParameters.add(p.copy());
|
||||
}
|
||||
Iterator<Entry<String, Param>> it = namedParameters.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, Param> entry = (Map.Entry<String, Param>) it.next();
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
for (Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = getClass().hashCode();
|
||||
hc = hc * 31 + namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
hc = hc * 31 + (preparedSql == null ? 0 : preparedSql.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof BindParams) {
|
||||
return hashCode() == o.hashCode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no bind parameters.
|
||||
*/
|
||||
@@ -131,10 +125,8 @@ public class BindParams implements Serializable {
|
||||
* Set an In Out parameter using position.
|
||||
*/
|
||||
public void setParameter(int position, Object value, int outType) {
|
||||
|
||||
addToQueryPlanHash(String.valueOf(position), value);
|
||||
|
||||
Param p = getParam(position);
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
@@ -144,10 +136,8 @@ public class BindParams implements Serializable {
|
||||
* must use setNullParameter.
|
||||
*/
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
addToQueryPlanHash(String.valueOf(position), value);
|
||||
|
||||
Param p = getParam(position);
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
}
|
||||
|
||||
@@ -182,10 +172,8 @@ public class BindParams implements Serializable {
|
||||
* Set a named In Out parameter.
|
||||
*/
|
||||
public void setParameter(String name, Object value, int outType) {
|
||||
|
||||
addToQueryPlanHash(name, value);
|
||||
|
||||
Param p = getParam(name);
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
@@ -203,48 +191,22 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
public Param setParameter(String name, Object value) {
|
||||
|
||||
addToQueryPlanHash(name, value);
|
||||
|
||||
Param p = getParam(name);
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* For binding collections calculate a hash to be used for the query plan.
|
||||
*/
|
||||
private void addToQueryPlanHash(String name, Object value){
|
||||
if (value != null){
|
||||
if (value instanceof Collection<?>){
|
||||
queryPlanHash = queryPlanHash * 31 + name.hashCode();
|
||||
queryPlanHash = queryPlanHash * 31 + ((Collection<?>)value).size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 int getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
@@ -300,9 +262,9 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
public static final class OrderedList {
|
||||
|
||||
final List<Param> paramList;
|
||||
private final List<Param> paramList;
|
||||
|
||||
final StringBuilder preparedSql;
|
||||
private final StringBuilder preparedSql;
|
||||
|
||||
public OrderedList() {
|
||||
this(new ArrayList<Param>());
|
||||
@@ -373,6 +335,16 @@ public class BindParams implements Serializable {
|
||||
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.
|
||||
*/
|
||||
@@ -448,14 +420,14 @@ public class BindParams implements Serializable {
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
@@ -506,12 +478,12 @@ public class BindParams implements Serializable {
|
||||
this.textLocation = textLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash key for a query including both the query plan and bind values.
|
||||
*/
|
||||
public class HashQuery {
|
||||
|
||||
private final HashQueryPlan planHash;
|
||||
|
||||
private final int bindHash;
|
||||
|
||||
/**
|
||||
* Create the HashQuery.
|
||||
*/
|
||||
public HashQuery(HashQueryPlan planHash, int bindHash) {
|
||||
this.planHash = planHash;
|
||||
this.bindHash = bindHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan hash.
|
||||
*/
|
||||
public HashQueryPlan getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind values hash.
|
||||
*/
|
||||
public int getBindHash() {
|
||||
return bindHash;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * planHash.hashCode();
|
||||
hc = 31 * 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,46 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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" : "");
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = planHash;
|
||||
hc = hc * 31 + bindCount;
|
||||
hc = hc * 31 + Objects.hashCode(rawSql);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQueryPlan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQueryPlan e = (HashQueryPlan) obj;
|
||||
return e.planHash == planHash
|
||||
&& e.bindCount == bindCount
|
||||
&& Objects.equals(e.rawSql, rawSql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
public class HashQueryPlanBuilder {
|
||||
|
||||
private int planHash;
|
||||
|
||||
private int bindCount;
|
||||
|
||||
private String rawSql;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 31;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 31 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 31 + Objects.hashCode(object);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 31 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 31 + (booleanValue ? 31 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a number to the bind count for the hash.
|
||||
*/
|
||||
public HashQueryPlanBuilder bind(int extraBindCount) {
|
||||
bindCount += extraBindCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add raw sql to the hash.
|
||||
*/
|
||||
public HashQueryPlanBuilder addRawSql(String rawSql) {
|
||||
this.rawSql = rawSql;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public HashQueryPlan build() {
|
||||
return new HashQueryPlan(rawSql, planHash, bindCount);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* A buffer of beans for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadBeanBuffer {
|
||||
|
||||
public List<EntityBeanIntercept> getBatch();
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
public String getFullPath();
|
||||
|
||||
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
}
|
||||
@@ -1,39 +1,10 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Controls the loading of ManyToOne and OneToOne relationships.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LoadBeanContext extends LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Configure the query to load beans for this node/path.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
/**
|
||||
* Return the full path of this node from the root object.
|
||||
*/
|
||||
public String getFullPath();
|
||||
|
||||
/**
|
||||
* Return the persistence context used for all queries
|
||||
* related to this object graph.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for beans for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the batchSize used for lazy loading beans.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
}
|
||||
|
||||
@@ -9,55 +9,52 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
* Request for loading ManyToOne and OneToOne relationships.
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanContext loadContext;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanContext loadContext, List<EntityBeanIntercept> batch,
|
||||
Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
|
||||
super(transaction, batchSize, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = batch;
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
public String getDescription() {
|
||||
String fullPath = loadContext.getFullPath();
|
||||
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
|
||||
+ batch.size();
|
||||
return s;
|
||||
}
|
||||
private final LoadBeanBuffer LoadBuffer;
|
||||
|
||||
/**
|
||||
* Return the batch of beans to actually load.
|
||||
*/
|
||||
public List<EntityBeanIntercept> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanContext getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
|
||||
boolean loadCache) {
|
||||
|
||||
super(transaction, lazy);
|
||||
this.LoadBuffer = LoadBuffer;
|
||||
this.batch = LoadBuffer.getBatch();
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
/**
|
||||
* A buffer of bean collections for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadManyBuffer {
|
||||
|
||||
public List<BeanCollection<?>> getBatch();
|
||||
|
||||
public BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
public String getFullPath();
|
||||
|
||||
public void configureQuery(SpiQuery<?> query);
|
||||
|
||||
}
|
||||
@@ -1,54 +1,9 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
/**
|
||||
* Controls the loading of OneToMany and ManyToMany relationships.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LoadManyContext extends LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Configure the query to load beans for this node/path.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Return the full path of this node from the root object.
|
||||
*/
|
||||
public String getFullPath();
|
||||
|
||||
/**
|
||||
* Return the node location for this node/path.
|
||||
*/
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
|
||||
/**
|
||||
* Return the persistence context used for all queries
|
||||
* related to this object graph.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Return the batchSize used for lazy loading beans.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for beans for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the associated Many bean property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,28 +13,24 @@ public class LoadManyRequest extends LoadRequest {
|
||||
|
||||
private final List<BeanCollection<?>> batch;
|
||||
|
||||
private final LoadManyContext loadContext;
|
||||
private final LoadManyBuffer loadContext;
|
||||
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadManyRequest(LoadManyContext loadContext,
|
||||
List<BeanCollection<?>> batch, Transaction transaction,
|
||||
int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) {
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
|
||||
boolean onlyIds, boolean loadCache) {
|
||||
|
||||
super(transaction, batchSize, lazy);
|
||||
super(transaction, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = batch;
|
||||
this.batch = loadContext.getBatch();
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
String fullPath = loadContext.getFullPath();
|
||||
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
|
||||
+ batch.size();
|
||||
return s;
|
||||
return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +43,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyContext getLoadContext() {
|
||||
public LoadManyBuffer getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,14 @@ public abstract class LoadRequest {
|
||||
|
||||
protected final boolean lazy;
|
||||
|
||||
protected final int batchSize;
|
||||
|
||||
protected final Transaction transaction;
|
||||
|
||||
public LoadRequest(Transaction transaction, int batchSize, boolean lazy) {
|
||||
public LoadRequest(Transaction transaction, boolean lazy) {
|
||||
|
||||
this.transaction = transaction;
|
||||
this.batchSize = batchSize;
|
||||
this.lazy = lazy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if this is a lazy load and false if it is a secondary query.
|
||||
*/
|
||||
@@ -28,13 +24,6 @@ public abstract class LoadRequest {
|
||||
return lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the requested batch size.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction to use if this is a secondary query.
|
||||
* <p>
|
||||
|
||||
@@ -19,6 +19,10 @@ public class ManyWhereJoins implements Serializable {
|
||||
|
||||
private final TreeSet<String> joins = new TreeSet<String>();
|
||||
|
||||
private StringBuilder formulaProperties = new StringBuilder();
|
||||
|
||||
private boolean formulaWithJoin;
|
||||
|
||||
/**
|
||||
* Add a many where join.
|
||||
*/
|
||||
@@ -73,4 +77,36 @@ public class ManyWhereJoins implements Serializable {
|
||||
return joins;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public boolean isHasMany() {
|
||||
return formulaWithJoin || !joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the findRowCount query just needs the id property in the select clause.
|
||||
*/
|
||||
public boolean isSelectId() {
|
||||
return !formulaWithJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formula properties to build the select clause for a findRowCount query.
|
||||
*/
|
||||
public String getFormulaProperties() {
|
||||
return formulaProperties.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.avaje.ebean.TxScope;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanLoader;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
@@ -29,6 +30,8 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
public void shutdownManaged();
|
||||
|
||||
public boolean isCollectQueryOrigins();
|
||||
|
||||
/**
|
||||
* Return true if DeleteMissingChildren defaults to true for stateless
|
||||
* updates.
|
||||
@@ -187,4 +190,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
public boolean isSupportedType(java.lang.reflect.Type genericType);
|
||||
|
||||
/**
|
||||
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
|
||||
*/
|
||||
public void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface SpiExpression extends Expression {
|
||||
* from an AutoFetch perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutoFetchHash();
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Calculate a hash value for the expression.
|
||||
@@ -37,7 +37,7 @@ public interface SpiExpression extends Expression {
|
||||
* case the query execution plan can be reused.
|
||||
* </p>
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Return the hash value for the values that will be bound.
|
||||
|
||||
@@ -8,6 +8,6 @@ public interface SpiExpressionFactory extends ExpressionFactory {
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
public ExpressionFactory createExpressionFactory(FilterExprPath prefix);
|
||||
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,24 +18,23 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
public List<SpiExpression> getUnderlyingList();
|
||||
|
||||
/**
|
||||
* Trim the path for filterMany() expressions.
|
||||
*/
|
||||
public void trimPath(int prefixTrim);
|
||||
/**
|
||||
* Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
|
||||
*/
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim);
|
||||
|
||||
/**
|
||||
* Restore the ExpressionFactory after deserialisation.
|
||||
*/
|
||||
public void setExpressionFactory(ExpressionFactory expr);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent
|
||||
* join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
|
||||
/**
|
||||
* Return true if this list is empty.
|
||||
@@ -62,10 +61,10 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
public ArrayList<Object> buildBindValues(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
@@ -153,11 +154,23 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
public String getLoadMode();
|
||||
|
||||
/**
|
||||
* This becomes a lazy loading query for a many relationship.
|
||||
*/
|
||||
public void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many);
|
||||
|
||||
/**
|
||||
* Return the lazy loading 'many' property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
|
||||
|
||||
/**
|
||||
* Return the list of parent Id's for lazy loading.
|
||||
*/
|
||||
public List<Object> getLazyLoadForParentIds();
|
||||
|
||||
/**
|
||||
* Set the load mode (+lazy or +query) and the load description.
|
||||
*
|
||||
* @param loadMode
|
||||
* @param loadDescription
|
||||
*/
|
||||
public void setLoadDescription(String loadMode, String loadDescription);
|
||||
|
||||
@@ -341,7 +354,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* tuning/modifying the query.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutofetchHash();
|
||||
public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Identifies queries that are the same bar the bind variables.
|
||||
@@ -354,7 +367,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* Excludes the actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
public HashQueryPlan queryPlanHash(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
@@ -368,7 +381,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Identifies queries that are exactly the same including bind variables.
|
||||
*/
|
||||
public int queryHash();
|
||||
public HashQuery queryHash();
|
||||
|
||||
/**
|
||||
* Return true if this is a query based on a SqlSelect rather than
|
||||
|
||||
@@ -224,7 +224,7 @@ public interface AutoFetchManager extends NodeUsageListener {
|
||||
* @param micros
|
||||
* the query executing time in microseconds
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,6 +61,7 @@ public class AutoFetchManagerFactory {
|
||||
FileInputStream fi = new FileInputStream(autoFetchFile);
|
||||
ObjectInputStream ois = new ObjectInputStream(fi);
|
||||
AutoFetchManager profListener = (AutoFetchManager) ois.readObject();
|
||||
ois.close();
|
||||
|
||||
logger.info("AutoFetch deserialized from file ["+autoFetchFile.getAbsolutePath()+"]");
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
|
||||
* query in which case the parentNode will be null, or a lazy loading query
|
||||
* resulting from traversal of the object graph.
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros) {
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
|
||||
|
||||
if (node != null){
|
||||
ObjectGraphOrigin origin = node.getOriginQueryPoint();
|
||||
|
||||
@@ -127,7 +127,7 @@ public class Statistics implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beansLoaded, int micros) {
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
|
||||
|
||||
synchronized (monitor) {
|
||||
String key = node.getPath();
|
||||
|
||||
@@ -13,11 +13,11 @@ public class StatisticsQuery implements Serializable {
|
||||
|
||||
private final String path;
|
||||
|
||||
private int exeCount;
|
||||
private long exeCount;
|
||||
|
||||
private int totalBeanLoaded;
|
||||
private long totalBeanLoaded;
|
||||
|
||||
private int totalMicros;
|
||||
private long totalMicros;
|
||||
|
||||
public StatisticsQuery(String path){
|
||||
this.path = path;
|
||||
@@ -27,7 +27,7 @@ public class StatisticsQuery implements Serializable {
|
||||
return new QueryStats(path, exeCount, totalBeanLoaded, totalMicros);
|
||||
}
|
||||
|
||||
public void add(int beansLoaded, int micros) {
|
||||
public void add(long beansLoaded, long micros) {
|
||||
exeCount++;
|
||||
totalBeanLoaded += beansLoaded;
|
||||
totalMicros += micros;
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.Statistics;
|
||||
|
||||
/**
|
||||
* Bean Finder for MetaAutoFetchStatistic.
|
||||
* <p>
|
||||
* This gets the meta data from the AutoFetchManager and creates a copy of that
|
||||
* data to give back to the caller in the form of MetaAutoFetchStatistic beans.
|
||||
* </p>
|
||||
*/
|
||||
public class BFAutoFetchStatisticFinder implements BeanFinder<MetaAutoFetchStatistic> {
|
||||
|
||||
|
||||
public MetaAutoFetchStatistic find(BeanQueryRequest<MetaAutoFetchStatistic> request) {
|
||||
SpiQuery<MetaAutoFetchStatistic> query = (SpiQuery<MetaAutoFetchStatistic>)request.getQuery();
|
||||
try {
|
||||
String queryPointKey = (String) query.getId();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
Statistics stats = manager.getStatistics(queryPointKey);
|
||||
if (stats != null) {
|
||||
return stats.createPublicMeta();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaAutoFetchStatistic> findMany(BeanQueryRequest<MetaAutoFetchStatistic> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)) {
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
BeanList<MetaAutoFetchStatistic> list = new BeanList<MetaAutoFetchStatistic>();
|
||||
|
||||
Iterator<Statistics> it = manager.iterateStatistics();
|
||||
while (it.hasNext()) {
|
||||
Statistics stats = it.next();
|
||||
// create a copy for public use
|
||||
list.add(stats.createPublicMeta());
|
||||
}
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchTunedQueryInfo;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo;
|
||||
|
||||
/**
|
||||
* BeanFinder for MetaAutoFetchTunedFetch.
|
||||
*/
|
||||
public class BFAutoFetchTunedFetchFinder implements BeanFinder<MetaAutoFetchTunedQueryInfo> {
|
||||
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo find(BeanQueryRequest<MetaAutoFetchTunedQueryInfo> request) {
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>)request.getQuery();
|
||||
try {
|
||||
String queryPointKey = (String)query.getId();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
TunedQueryInfo tunedFetch = manager.getTunedQueryInfo(queryPointKey);
|
||||
if (tunedFetch != null){
|
||||
return tunedFetch.createPublicMeta();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e){
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaAutoFetchTunedQueryInfo> findMany(BeanQueryRequest<MetaAutoFetchTunedQueryInfo> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)){
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
BeanList<MetaAutoFetchTunedQueryInfo> list = new BeanList<MetaAutoFetchTunedQueryInfo>();
|
||||
|
||||
Iterator<TunedQueryInfo> it = manager.iterateTunedQueryInfo();
|
||||
while (it.hasNext()) {
|
||||
TunedQueryInfo tunedFetch = it.next();
|
||||
// create a copy for public use
|
||||
list.add(tunedFetch.createPublicMeta());
|
||||
}
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType, origQueryPlanHash";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaQueryStatistic;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
|
||||
/**
|
||||
* BeanFinder for MetaQueryStatistic.
|
||||
*/
|
||||
public class BFQueryStatisticFinder implements BeanFinder<MetaQueryStatistic> {
|
||||
|
||||
|
||||
public MetaQueryStatistic find(BeanQueryRequest<MetaQueryStatistic> request) {
|
||||
throw new RuntimeException("Not Supported yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaQueryStatistic> findMany(BeanQueryRequest<MetaQueryStatistic> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)){
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
BeanList<MetaQueryStatistic> list = new BeanList<MetaQueryStatistic>();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
build(list, server);
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType, origQueryPlanHash, autofetchTuned";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void build(List<MetaQueryStatistic> list, SpiEbeanServer server) {
|
||||
|
||||
for (BeanDescriptor<?> desc : server.getBeanDescriptors()) {
|
||||
desc.clearQueryStatistics();
|
||||
build(list, desc);
|
||||
}
|
||||
}
|
||||
|
||||
private void build(List<MetaQueryStatistic> list, BeanDescriptor<?> desc) {
|
||||
|
||||
Iterator<CQueryPlan> it = desc.queryPlans();
|
||||
while (it.hasNext()) {
|
||||
CQueryPlan queryPlan = (CQueryPlan) it.next();
|
||||
list.add(queryPlan.createMetaQueryStatistic(desc.getFullName()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>BeanFinders, BeanControllers etc for "meta" beans</title>
|
||||
</head>
|
||||
<body>
|
||||
BeanFinders, BeanControllers etc for "meta" beans
|
||||
</body>
|
||||
</html>
|
||||
@@ -96,6 +96,10 @@ public abstract class BeanRequest {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
public SpiEbeanServer getServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Transaction associated with this request.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.avaje.ebean.meta.*;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebeaninternal.server.util.LongAdder;
|
||||
|
||||
/**
|
||||
* Helper to collect the query execution statistics for a given node.
|
||||
*/
|
||||
public class CObjectGraphNodeStatistics {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
|
||||
private final LongAdder count = new LongAdder();
|
||||
|
||||
private final LongAdder totalTime = new LongAdder();
|
||||
|
||||
private final LongAdder totalBeans = new LongAdder();
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
public CObjectGraphNodeStatistics(ObjectGraphNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public void add(long beanCount, long exeMicros) {
|
||||
count.increment();
|
||||
totalTime.add(exeMicros);
|
||||
totalBeans.add(beanCount);
|
||||
}
|
||||
|
||||
public MetaObjectGraphNodeStats get(boolean reset) {
|
||||
if (reset) {
|
||||
return new Snapshot(node, startTime.getAndSet(System.currentTimeMillis()), count.sumThenReset(),
|
||||
totalTime.sumThenReset(), totalBeans.sumThenReset());
|
||||
} else {
|
||||
return new Snapshot(node, startTime.get(), count.sum(), totalTime.sum(), totalBeans.sum());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Snapshot implements MetaObjectGraphNodeStats {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
private final long startTime;
|
||||
private final long count;
|
||||
private final long totalTime;
|
||||
private final long totalBeans;
|
||||
|
||||
public Snapshot(ObjectGraphNode node, long startTime, long count, long totalTime, long totalBeans) {
|
||||
this.node = node;
|
||||
this.startTime = startTime;
|
||||
this.count = count;
|
||||
this.totalTime = totalTime;
|
||||
this.totalBeans = totalBeans;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return node + " count[" + count + "] time[" + totalTime + "] beans[" + totalBeans + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTime() {
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalBeans() {
|
||||
return totalBeans;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,17 +8,7 @@ import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.H2Platform;
|
||||
import com.avaje.ebean.config.dbplatform.HsqldbPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.MsSqlServer2000Platform;
|
||||
import com.avaje.ebean.config.dbplatform.MsSqlServer2005Platform;
|
||||
import com.avaje.ebean.config.dbplatform.MySqlPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.Oracle10Platform;
|
||||
import com.avaje.ebean.config.dbplatform.Oracle9Platform;
|
||||
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.SqlAnywherePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -84,7 +74,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("sqlanywhere")) {
|
||||
return new SqlAnywherePlatform();
|
||||
}
|
||||
|
||||
if (dbName.equals("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
if (dbName.equals("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
@@ -132,41 +124,43 @@ public class DatabasePlatformFactory {
|
||||
|
||||
int majorVersion = metaData.getDatabaseMajorVersion();
|
||||
|
||||
if (dbProductName.indexOf("oracle") > -1) {
|
||||
if (dbProductName.contains("oracle")) {
|
||||
if (majorVersion > 9) {
|
||||
return new Oracle10Platform();
|
||||
} else {
|
||||
return new Oracle9Platform();
|
||||
}
|
||||
}
|
||||
if (dbProductName.indexOf("microsoft") > -1) {
|
||||
else if (dbProductName.contains("microsoft")) {
|
||||
if (majorVersion > 8) {
|
||||
return new MsSqlServer2005Platform();
|
||||
} else {
|
||||
return new MsSqlServer2000Platform();
|
||||
}
|
||||
}
|
||||
|
||||
if (dbProductName.indexOf("mysql") > -1) {
|
||||
else if (dbProductName.contains("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("h2") > -1) {
|
||||
else if (dbProductName.contains("h2")) {
|
||||
return new H2Platform();
|
||||
}
|
||||
if (dbProductName.indexOf("hsql database engine") > -1) {
|
||||
else if (dbProductName.contains("hsql database engine")) {
|
||||
return new HsqldbPlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("postgres") > -1) {
|
||||
else if (dbProductName.contains("postgres")) {
|
||||
return new PostgresPlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("sqlite") > -1) {
|
||||
else if (dbProductName.contains("sqlite")) {
|
||||
return new SQLitePlatform();
|
||||
}
|
||||
if (dbProductName.indexOf("sql anywhere") > -1) {
|
||||
else if (dbProductName.contains("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
else if (dbProductName.contains("sql anywhere")) {
|
||||
return new SqlAnywherePlatform();
|
||||
}
|
||||
|
||||
// use the standard one
|
||||
|
||||
// use the standard one
|
||||
return new DatabasePlatform();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyContext;
|
||||
import com.avaje.ebeaninternal.api.LoadManyRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyBuffer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -27,8 +27,6 @@ import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
/**
|
||||
* Helper to handle lazy loading and refreshing of beans.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class DefaultBeanLoader {
|
||||
|
||||
@@ -50,32 +48,29 @@ public class DefaultBeanLoader {
|
||||
* re-use the query plan cache and get DB statement re-use.
|
||||
* </p>
|
||||
*/
|
||||
private int getBatchSize(int batchListSize, int requestedBatchSize) {
|
||||
if (batchListSize == requestedBatchSize) {
|
||||
return batchListSize;
|
||||
}
|
||||
if (batchListSize == 1) {
|
||||
private int getBatchSize(int batchSize) {
|
||||
|
||||
if (batchSize == 1) {
|
||||
// there is only one bean/collection to load
|
||||
return 1;
|
||||
}
|
||||
if (requestedBatchSize <= 5) {
|
||||
if (batchSize <= 5) {
|
||||
// anything less than 5 becomes 5
|
||||
return 5;
|
||||
}
|
||||
if (batchListSize <= 10 || requestedBatchSize <= 10) {
|
||||
// 10 or less to load
|
||||
// ... or we wanted a batch size between 6 and 10
|
||||
if (batchSize <= 10) {
|
||||
return 10;
|
||||
}
|
||||
if (batchListSize <= 20 || requestedBatchSize <= 20) {
|
||||
// 20 or less to load
|
||||
// ... or we wanted a batch size between 11 and 20
|
||||
if (batchSize <= 20) {
|
||||
return 20;
|
||||
}
|
||||
if (batchListSize <= 50) {
|
||||
if (batchSize <= 50) {
|
||||
return 50;
|
||||
}
|
||||
return requestedBatchSize;
|
||||
if (batchSize <= 100) {
|
||||
return 100;
|
||||
}
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName) {
|
||||
@@ -86,9 +81,9 @@ public class DefaultBeanLoader {
|
||||
|
||||
List<BeanCollection<?>> batch = loadRequest.getBatch();
|
||||
|
||||
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadManyContext ctx = loadRequest.getLoadContext();
|
||||
LoadManyBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanPropertyAssocMany<?> many = ctx.getBeanProperty();
|
||||
|
||||
PersistenceContext pc = ctx.getPersistenceContext();
|
||||
@@ -111,20 +106,12 @@ public class DefaultBeanLoader {
|
||||
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
String idProperty = desc.getIdBinder().getIdProperty();
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
|
||||
query.setLazyLoadForParents(idList, many);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
|
||||
query.setMode(Mode.LAZYLOAD_MANY);
|
||||
query.setLazyLoadManyPath(many.getName());
|
||||
query.setPersistenceContext(pc);
|
||||
query.select(idProperty);
|
||||
query.fetch(many.getName());
|
||||
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
@@ -134,7 +121,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
if (loadRequest.isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.fetch(many.getName(), many.getTargetIdProperty());
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
server.findList(query, loadRequest.getTransaction());
|
||||
@@ -154,14 +141,14 @@ public class DefaultBeanLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadMany(BeanCollection<?> bc, LoadManyContext ctx, boolean onlyIds) {
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
Object parentBean = bc.getOwnerBean();
|
||||
String propertyName = bc.getPropertyName();
|
||||
|
||||
ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
|
||||
//ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
|
||||
|
||||
loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
|
||||
loadManyInternal(parentBean, propertyName, null, false, null, onlyIds);
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
|
||||
@@ -170,17 +157,15 @@ public class DefaultBeanLoader {
|
||||
|
||||
private void loadManyInternal(Object parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
|
||||
|
||||
EntityBeanIntercept ebi = null;
|
||||
PersistenceContext pc = null;
|
||||
BeanCollection<?> beanCollection = null;
|
||||
ExpressionList<?> filterMany = null;
|
||||
|
||||
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
|
||||
pc = ebi.getPersistenceContext();
|
||||
EntityBeanIntercept ebi = ((EntityBean) parentBean)._ebean_getIntercept();
|
||||
PersistenceContext pc = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
|
||||
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) parentDesc.getBeanProperty(propertyName);
|
||||
|
||||
BeanCollection<?> beanCollection = null;
|
||||
ExpressionList<?> filterMany = null;
|
||||
|
||||
Object currentValue = many.getValue(parentBean);
|
||||
if (currentValue instanceof BeanCollection<?>) {
|
||||
beanCollection = (BeanCollection<?>) currentValue;
|
||||
@@ -270,9 +255,9 @@ public class DefaultBeanLoader {
|
||||
throw new RuntimeException("Nothing in batch?");
|
||||
}
|
||||
|
||||
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadBeanContext ctx = loadRequest.getLoadContext();
|
||||
LoadBeanBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
@@ -349,7 +334,6 @@ public class DefaultBeanLoader {
|
||||
ebis[i].setLazyLoadFailure();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void refresh(Object bean) {
|
||||
@@ -362,7 +346,6 @@ public class DefaultBeanLoader {
|
||||
|
||||
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
|
||||
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
|
||||
PersistenceContext pc = ebi.getPersistenceContext();
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.meta.MetaObjectGraphNodeStats;
|
||||
|
||||
/**
|
||||
* DefaultServer based implementation of MetaInfoManager.
|
||||
*/
|
||||
public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
|
||||
private final DefaultServer server;
|
||||
|
||||
public DefaultMetaInfoManager(DefaultServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass) {
|
||||
return server.getBeanDescriptor(beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
return new ArrayList<MetaBeanInfo>(server.getBeanDescriptors());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<MetaQueryPlanStatistic>();
|
||||
|
||||
for (MetaBeanInfo metaBeanInfo : getMetaBeanInfoList()) {
|
||||
list.addAll(metaBeanInfo.collectQueryPlanStatistics(reset));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset) {
|
||||
|
||||
List<MetaObjectGraphNodeStats> list = new ArrayList<MetaObjectGraphNodeStats>();
|
||||
|
||||
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
|
||||
MetaObjectGraphNodeStats nodeStats = nodeStatistics.get(reset);
|
||||
if (nodeStats.getCount() > 0) {
|
||||
// Only collection non-empty statistics
|
||||
list.add(nodeStats);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
@@ -49,13 +50,18 @@ import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.bean.PersistenceContext.WithOption;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.EncryptKeyManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebean.text.json.JsonElement;
|
||||
@@ -154,7 +160,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private final CQueryEngine cqueryEngine;
|
||||
|
||||
@Deprecated
|
||||
//@Deprecated
|
||||
private DdlGenerator ddlGenerator;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
@@ -167,6 +173,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private final JsonContext jsonContext;
|
||||
|
||||
private final MetaInfoManager metaInfoManager;
|
||||
|
||||
/**
|
||||
* The MBean name used to register Ebean.
|
||||
*/
|
||||
@@ -187,38 +195,56 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
private int lazyLoadBatchSize;
|
||||
|
||||
/** The query batch size */
|
||||
/**
|
||||
* The query batch size
|
||||
*/
|
||||
private int queryBatchSize;
|
||||
|
||||
/**
|
||||
* JDBC driver specific handling for JDBC batch execution.
|
||||
*/
|
||||
private PstmtBatch pstmtBatch;
|
||||
|
||||
|
||||
/**
|
||||
* holds plugins (e.g. ddl generator) detected by the service loader
|
||||
*/
|
||||
private List<SpiEbeanPlugin> ebeanPlugins;
|
||||
|
||||
private final boolean collectQueryOrigins;
|
||||
|
||||
private final boolean collectQueryStatsByNode;
|
||||
|
||||
/**
|
||||
* Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points).
|
||||
*/
|
||||
protected final ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics> objectGraphStats;
|
||||
|
||||
/**
|
||||
* Create the DefaultServer.
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
ServerConfig serverConfig = config.getServerConfig();
|
||||
|
||||
this.objectGraphStats = new ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics>();
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
this.serverCacheManager = cache;
|
||||
this.pstmtBatch = config.getPstmtBatch();
|
||||
this.databasePlatform = config.getDatabasePlatform();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.serverName = config.getServerConfig().getName();
|
||||
this.lazyLoadBatchSize = config.getServerConfig().getLazyLoadBatchSize();
|
||||
this.queryBatchSize = config.getServerConfig().getQueryBatchSize();
|
||||
|
||||
this.serverName = serverConfig.getName();
|
||||
this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize();
|
||||
this.queryBatchSize = serverConfig.getQueryBatchSize();
|
||||
this.cqueryEngine = config.getCQueryEngine();
|
||||
this.expressionFactory = config.getExpressionFactory();
|
||||
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
|
||||
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
|
||||
|
||||
this.beanDescriptorManager = config.getBeanDescriptorManager();
|
||||
beanDescriptorManager.setEbeanServer(this);
|
||||
|
||||
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
|
||||
|
||||
this.defaultUpdateNullProperties = "true"
|
||||
@@ -276,6 +302,11 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
public boolean isDefaultDeleteMissingChildren() {
|
||||
return defaultDeleteMissingChildren;
|
||||
}
|
||||
@@ -295,6 +326,11 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
public DatabasePlatform getDatabasePlatform() {
|
||||
return databasePlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaInfoManager getMetaInfoManager() {
|
||||
return metaInfoManager;
|
||||
}
|
||||
|
||||
public BackgroundExecutor getBackgroundExecutor() {
|
||||
return backgroundExecutor;
|
||||
@@ -501,7 +537,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
beanLoader.loadMany(bc, null, onlyIds);
|
||||
beanLoader.loadMany(bc, onlyIds);
|
||||
}
|
||||
|
||||
public void refresh(Object bean) {
|
||||
@@ -1136,9 +1172,14 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
// first look in the persistence context
|
||||
context = t.getPersistenceContext();
|
||||
if (context != null) {
|
||||
Object o = context.get(beanDescriptor.getBeanType(), query.getId());
|
||||
WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId());
|
||||
if (o != null) {
|
||||
return (T) o;
|
||||
if (o.isDeleted()) {
|
||||
// Bean was previously deleted in the same transaction / persistence context
|
||||
return null;
|
||||
}
|
||||
// Return the entity bean instance from the persistence context
|
||||
return (T) o.getBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1148,6 +1189,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hit the L2 bean cache
|
||||
Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), query.isReadOnly());
|
||||
if (cachedBean != null) {
|
||||
if (context == null) {
|
||||
@@ -1156,7 +1198,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
context.put(query.getId(), cachedBean);
|
||||
|
||||
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false);
|
||||
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), query);
|
||||
loadContext.setPersistenceContext(context);
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
|
||||
@@ -1918,6 +1960,13 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return beanDescriptorManager.getBeanDescriptorList();
|
||||
}
|
||||
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
List<MetaBeanInfo> list = new ArrayList<MetaBeanInfo>();
|
||||
list.addAll(getBeanDescriptors());
|
||||
return list;
|
||||
}
|
||||
|
||||
public void register(BeanPersistController c) {
|
||||
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -2067,4 +2116,20 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return jsonContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
|
||||
|
||||
if (collectQueryStatsByNode) {
|
||||
CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node);
|
||||
if (nodeStatistics == null) {
|
||||
// race condition here but I actually don't care too much if we miss a
|
||||
// few early statistics - especially when the server is warming up etc
|
||||
nodeStatistics = new CObjectGraphNodeStatistics(node);
|
||||
objectGraphStats.put(node, nodeStatistics);
|
||||
}
|
||||
nodeStatistics.add(loadedBeanCount, timeMicros);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -353,6 +353,11 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
if (sequenceFormat != null) {
|
||||
nc.setSequenceFormat(sequenceFormat);
|
||||
}
|
||||
|
||||
String schema = config.getProperty("namingConvention.schema");
|
||||
if (schema != null) {
|
||||
nc.setSchema(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,32 +415,10 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dsConfig.getHeartbeatSql() == null) {
|
||||
// use default heartbeatSql from the DatabasePlatform
|
||||
String heartbeatSql = getHeartbeatSql(dsConfig.getDriver());
|
||||
dsConfig.setHeartbeatSql(heartbeatSql);
|
||||
}
|
||||
|
||||
DataSourceAlert notify = new SimpleDataSourceAlert();
|
||||
return new DataSourcePool(notify, config.getName(), dsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a heartbeatSql depending on the jdbc driver name.
|
||||
*/
|
||||
private String getHeartbeatSql(String driver) {
|
||||
if (driver != null) {
|
||||
String d = driver.toLowerCase();
|
||||
if (d.contains("oracle")) {
|
||||
return "select 'x' from dual";
|
||||
}
|
||||
if (d.contains(".h2.") || d.contains(".mysql.") || d.contains("postgre")) {
|
||||
return "select 1";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the autoCommit and Transaction Isolation levels of the DataSource.
|
||||
* <p>
|
||||
|
||||
@@ -14,6 +14,8 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.BeanIdList;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
@@ -49,9 +51,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
private Integer cacheKey;
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private int queryPlanHash;
|
||||
private HashQueryPlan queryPlanHash;
|
||||
|
||||
/**
|
||||
* Flag set if background fetching taking place. In this case the transaction
|
||||
@@ -332,7 +334,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* with just the bind variables changing.
|
||||
* </p>
|
||||
*/
|
||||
public int getQueryPlanHash() {
|
||||
public HashQueryPlan getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
}
|
||||
|
||||
@@ -356,14 +358,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.getType() == null) {
|
||||
// the query plan and bind values must be the same
|
||||
cacheKey = Integer.valueOf(query.queryHash());
|
||||
|
||||
} else {
|
||||
// additionally the return type (List/Set/Map) must be the same
|
||||
cacheKey = Integer.valueOf(31 * query.queryHash() + query.getType().hashCode());
|
||||
}
|
||||
cacheKey = query.queryHash();
|
||||
|
||||
// TODO: Sort out returning BeanCollection from L2 cache
|
||||
return null;
|
||||
@@ -389,4 +384,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
}
|
||||
|
||||
public void flushPersistenceContextOnIterate() {
|
||||
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,176 +1,71 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathReader;
|
||||
import com.avaje.ebeaninternal.server.util.DefaultClassPathReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to read the orm.xml and ebean-orm.xml configuration files.
|
||||
*
|
||||
*
|
||||
* @author rbygrave
|
||||
* @author Richard Vowles - http://plus.google.com/RichardVowles
|
||||
*/
|
||||
public class XmlConfigLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class);
|
||||
|
||||
private final ClassPathReader classPathReader;
|
||||
private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class);
|
||||
|
||||
private final Object[] classPaths;
|
||||
|
||||
|
||||
public XmlConfigLoader(ClassLoader classLoader){
|
||||
|
||||
if (classLoader == null) {
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
|
||||
String cn = GlobalProperties.get("ebean.classpathreader", null);
|
||||
if (cn != null){
|
||||
// use a user defined classPathReader
|
||||
logger.info("Using ["+cn+"] to read the searchable class path");
|
||||
this.classPathReader = (ClassPathReader)ClassUtil.newInstance(cn, this.getClass());
|
||||
} else {
|
||||
this.classPathReader = new DefaultClassPathReader();
|
||||
}
|
||||
|
||||
this.classPaths = classPathReader.readPath(classLoader);
|
||||
}
|
||||
|
||||
public XmlConfig load() {
|
||||
List<Dnode> ormXml = search("META-INF/orm.xml");
|
||||
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
|
||||
|
||||
return new XmlConfig(ormXml, ebeanOrmXml);
|
||||
}
|
||||
|
||||
public List<Dnode> search(String searchFor) {
|
||||
|
||||
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
String charsetName = Charset.defaultCharset().name();
|
||||
public XmlConfigLoader(ClassLoader classLoader) {
|
||||
|
||||
for (int h = 0; h < classPaths.length; h++) {
|
||||
if (classLoader == null) {
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
|
||||
try {
|
||||
// for each class path ...
|
||||
File classPath;
|
||||
if (URL.class.isInstance(classPaths[h])) {
|
||||
classPath = new File(((URL) classPaths[h]).getFile());
|
||||
} else {
|
||||
classPath = new File(classPaths[h].toString());
|
||||
}
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
// URL Decode the path replacing %20 to space characters.
|
||||
String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName);
|
||||
public XmlConfig load() {
|
||||
List<Dnode> ormXml = search("META-INF/orm.xml");
|
||||
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
|
||||
|
||||
classPath = new File(path);
|
||||
return new XmlConfig(ormXml, ebeanOrmXml);
|
||||
}
|
||||
|
||||
if (classPath.isDirectory()) {
|
||||
checkDir(searchFor, xmlList, classPath);
|
||||
public List<Dnode> search(String resourceName) {
|
||||
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
|
||||
|
||||
} else if (classPath.getName().endsWith(".jar") || classPath.getName().endsWith(".war") || classPath.getName().endsWith(".war!/WEB-INF/classes")) {
|
||||
checkJar(searchFor, xmlList, classPath);
|
||||
|
||||
} else {
|
||||
// this is not expected
|
||||
String msg = "Not a Jar or Directory? " + classPath.getAbsolutePath();
|
||||
logger.error(msg);
|
||||
}
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources(resourceName);
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
|
||||
return xmlList;
|
||||
|
||||
}
|
||||
InputStream is = url.openStream();
|
||||
processInputStream(xmlList, is);
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Unable to find resources {}", resourceName);
|
||||
}
|
||||
|
||||
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
|
||||
|
||||
DnodeReader reader = new DnodeReader();
|
||||
Dnode xmlDoc = reader.parseXml(is);
|
||||
is.close();
|
||||
|
||||
xmlList.add(xmlDoc);
|
||||
}
|
||||
|
||||
private void checkFile(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
|
||||
return xmlList;
|
||||
}
|
||||
|
||||
File f = new File(dir, searchFor);
|
||||
if (f.exists()){
|
||||
FileInputStream fis = new FileInputStream(f);
|
||||
BufferedInputStream is = new BufferedInputStream(fis);
|
||||
processInputStream(xmlList, is);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDir(String searchFor, ArrayList<Dnode> xmlList, File dir) throws IOException {
|
||||
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
|
||||
|
||||
checkFile(searchFor, xmlList, dir);
|
||||
|
||||
if (dir.getPath().endsWith("classes")) {
|
||||
// see if this is part of webapp and look for META-INF/searchFor
|
||||
// relative to the WEB-INF/classes directory
|
||||
File parent = dir.getParentFile();
|
||||
if (parent != null && parent.getPath().endsWith("WEB-INF")){
|
||||
parent = parent.getParentFile();
|
||||
if (parent != null){
|
||||
File metaInf = new File(parent, "META-INF");
|
||||
if (metaInf.exists()){
|
||||
checkFile(searchFor, xmlList, metaInf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkJar(String searchFor, ArrayList<Dnode> xmlList, File classPath) throws IOException {
|
||||
|
||||
String fileName = classPath.getName();
|
||||
if (fileName.toLowerCase().startsWith("surefire")){
|
||||
return;
|
||||
}
|
||||
if (classPath.getAbsolutePath().endsWith(".war!/WEB-INF/classes")) {
|
||||
classPath = new File(classPath.getAbsolutePath().substring(0, classPath.getAbsolutePath().lastIndexOf('!')));
|
||||
}
|
||||
JarFile module = null;
|
||||
try {
|
||||
module = new JarFile(classPath);
|
||||
ZipEntry entry = module.getEntry(searchFor);
|
||||
if (entry != null){
|
||||
InputStream is = module.getInputStream(entry);
|
||||
processInputStream(xmlList, is);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("Unable to check jar file "+fileName+" for ebean-orm.xml");
|
||||
} finally {
|
||||
if (module != null){
|
||||
module.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
DnodeReader reader = new DnodeReader();
|
||||
Dnode xmlDoc = reader.parseXml(is);
|
||||
is.close();
|
||||
|
||||
|
||||
xmlList.add(xmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.Query.UseIndex;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
@@ -33,8 +36,11 @@ import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebean.text.TextException;
|
||||
import com.avaje.ebean.text.json.JsonWriteBeanVisitor;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
@@ -59,6 +65,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.persist.DmlUtil;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
|
||||
@@ -72,19 +79,16 @@ import com.avaje.ebeaninternal.util.SortByClause;
|
||||
import com.avaje.ebeaninternal.util.SortByClause.Property;
|
||||
import com.avaje.ebeaninternal.util.SortByClauseParser;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> {
|
||||
public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
private final ConcurrentHashMap<Integer, SpiUpdatePlan> updatePlanCache = new ConcurrentHashMap<Integer, SpiUpdatePlan>();
|
||||
|
||||
private final ConcurrentHashMap<Integer, CQueryPlan> queryPlanCache = new ConcurrentHashMap<Integer, CQueryPlan>();
|
||||
private final ConcurrentHashMap<HashQueryPlan, CQueryPlan> queryPlanCache = new ConcurrentHashMap<HashQueryPlan, CQueryPlan>();
|
||||
|
||||
private final ConcurrentHashMap<String, ElPropertyValue> elGetCache = new ConcurrentHashMap<String, ElPropertyValue>();
|
||||
|
||||
@@ -966,11 +970,14 @@ public class BeanDescriptor<T> {
|
||||
|
||||
public void cachePutMany(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId) {
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
Collection<?> actualDetails = bc.getActualDetails();
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<Object>();
|
||||
|
||||
// get the underlying collection of beans (in the List, Set or Map)
|
||||
Collection<?> actualDetails = bc.getActualDetails();
|
||||
for (Object bean : actualDetails) {
|
||||
Object id = targetDescriptor.getId(bean);
|
||||
idList.add(id);
|
||||
// Collect the id values
|
||||
idList.add(targetDescriptor.getId(bean));
|
||||
}
|
||||
CachedManyIds ids = new CachedManyIds(idList);
|
||||
cachePutCachedManyIds(parentId, many.getName(), ids);
|
||||
@@ -1149,6 +1156,27 @@ public class BeanDescriptor<T> {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
return collectQueryPlanStatisticsInternal(reset, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset) {
|
||||
return collectQueryPlanStatisticsInternal(reset, false);
|
||||
}
|
||||
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatisticsInternal(boolean reset, boolean collectAll) {
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<MetaQueryPlanStatistic>(queryPlanCache.size());
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
Snapshot snapshot = queryPlan.getSnapshot(reset);
|
||||
if (collectAll || snapshot.getExecutionCount() > 0) {
|
||||
list.add(snapshot);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the statistics on all the query plans.
|
||||
*/
|
||||
@@ -1178,11 +1206,11 @@ public class BeanDescriptor<T> {
|
||||
return queryPlanCache.values().iterator();
|
||||
}
|
||||
|
||||
public CQueryPlan getQueryPlan(Integer key) {
|
||||
public CQueryPlan getQueryPlan(HashQueryPlan key) {
|
||||
return queryPlanCache.get(key);
|
||||
}
|
||||
|
||||
public void putQueryPlan(Integer key, CQueryPlan plan) {
|
||||
public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) {
|
||||
queryPlanCache.put(key, plan);
|
||||
}
|
||||
|
||||
@@ -2445,5 +2473,13 @@ public class BeanDescriptor<T> {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void flushPersistenceContextOnIterate(PersistenceContext persistenceContext) {
|
||||
persistenceContext.clear(beanType);
|
||||
for (int i = 0; i < propertiesMany.length; i++) {
|
||||
persistenceContext.clear(propertiesMany[i].getBeanDescriptor().getBeanType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,11 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
public boolean isDeployOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false.
|
||||
|
||||
@@ -36,6 +36,9 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
@@ -77,12 +80,21 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new ArrayList<T>() : new BeanList<T>();
|
||||
if (vanilla) {
|
||||
return new ArrayList<T>();
|
||||
}
|
||||
BeanList<T> beanList = new BeanList<T>();
|
||||
if (many != null) {
|
||||
beanList.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanList;
|
||||
}
|
||||
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanList<T>(loader, parentBean, propertyName);
|
||||
BeanList<T> beanList = new BeanList<T>(loader, parentBean, propertyName);
|
||||
beanList.setModifyListening(many.getModifyListenMode());
|
||||
return beanList;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -101,22 +101,34 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new LinkedHashMap() : new BeanMap();
|
||||
if (vanilla) {
|
||||
return new LinkedHashMap();
|
||||
}
|
||||
BeanMap beanMap = new BeanMap();
|
||||
if (many != null) {
|
||||
beanMap.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
|
||||
Object keyValue = beanProperty.getValueIntercept(bean);
|
||||
|
||||
Map<Object, Object> map = (Map<Object, Object>) collection;
|
||||
map.put(keyValue, bean);
|
||||
((BeanMap<?,?>) collection).internalPut(keyValue, bean);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanMap(loader, parentBean, propertyName);
|
||||
BeanMap beanMap = new BeanMap(loader, parentBean, propertyName);
|
||||
if (many != null) {
|
||||
beanMap.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -837,6 +837,12 @@ public class BeanProperty implements ElPropertyValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return formula && sqlFormulaJoin != null;
|
||||
}
|
||||
|
||||
public boolean containsManySince(String sinceProperty) {
|
||||
return containsMany();
|
||||
}
|
||||
|
||||
@@ -140,11 +140,23 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
} else {
|
||||
delStmt = "delete from "+targetDescriptor.getBaseTable()+" where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false);
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true);
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false,"");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true,"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the bean to the appropriate collection on the parent bean.
|
||||
*/
|
||||
public void addBeanToCollectionWithCreate(Object parentBean, Object detailBean) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>)super.getValue(parentBean);
|
||||
if (bc == null) {
|
||||
bc = (BeanCollection<?>)help.createEmpty(false);
|
||||
setValue(parentBean, bc);
|
||||
}
|
||||
help.add(bc, detailBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Object bean) {
|
||||
return super.getValue(bean);
|
||||
@@ -196,7 +208,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private List<Object> findIdsByParentId(Object parentId, Transaction t, ArrayList<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false);
|
||||
String rawWhere = deriveWhereParentIdSql(false,"");
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
@@ -212,9 +224,29 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = manyToMany ? "int_." : "t0.";
|
||||
if (manyToMany) {
|
||||
query.setIncludeTableJoin(inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String inClause = descriptor.getIdBinder().getIdInValueExpr(parentIds.size());
|
||||
|
||||
String expr = rawWhere+inClause;
|
||||
|
||||
// Flatten the bind values if needed (embeddedId)
|
||||
List<Object> bindValues = getBindParentIds(parentIds);
|
||||
|
||||
query.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true);
|
||||
String rawWhere = deriveWhereParentIdSql(true,"");
|
||||
String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size());
|
||||
|
||||
String expr = rawWhere+inClause;
|
||||
@@ -465,6 +497,20 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public Object getParentId(Object parentBean) {
|
||||
return descriptor.getId(parentBean);
|
||||
}
|
||||
|
||||
public List<Object> getBindParentIds(List<Object> parentIds) {
|
||||
if (exportedProperties.length == 1){
|
||||
return parentIds;
|
||||
}
|
||||
List<Object> expandedList = new ArrayList<Object>(parentIds.size()*exportedProperties.length);
|
||||
for (int i=0; i < parentIds.size(); i++) {
|
||||
for (int y = 0; y < exportedProperties.length; y++) {
|
||||
Object compId = parentIds.get(i);
|
||||
expandedList.add(exportedProperties[y].getValue(compId));
|
||||
}
|
||||
}
|
||||
return expandedList;
|
||||
}
|
||||
|
||||
private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId){
|
||||
|
||||
@@ -493,7 +539,18 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return pos;
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause) {
|
||||
public void addSelectExported(DbSqlContext ctx, String tableAlias) {
|
||||
|
||||
String alias = manyToMany ? "int_" : tableAlias;
|
||||
if (alias == null) {
|
||||
alias = "t0";
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -506,7 +563,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(fkColumn);
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause){
|
||||
sb.append("=? ");
|
||||
}
|
||||
|
||||
@@ -77,17 +77,29 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new LinkedHashSet<T>() : new BeanSet<T>();
|
||||
if (vanilla) {
|
||||
return new LinkedHashSet<T>();
|
||||
}
|
||||
BeanSet<T> beanSet = new BeanSet<T>();
|
||||
if (many != null) {
|
||||
beanSet.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanSet;
|
||||
}
|
||||
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanSet<T>(loader, parentBean, propertyName);
|
||||
BeanSet<T> beanSet = new BeanSet<T>(loader, parentBean, propertyName);
|
||||
beanSet.setModifyListening(many.getModifyListenMode());
|
||||
return beanSet;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -72,7 +72,7 @@ public interface DbReadContext {
|
||||
/**
|
||||
* Set back the bean that has just been loaded with its id.
|
||||
*/
|
||||
public void setLoadedBean(Object loadedBean, Object id);
|
||||
public void setLoadedBean(Object loadedBean, Object id, Object lazyLoadParentId);
|
||||
|
||||
/**
|
||||
* Set back the 'detail' bean that has just been loaded.
|
||||
|
||||
@@ -38,6 +38,10 @@ public class ManyType {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return Underlying.MAP.equals(underlying);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the matching Query type.
|
||||
*/
|
||||
|
||||
@@ -122,7 +122,13 @@ public class ElPropertyChain implements ElPropertyValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsMany() {
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
// Not cascading the check at this stage
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsMany() {
|
||||
return containsMany;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@ public interface ElPropertyDeploy {
|
||||
*/
|
||||
public static final String ROOT_ELPREFIX = "${}";
|
||||
|
||||
/**
|
||||
* Return true if the property is a formula with a join clause.
|
||||
*/
|
||||
public boolean containsFormulaWithJoin();
|
||||
|
||||
/**
|
||||
* Return true if there is a property on the path that is a many property.
|
||||
*/
|
||||
|
||||
@@ -40,11 +40,18 @@ public abstract class AbstractExpression implements SpiExpression {
|
||||
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
String propertyName = getPropertyName();
|
||||
if (propertyName != null){
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName);
|
||||
if (elProp != null && elProp.containsMany()){
|
||||
manyWhereJoin.add(elProp);
|
||||
if (elProp != null) {
|
||||
if (elProp.containsFormulaWithJoin()) {
|
||||
// for findRowCount query select clause
|
||||
manyWhereJoin.addFormulaWithJoin(propertyName);
|
||||
}
|
||||
if (elProp.containsMany()){
|
||||
// for findRowCount we join to a many property
|
||||
manyWhereJoin.add(elProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -105,29 +107,29 @@ class AllEqualsExpression implements SpiExpression {
|
||||
* The null check is required due to the "is null" sql being generated.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
int hc = AllEqualsExpression.class.getName().hashCode();
|
||||
Set<Entry<String, Object>> entries = propMap.entrySet();
|
||||
Iterator<Entry<String, Object>> it = entries.iterator();
|
||||
builder.add(AllEqualsExpression.class);
|
||||
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<java.lang.String, java.lang.Object> entry = it.next();
|
||||
for (Entry<String, Object> entry : propMap.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
String propName = entry.getKey();
|
||||
builder.add(propName).add(value == null ? 0 : 1);
|
||||
builder.bind(value == null ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
hc = hc * 31 + (value == null ? 0 : 1);
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
int hc = 31;
|
||||
for (Object value : propMap.values()) {
|
||||
hc = hc * 31 + Objects.hashCode(value);
|
||||
}
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
return queryAutoFetchHash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
|
||||
@@ -30,14 +31,13 @@ class BetweenExpression extends AbstractExpression {
|
||||
request.append(getPropertyName()).append(BETWEEN).append(" ? and ? ");
|
||||
}
|
||||
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = BetweenExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenExpression.class).add(propName);
|
||||
builder.bind(2);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
+6
-7
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -65,15 +66,13 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty));
|
||||
}
|
||||
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = BetweenPropertyExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + lowProperty.hashCode();
|
||||
hc = hc * 31 + highProperty.hashCode();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenPropertyExpression.class).add(lowProperty).add(highProperty);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
+7
-7
@@ -1,10 +1,11 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
class CaseInsensitiveEqualExpression extends AbstractExpression implements LuceneAwareExpression {
|
||||
class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = -6406036750998971064L;
|
||||
|
||||
@@ -40,14 +41,13 @@ class CaseInsensitiveEqualExpression extends AbstractExpression implements Lucen
|
||||
request.append("lower(").append(pname).append(") =? ");
|
||||
}
|
||||
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = CaseInsensitiveEqualExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(CaseInsensitiveEqualExpression.class).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
+6
-7
@@ -6,6 +6,7 @@ import java.util.Iterator;
|
||||
import com.avaje.ebean.ExampleExpression;
|
||||
import com.avaje.ebean.LikeType;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -163,28 +164,26 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
/**
|
||||
* Return a hash for autoFetch query identification.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
// we have not yet built the list of expressions
|
||||
// so just based on the class name
|
||||
return DefaultExampleExpression.class.getName().hashCode();
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for query plan identification.
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
// this is always called once, and always called before
|
||||
// addSql() and addBindValues() methods
|
||||
list = buildExpressions(request);
|
||||
|
||||
int hc = DefaultExampleExpression.class.getName().hashCode();
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
hc = hc * 31 + list.get(i).queryPlanHash(request);
|
||||
list.get(i).queryPlanHash(request, builder);
|
||||
}
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-7
@@ -21,18 +21,19 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
|
||||
private static final Object[] EMPTY_ARRAY = new Object[] {};
|
||||
|
||||
private final FilterExprPath prefix;
|
||||
private final FilterExprPath prefix = null;
|
||||
|
||||
public DefaultExpressionFactory() {
|
||||
this(null);
|
||||
//this();//null);
|
||||
}
|
||||
|
||||
public DefaultExpressionFactory(FilterExprPath prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
// public DefaultExpressionFactory(FilterExprPath prefix) {
|
||||
// this.prefix = prefix;
|
||||
// }
|
||||
|
||||
public ExpressionFactory createExpressionFactory(FilterExprPath prefix) {
|
||||
return new DefaultExpressionFactory(prefix);
|
||||
public ExpressionFactory createExpressionFactory(){//FilterExprPath prefix) {
|
||||
return this;
|
||||
//return new DefaultExpressionFactory(prefix);
|
||||
}
|
||||
|
||||
public String getLang() {
|
||||
@@ -244,6 +245,9 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
* Id Equal to - ID property is equal to the value.
|
||||
*/
|
||||
public Expression idEq(Object value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("The id value is null");
|
||||
}
|
||||
return new IdExpression(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,38 +5,39 @@ import java.io.Serializable;
|
||||
/**
|
||||
* This is the path prefix for filterMany.
|
||||
* <p>
|
||||
* The actual path can change due to FetchConfig query joins that proceed
|
||||
* the query that includes the filterMany.
|
||||
* The actual path can change due to FetchConfig query joins that proceed the
|
||||
* query that includes the filterMany.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class FilterExprPath implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6420905565372842018L;
|
||||
|
||||
/**
|
||||
* The path of the filterMany.
|
||||
*/
|
||||
private String path;
|
||||
|
||||
public FilterExprPath(String path){
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim off leading part of the path due to a
|
||||
* proceeding (earlier) query join etc.
|
||||
*/
|
||||
public void trimPath(int prefixTrim) {
|
||||
path = path.substring(prefixTrim);
|
||||
}
|
||||
private static final long serialVersionUID = -6420905565372842018L;
|
||||
|
||||
/**
|
||||
* Return the path. This is a prefix used in the filterMany expressions.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
/**
|
||||
* The path of the filterMany.
|
||||
*/
|
||||
private String path;
|
||||
|
||||
public FilterExprPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the FilterExprPath trimming off leading part of the path
|
||||
* due to a proceeding (earlier) query join etc.
|
||||
*/
|
||||
public FilterExprPath trimPath(int prefixTrim) {
|
||||
if (prefixTrim >= path.length()) {
|
||||
return new FilterExprPath(null);
|
||||
}
|
||||
return new FilterExprPath(path.substring(prefixTrim));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path. This is a prefix used in the filterMany expressions.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -49,14 +50,13 @@ class IdExpression implements SpiExpression {
|
||||
/**
|
||||
* No properties so this is just a unique static number.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
// this number is unique for a given bean type
|
||||
// which is all that is required
|
||||
return IdExpression.class.getName().hashCode();
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdExpression.class);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -67,16 +68,13 @@ public class IdInExpression implements SpiExpression {
|
||||
/**
|
||||
* Incorporates the number of Id values to bind.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
// this number is unique for a given bean type
|
||||
// which is all that is required
|
||||
int hc = IdInExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + idList.size();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdInExpression.class).add(idList.size());
|
||||
builder.bind(idList.size());
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -79,19 +80,18 @@ class InExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on the number of values in the in clause.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = InExpression.class.getName().hashCode() + 31 * values.length;
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InExpression.class).add(propName).add(values.length);
|
||||
builder.bind(values.length);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = 0;
|
||||
for (int i = 1; i < values.length; i++) {
|
||||
int hc = 31;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
hc = 31 * hc + values[i].hashCode();
|
||||
}
|
||||
return hc;
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
@@ -26,23 +27,19 @@ class InQueryExpression extends AbstractExpression {
|
||||
this.subQuery = subQuery;
|
||||
}
|
||||
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = InQueryExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
hc = hc * 31 + subQuery.queryAutofetchHash();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InQueryExpression.class).add(propName);
|
||||
|
||||
subQuery.queryAutofetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
// queryPlanHash executes prior to addSql() or addBindValues()
|
||||
// ... so compiledQuery will exist
|
||||
compiledSubQuery = compileSubQuery(request);
|
||||
|
||||
int hc = InQueryExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
hc = hc * 31 + subQuery.queryPlanHash(request);
|
||||
return hc;
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.ebean.QueryListener;
|
||||
import com.avaje.ebean.QueryResultVisitor;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -115,28 +116,20 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
/**
|
||||
* Based on Junction type and all the expression contained.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = JunctionExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + joinType.hashCode();
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
hc = hc * 31 + list.get(i).queryAutoFetchHash();
|
||||
list.get(i).queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
int hc = JunctionExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + joinType.hashCode();
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
hc = hc * 31 + list.get(i).queryPlanHash(request);
|
||||
list.get(i).queryPlanHash(request, builder);
|
||||
}
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -2,10 +2,11 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.LikeType;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
class LikeExpression extends AbstractExpression implements LuceneAwareExpression {
|
||||
class LikeExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = -5398151809111172380L;
|
||||
|
||||
@@ -59,15 +60,13 @@ class LikeExpression extends AbstractExpression implements LuceneAwareExpression
|
||||
/**
|
||||
* Based on caseInsensitive and the property name.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = LikeExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + (caseInsensitive ? 0 : 1);
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(LikeExpression.class).add(caseInsensitive).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.Expression;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -69,18 +70,16 @@ abstract class LogicExpression implements SpiExpression {
|
||||
/**
|
||||
* Based on the joinType plus the two expressions.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = LogicExpression.class.getName().hashCode() + joinType.hashCode();
|
||||
hc = hc * 31 + expOne.queryAutoFetchHash();
|
||||
hc = hc * 31 + expTwo.queryAutoFetchHash();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(LogicExpression.class).add(joinType);
|
||||
expOne.queryAutoFetchHash(builder);
|
||||
expTwo.queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
int hc = LogicExpression.class.getName().hashCode() + joinType.hashCode();
|
||||
hc = hc * 31 + expOne.queryPlanHash(request);
|
||||
hc = hc * 31 + expTwo.queryPlanHash(request);
|
||||
return hc;
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
builder.add(LogicExpression.class).add(joinType);
|
||||
expOne.queryPlanHash(request, builder);
|
||||
expTwo.queryPlanHash(request, builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
/**
|
||||
* Marker interface for lucene aware expressions.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LuceneAwareExpression {
|
||||
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.Expression;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
final class NotExpression implements SpiExpression, LuceneAwareExpression {
|
||||
final class NotExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = 5648926732402355781L;
|
||||
|
||||
@@ -36,16 +37,13 @@ final class NotExpression implements SpiExpression, LuceneAwareExpression {
|
||||
/**
|
||||
* Based on the expression.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = NotExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + exp.queryAutoFetchHash();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NotExpression.class);
|
||||
exp.queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
int hc = NotExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + exp.queryPlanHash(request);
|
||||
return hc;
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -41,15 +42,12 @@ class NullExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on notNull flag and the propertyName.
|
||||
*/
|
||||
public int queryAutoFetchHash() {
|
||||
int hc = NullExpression.class.getName().hashCode();
|
||||
hc = hc * 31 + (notNull ? 1 : 0);
|
||||
hc = hc * 31 + propName.hashCode();
|
||||
return hc;
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NullExpression.class).add(notNull).add(propName);
|
||||
}
|
||||
|
||||
public int queryPlanHash(BeanQueryRequest<?> request) {
|
||||
return queryAutoFetchHash();
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Exception used to wrap Lucene parsing exceptions.
|
||||
*/
|
||||
public class PersistenceLuceneParseException extends PersistenceException {
|
||||
|
||||
private static final long serialVersionUID = 838790249273928392L;
|
||||
|
||||
public PersistenceLuceneParseException(Throwable e){
|
||||
super(e);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user