Compare commits

..
Author SHA1 Message Date
Rob Bygrave 6f214590aa [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1-RC2 2014-01-31 02:12:07 +13:00
Rob Bygrave 0014ea3501 Remove unused Lucene references including Query.UserIndex and associated methods 2014-01-31 01:26:12 +13:00
Rob Bygrave db3140512f set specific packages on dynamically loaded agent for tests 2014-01-31 00:59:48 +13:00
Rob Bygrave 8bfa331c61 Cleanup of expressions after FilterExprPath now only needed on FilterExpressionList 2014-01-31 00:48:59 +13:00
Rob Bygrave 456a1d8608 Remove AutoFetch query statistics meta entity beans, Remove Xml bean parsing (for json etc) 2014-01-31 00:22:38 +13:00
Rob Bygrave 695d3374a8 Default classPath scan include all jars (hopefully better default
behaviour)
2014-01-31 00:17:11 +13:00
Rob Bygrave 5488d054bf [maven-release-plugin] prepare for next development iteration 2014-01-30 23:25:57 +13:00
Rob Bygrave 4d72273dc3 [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1-RC1 2014-01-30 23:25:44 +13:00
Rob Bygrave cb9a5d6709 Change pom version 3.3.1-RC1 2014-01-30 23:23:53 +13:00
Rob Bygrave 1b8aa682c6 Change classPath scan to support searching manifest (maven surefire classpath scanning) 2014-01-30 23:21:50 +13:00
Rob Bygrave ef39bfcf14 Merge branch 'many-lazy-load' 2014-01-30 22:32:50 +13:00
Rob Bygrave 749f8d0d45 Change *ToMany lazy loading to not use the parent entity/table 2014-01-30 22:27:52 +13:00
Rob Bygrave 2fd221bbb5 Removed old XML code that never got into main code 2014-01-23 00:55:09 +13:00
Rob Bygrave ee96c4afa6 Fix for Issue 72 - Bug - models.Role cannot be cast to java.util.Map [error] at com.avaje.ebeaninternal.server.persist.DefaultPersister.saveAssocManyDetails(DefaultPersister.java:877) 2014-01-23 00:50:09 +13:00
Rob Bygrave 87a948223c Merge branch 'dbpool-logging' 2014-01-22 01:09:19 +13:00
Rob Bygrave acca803a80 Add MetaObjectGraphNodeStats and MetaQueryPlanOriginCount etc 2014-01-22 01:05:22 +13:00
Rob Bygrave 470b27efe1 Mark QueryListener deprecated in favor of findIterate() and findVisit() 2014-01-21 19:41:46 +13:00
Rob Bygrave f287205421 Refactor of query plan hash - add HashQueryPlan, HashQuery etc 2014-01-21 00:48:38 +13:00
Rob Bygrave 6bd8c2bd05 Add MetaInfoManager API, remove MetaQueryStatistic entity bean, refactor query execution stats collection 2014-01-19 22:49:18 +13:00
Rob Bygrave 05dcdf162b Refactor internals of batch lazy loading and query fetches 2014-01-18 23:35:42 +13:00
Rob Bygrave e97e77e2e0 Add reading/setting of namingconvention.schema from ebean.properties 2014-01-17 00:31:03 +13:00
Rob Bygrave e685e2f4ac Updated maven version in README 2014-01-16 23:16:00 +13:00
Rob Bygrave c6019bf3c5 Fix for issue 46 - Persistence Context Cache not cleared when deleting a collection of entities in a running transaction 2014-01-16 23:09:57 +13:00
Rob Bygrave 1a9a4c957c Fix for issue 49 - Query parsing error for SELECT something * 1.5 AS something_else 2014-01-16 21:48:27 +13:00
Rob Bygrave bd57a3b9c8 Merge pull request #65 from yuba/master
make Transaction interface extends Closeable
2014-01-16 00:18:33 -08:00
Rob Bygrave 21a71c3a4f Fix for Issue 62 - Query.setId should not return all results for null input Object 2014-01-16 21:10:13 +13:00
Rob Bygrave f66439a62e Fix for Issue 40 - Expr does not have a betweenProperties(...) method. 2014-01-16 21:02:19 +13:00
Rob Bygrave 73452a5f68 Fix for issue 68 - findRowCount sql error when using a @Formula property in the where clause 2014-01-16 20:36:23 +13:00
Rob Bygrave 5f4789f9ba Support heart beat timeout, tidy up free buffer 2014-01-16 00:28:00 +13:00
yuba eaac874ac0 make Transaction interface extends Closeable
To avoid method name conflict, JtaTransaction#close was renamed to
closeConnection()
2013-12-27 14:00:00 +09:00
Rob Bygrave 4900a460d1 Fix for DataSourcePool trim idle connections not firing frequently
enough
2013-12-14 21:24:26 +13:00
Rob Bygrave d16f33d26b Modified DataSource pool - cleanup of reset and statistics collection 2013-12-12 20:57:50 +13:00
Rob Bygrave 664f08fd6f [maven-release-plugin] prepare for next development iteration 2013-11-25 21:06:09 +13:00
Rob Bygrave 39416e933e [maven-release-plugin] prepare release avaje-ebeanorm-3.2.5 2013-11-25 21:06:00 +13:00
Rob Bygrave 74b118e8f0 Merge pull request #59 from andersha/db2
support db2 and SqlLimiter for db2
2013-11-21 03:19:10 -08:00
AndersHA 43853b0357 cleanup db2 platform 2013-11-20 13:54:01 +01:00
AndersHA f62bc9a35f support db2 and write sqllimiter for db2 2013-11-20 11:07:23 +01:00
Rob Bygrave 9d8fe97de0 Adjusted Fix for Issue 56 - Using findIterate with MySQL streams the
entire result set at once.  This makes the fix specific to the
DatabasePlatform and findIterate() or findVisit().
2013-11-07 23:58:15 +13:00
Rob Bygrave b896e55af1 Fix for Issue 56 - Using findIterate with MySQL streams the entire
result set at once
2013-11-07 23:07:39 +13:00
Rob Bygrave bd75f2cf77 Merge pull request #55 from rvowles/master
Add support for bang paths
2013-11-07 01:15:15 -08:00
Rob Bygrave 4f33fe087e Merge pull request #47 from romming/BUG420
Thanks!!
2013-11-07 00:08:54 -08:00
Rob Bygrave ccf94bf1a4 Merge pull request #51 from mollstam/limitbug
Invalid SQL when firstRow set but not maxRows
2013-11-06 23:16:08 -08:00
Richard Vowles 0acabf820e Add support for bang paths 2013-11-02 20:07:35 +13:00
Tobias 1330fe8fb6 Generate valid SQL when firstRow is set but not maxRows 2013-10-07 16:29:23 +02:00
Rob Bygrave 09ea5217dc [maven-release-plugin] prepare for next development iteration 2013-09-11 16:26:44 +12:00
Christian Romming 236fb5541b Fixed whitespace in test. 2013-08-28 23:15:20 +02:00
Christian Romming 1cafea20c6 Test and fix for BUG 420. 2013-08-28 23:06:37 +02:00
226 changed files with 6834 additions and 6334 deletions
+1 -1
View File
@@ -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>
+16 -2
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.4</version>
<version>3.3.1-RC2</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>
+8
View File
@@ -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.
*/
@@ -36,11 +36,6 @@ import java.util.Map;
*/
public interface ExpressionFactory {
/**
* Return the language for this expression factory.
*/
public String getLang();
/**
* Equal To - property equal to 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);
+20 -15
View File
@@ -24,7 +24,7 @@ import java.io.Serializable;
*
* <pre class="code">
* // Normal fetch join results in a single SQL query
* List&lt;Order&gt; list = Ebean.find(Order.class).join(&quot;details&quot;).findList();
* List&lt;Order&gt; list = Ebean.find(Order.class).fetch(&quot;details&quot;).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&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new JoinConfig().query())
* .fetch(&quot;customer&quot;, new JoinConfig().query(5))
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .fetch(&quot;customer&quot;, new FetchConfig().queryFirst(5))
* .findList();
*
* // query 1) find order
@@ -70,7 +70,7 @@ import java.io.Serializable;
* .select(&quot;status, shipDate&quot;)
* .fetch(&quot;details&quot;, &quot;quantity, price&quot;, new FetchConfig().query())
* .fetch(&quot;details.product&quot;, &quot;sku, name&quot;)
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().query(10))
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().queryFirst(5))
* .fetch(&quot;customer.contacts&quot;)
* .fetch(&quot;customer.shippingAddress&quot;)
* .findList();
@@ -96,13 +96,13 @@ import java.io.Serializable;
* <pre class="code">
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;customer&quot;, new FetchConfig().query(3).lazy(10))
* .fetch(&quot;customer&quot;, 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.
*/
+47 -22
View File
@@ -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();
+7 -25
View File
@@ -269,26 +269,6 @@ import java.util.Set;
*/
public interface Query<T> extends Serializable {
/**
* How this query should use (or not) the Lucene Index if one is defined for
* the bean type.
*/
public enum UseIndex {
NO, DEFAULT, YES_IDS, YES_OBJECTS
}
/**
* Explicitly specify how this query should use a Lucene Index if one is
* defined for this bean type.
*/
public Query<T> setUseIndex(UseIndex useIndex);
/**
* Return the setting for how this query should use a Lucene Index if one is
* defined for this bean type.
*/
public UseIndex getUseIndex();
/**
* Return the RawSql that was set to use for this query.
*/
@@ -684,7 +664,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 +689,7 @@ public interface Query<T> extends Serializable {
* // list (emptyList) will be empty ...
* List&lt;Order&gt; emtyList = query.findList();
* </pre>
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
@@ -1068,16 +1052,14 @@ public interface Query<T> extends Serializable {
*/
public String getGeneratedSql();
/**
* Return the total hits matched for a lucene text search query.
*/
public int getTotalHits();
/**
* executed the select with "for update" which should lock the record
* "on read"
*/
public Query<T> setForUpdate(boolean forUpdate);
/**
* Return true if this query has forUpdate set.
*/
public boolean isForUpdate();
}
@@ -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
@@ -34,13 +35,6 @@ public interface Transaction {
*/
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
// /**
// * You can call this after the transaction commit to wait for any changes to
// * Lucene indexes to be made and committed. Note that generally this could
// * be some time so it is not expected that you call this method generally.
// */
// public void waitForIndexUpdates();
/**
* Return true if this transaction is read only.
*/
@@ -6,7 +6,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.avaje.ebean.Query;
import com.avaje.ebean.Query.UseIndex;
/**
* Specify the default cache use specific entity type.
@@ -56,9 +55,4 @@ public @interface CacheStrategy {
*/
String warmingQuery() default "";
/**
* Default setting for using a text index if it has been defined on this bean
* type.
*/
UseIndex useIndex() default UseIndex.DEFAULT;
};
@@ -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"));
@@ -1,228 +0,0 @@
package com.avaje.ebean.meta;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import com.avaje.ebean.bean.ObjectGraphOrigin;
/**
* Statistics collected by AutoFetch profiling.
*/
@Entity
public class MetaAutoFetchStatistic implements Serializable {
private static final long serialVersionUID = -6640406753257176803L;
@Id
private String id;
private ObjectGraphOrigin origin;
private String beanType;
private int counter;
@Transient
private List<QueryStats> queryStats;
@Transient
private List<NodeUsageStats> nodeUsageStats;
public MetaAutoFetchStatistic() {
}
public MetaAutoFetchStatistic(ObjectGraphOrigin origin, int counter, List<QueryStats> queryStats,
List<NodeUsageStats> nodeUsageStats) {
this.origin = origin;
this.beanType = origin == null ? null : origin.getBeanType();
this.id = origin == null ? null : origin.getKey();
this.counter = counter;
this.queryStats = queryStats;
this.nodeUsageStats = nodeUsageStats;
}
/**
* This is the query point key.
*/
public String getId() {
return id;
}
/**
* Return the bean type.
*/
public String getBeanType() {
return beanType;
}
/**
* Return the query point.
*/
public ObjectGraphOrigin getOrigin() {
return origin;
}
/**
* Return the number of profiled queries the statistics is based on.
*/
public int getCounter() {
return counter;
}
/**
* Return the query execution statistics.
*/
public List<QueryStats> getQueryStats() {
return queryStats;
}
/**
* Return the node usage statistics.
*/
public List<NodeUsageStats> getNodeUsageStats() {
return nodeUsageStats;
}
/**
* FIXME: This will likely be deprecated in favour of a separate object graph
* cost.
*/
public static class QueryStats implements Serializable {
private static final long serialVersionUID = -5517935732867671387L;
private final String path;
private final int exeCount;
private final int totalBeanLoaded;
private final int totalMicros;
public QueryStats(String path, int exeCount, int totalBeanLoaded, int totalMicros) {
this.path = path;
this.exeCount = exeCount;
this.totalBeanLoaded = totalBeanLoaded;
this.totalMicros = totalMicros;
}
/**
* Return the path. This is empty string for the origin query and otherwise
* the path for the associated lazy loading queries.
*/
public String getPath() {
return path;
}
/**
* The number of queries executed.
*/
public int getExeCount() {
return exeCount;
}
/**
* The total number of beans loaded by the query.
*/
public int getTotalBeanLoaded() {
return totalBeanLoaded;
}
/**
* The total time in microseconds of the queries.
*/
public int getTotalMicros() {
return totalMicros;
}
public String toString() {
long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount;
return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded["
+ totalBeanLoaded + "] avgMicros[" + avgMicros + "] totalMicros[" + totalMicros
+ "]";
}
}
/**
* Collects usages statistics for a given node in the object graph.
*/
public static class NodeUsageStats implements Serializable {
private static final long serialVersionUID = 1786787832374844739L;
private final String path;
private final int profileCount;
private final int profileUsedCount;
private final String[] usedProperties;
public NodeUsageStats(String path, int profileCount, int profileUsedCount,
String[] usedProperties) {
this.path = path == null ? "" : path;
this.profileCount = profileCount;
this.profileUsedCount = profileUsedCount;
this.usedProperties = usedProperties;
}
/**
* Return the path. This is empty string for the origin and otherwise the
* path for the associated nodes.
*/
public String getPath() {
return path;
}
/**
* The number of profiled beans for this node.
*/
public int getProfileCount() {
return profileCount;
}
/**
* The number of profiled beans that where actually used for this node.
* <p>
* The difference between profiled and used could show uneven traversal of
* the object graph. UI paging through results means the traversal for the
* first x beans can be much higher than the last x beans.
* </p>
*/
public int getProfileUsedCount() {
return profileUsedCount;
}
/**
* The properties used at this node.
*/
public String[] getUsedProperties() {
return usedProperties;
}
/**
* Return the properties as a Set rather than an Array.
*/
public Set<String> getUsedPropertiesSet() {
LinkedHashSet<String> s = new LinkedHashSet<String>();
for (int i = 0; i < usedProperties.length; i++) {
s.add(usedProperties[i]);
}
return s;
}
public String toString() {
return "path[" + path + "] profileCount[" + profileCount + "] used[" + profileUsedCount
+ "] props" + Arrays.toString(usedProperties);
}
}
}
@@ -1,119 +0,0 @@
package com.avaje.ebean.meta;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import com.avaje.ebean.bean.ObjectGraphOrigin;
/**
* "Tuned fetch" information used by AutoFetch.
* <p>
* Note that the queryPoint is effectively the Id field for this bean.
* </p>
* <p>
* The queryPoint identifies both the query and call stack.
* </p>
*/
@Entity
public class MetaAutoFetchTunedQueryInfo implements Serializable {
private static final long serialVersionUID = 3119991928889170215L;
@Id
private String id;
private String beanType;
/**
* The profile query point (call stack and query).
*/
private ObjectGraphOrigin origin;
/**
* The tuned query details with joins and properties.
*/
private String tunedDetail;
/**
* The number of times profiling has been collected for this query point.
*/
private int profileCount;
/**
* The number of queries tuned by this info.
*/
private int tunedCount;
private long lastTuneTime;
public MetaAutoFetchTunedQueryInfo() {
}
public MetaAutoFetchTunedQueryInfo(final ObjectGraphOrigin origin, String tunedDetail,
int profileCount, int tunedCount, long lastTuneTime) {
this.origin = origin;
this.beanType = origin == null ? null : origin.getBeanType();
this.id = origin == null ? null : origin.getKey();
this.tunedDetail = tunedDetail;
this.profileCount = profileCount;
this.tunedCount = tunedCount;
this.lastTuneTime = lastTuneTime;
}
/**
* Return the query point key.
*/
public String getId() {
return id;
}
/**
* Return the type of bean this is tuned for.
*/
public String getBeanType() {
return beanType;
}
/**
* Return the query point.
*/
public ObjectGraphOrigin getOrigin() {
return origin;
}
/**
* The tuned query detail in string form.
*/
public String getTunedDetail() {
return tunedDetail;
}
/**
* The number of profiled queries the tuned query is based on.
*/
public int getProfileCount() {
return profileCount;
}
/**
* Return the number of queries tuned.
*/
public int getTunedCount() {
return tunedCount;
}
/**
* Return the time of the last tune (that changed the query).
*/
public long getLastTuneTime() {
return lastTuneTime;
}
public String toString() {
return "origin[" + origin + "] query[" + tunedDetail + "] profileCount[" + profileCount + "]";
}
}
@@ -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;
@@ -79,11 +80,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
SUBQUERY
}
/**
* Set total hits when querying against lucene.
*/
public void setTotalHits(int totalHits);
/**
* Return true if select all properties was used to ensure the property
@@ -153,11 +149,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 +349,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 +362,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 +376,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();
@@ -1,19 +1,14 @@
package com.avaje.ebeaninternal.server.autofetch;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.text.PathProperties.Props;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -53,30 +48,6 @@ public class Statistics implements Serializable {
}
}
public MetaAutoFetchStatistic createPublicMeta() {
synchronized (monitor) {
StatisticsQuery[] sourceQueryStats = queryStatsMap.values().toArray(new StatisticsQuery[queryStatsMap.size()]);
List<QueryStats> destQueryStats = new ArrayList<QueryStats>(sourceQueryStats.length);
// copy the query statistics
for (int i = 0; i < sourceQueryStats.length; i++) {
destQueryStats.add(sourceQueryStats[i].createPublicMeta());
}
StatisticsNodeUsage[] sourceNodeUsage = nodeUsageMap.values().toArray(new StatisticsNodeUsage[nodeUsageMap.size()]);
List<NodeUsageStats> destNodeUsage = new ArrayList<NodeUsageStats>(sourceNodeUsage.length);
// copy the node usage statistics
for (int i = 0; i < sourceNodeUsage.length; i++) {
destNodeUsage.add(sourceNodeUsage[i].createPublicMeta());
}
return new MetaAutoFetchStatistic(origin, counter, destQueryStats, destNodeUsage);
}
}
/**
* Return the number of times the root query has executed.
* <p>
@@ -127,7 +98,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();
@@ -5,16 +5,16 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Collects usages statistics for a given node in the object graph.
@@ -44,13 +44,6 @@ public class StatisticsNodeUsage implements Serializable {
this.queryTuningAddVersion = queryTuningAddVersion;
}
public NodeUsageStats createPublicMeta() {
synchronized(monitor){
String[] usedProps = aggregateUsed.toArray(new String[aggregateUsed.size()]);
return new NodeUsageStats(path, profileCount, profileUsedCount, usedProps);
}
}
public void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc) {
synchronized(monitor){
@@ -2,8 +2,6 @@ package com.avaje.ebeaninternal.server.autofetch;
import java.io.Serializable;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
/**
* Used to accumulate query execution statistics.
*/
@@ -13,21 +11,17 @@ 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;
}
public QueryStats createPublicMeta() {
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;
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.autofetch;
import java.io.Serializable;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.meta.MetaAutoFetchTunedQueryInfo;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
@@ -73,13 +72,6 @@ public class TunedQueryInfo implements Serializable {
}
}
/**
* Create a copy of this tuned fetch data for public consumption.
*/
public MetaAutoFetchTunedQueryInfo createPublicMeta() {
return new MetaAutoFetchTunedQueryInfo(origin, tunedDetail.toString(), profileCount, tunedCount, lastTuneTime);
}
/**
* Set the number of times profiling has been collected for this query
* point.
@@ -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.
*/
@@ -8,8 +8,9 @@ import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
@@ -22,8 +23,6 @@ import com.avaje.ebean.event.ServerConfigStartup;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
@@ -33,8 +32,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
private ArrayList<Class<?>> xmlBeanList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> embeddableList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> entityList = new ArrayList<Class<?>>();
@@ -55,8 +52,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> luceneIndexList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
@@ -75,7 +70,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
}
private BootupClasses(BootupClasses parent) {
this.xmlBeanList.addAll(parent.xmlBeanList);
this.embeddableList.addAll(parent.embeddableList);
this.entityList.addAll(parent.entityList);
this.scalarTypeList.addAll(parent.scalarTypeList);
@@ -86,7 +80,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
this.beanFinderList.addAll(parent.beanFinderList);
this.beanListenerList.addAll(parent.beanListenerList);
this.beanQueryAdapterList.addAll(parent.beanQueryAdapterList);
this.luceneIndexList.addAll(parent.luceneIndexList);
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
}
@@ -304,13 +297,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
return beanListenerList;
}
/**
* Return the list of XML Beans.
*/
public ArrayList<Class<?>> getXmlBeanList() {
return xmlBeanList;
}
public void add(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> clazz = it.next();
@@ -325,11 +311,7 @@ public class BootupClasses implements ClassPathSearchMatcher {
} else if (isEntity(cls)) {
entityList.add(cls);
} else if (isXmlBean(cls)){
entityList.add(cls);
//xmlBeanList.add(cls);
} else if (isInterestingInterface(cls)) {
return true;
@@ -419,18 +401,4 @@ public class BootupClasses implements ClassPathSearchMatcher {
}
return false;
}
private boolean isXmlBean(Class<?> cls) {
Annotation ann = cls.getAnnotation(XmlRootElement.class);
if (ann != null) {
return true;
}
ann = cls.getAnnotation(XmlType.class);
if (ann != null) {
// Only looking for Beans and not Enums
return !cls.isEnum();
}
return false;
}
}
@@ -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;
@@ -454,7 +490,7 @@ public final class DefaultServer implements SpiEbeanServer {
}
/**
* Compile a query. Only valid for ORM queries (not LDAP)
* Compile a query. Only valid for ORM queries.
*/
public <T> CQuery<T> compileQuery(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> qr = createQueryRequest(Type.SUBQUERY, query, t);
@@ -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
@@ -78,10 +80,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
graphContext.registerSecondaryQueries(query);
}
public void setTotalHits(int totalHits) {
query.setTotalHits(totalHits);
}
public void executeSecondaryQueries(int defaultQueryBatch) {
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
}
@@ -332,7 +330,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 +354,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 +380,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,8 +15,10 @@ 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;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.annotation.ConcurrencyMode;
@@ -33,8 +35,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 +64,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 +78,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>();
@@ -93,7 +96,7 @@ public class BeanDescriptor<T> {
private final ConcurrentHashMap<String, BeanFkeyProperty> fkeyMap = new ConcurrentHashMap<String, BeanFkeyProperty>();
public enum EntityType {
ORM, EMBEDDED, SQL, META, XMLELEMENT
ORM, EMBEDDED, SQL
}
/**
@@ -102,7 +105,7 @@ public class BeanDescriptor<T> {
private final String serverName;
/**
* Set to true if this is a LDAP domain object.
* The nature/type of this bean.
*/
private final EntityType entityType;
@@ -342,12 +345,9 @@ public class BeanDescriptor<T> {
private final String defaultSelectClause;
private final Set<String> defaultSelectClauseSet;
private final String[] defaultSelectDbArray;
private final String descriptorId;
private final UseIndex useIndex;
private SpiEbeanServer ebeanServer;
private ServerCache beanCache;
@@ -368,7 +368,6 @@ public class BeanDescriptor<T> {
this.fullName = InternString.intern(deploy.getFullName());
this.descriptorId = descriptorId;
this.useIndex = deploy.getUseIndex();
this.typeManager = typeManager;
this.beanType = deploy.getBeanType();
this.factoryType = deploy.getFactoryType();
@@ -386,7 +385,6 @@ public class BeanDescriptor<T> {
this.defaultSelectClause = deploy.getDefaultSelectClause();
this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause);
this.defaultSelectDbArray = deploy.getDefaultSelectDbArray(defaultSelectClauseSet);
this.idType = deploy.getIdType();
this.idGenerator = deploy.getIdGenerator();
@@ -565,14 +563,6 @@ public class BeanDescriptor<T> {
return entityType;
}
/**
* Return the default strategy for using a lucene index (if an index is
* defined on this bean type).
*/
public UseIndex getUseIndex() {
return useIndex;
}
/**
* Initialise the Id properties first.
* <p>
@@ -804,13 +794,6 @@ public class BeanDescriptor<T> {
return defaultSelectClauseSet;
}
/**
* For LDAP return array of (DB) attributes to include in query by default.
*/
public String[] getDefaultSelectDbArray() {
return defaultSelectDbArray;
}
/**
* Return true if this object is the root level object in its entity
* inheritance.
@@ -854,8 +837,7 @@ public class BeanDescriptor<T> {
}
/**
* Return true if there is L2 caching (Lucene or Bean cache) for this bean
* type.
* Return true if there is L2 bean caching for this bean type.
*/
public boolean isUsingL2Cache() {
return isBeanCaching();
@@ -966,11 +948,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 +1134,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 +1184,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);
}
@@ -2375,13 +2381,6 @@ public class BeanDescriptor<T> {
@SuppressWarnings("unchecked")
private T createJsonBean() {
if (EntityType.XMLELEMENT.equals(entityType)) {
try {
return beanType.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return (T)createEntityBean();
}
@@ -2445,5 +2444,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());
}
}
}
@@ -106,9 +106,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final BeanManagerFactory beanManagerFactory;
private int enhancedClassCount;
private int subclassClassCount;
private final HashSet<String> subclassedEntities = new HashSet<String>();
private final boolean updateChangesOnly;
private final BootupClasses bootupClasses;
@@ -447,26 +445,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]");
}
/**
* Log Warning if mixing subclass and enhancement.
* <p>
* If enhancement is used for some classes it is expected to be used for all
* and vice versa.
* </p>
*/
private void logStatus() {
String msg = "Entities enhanced[" + enhancedClassCount + "] subclassed[" + subclassClassCount + "]";
logger.info(msg);
if (enhancedClassCount > 0) {
if (subclassClassCount > 0) {
String subclassEntityNames = subclassedEntities.toString();
String m = "Mixing enhanced and subclassed entities. Subclassed classes:" + subclassEntityNames;
logger.warn(m);
}
}
logger.info("Entities enhanced[" + enhancedClassCount + "]");
}
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
@@ -1462,25 +1442,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private void checkEnhanced(DeployBeanDescriptor<?> desc, Class<?> beanClass) {
// the bean already implements EntityBean
checkInheritedClasses(true, beanClass);
desc.setFactoryType(beanClass);
if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) {
enhancedClassCount++;
}
enhancedClassCount++;
}
private void checkSubclass(DeployBeanDescriptor<?> desc, Class<?> beanClass) {
checkInheritedClasses(false, beanClass);
desc.checkReadAndWriteMethods();
EntityType entityType = desc.getEntityType();
if (EntityType.XMLELEMENT.equals(entityType)) {
desc.setFactoryType(beanClass);
} else {
throw new PersistenceException("Entity type "+beanClass+" is not an enhanced entity bean. Subclassing is not longer supported in Ebean");
}
throw new PersistenceException("Entity type "+beanClass+" is not an enhanced entity bean. Subclassing is not longer supported in Ebean");
}
/**
@@ -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();
}
@@ -1043,19 +1049,6 @@ public class BeanProperty implements ElPropertyValue {
return value;
}
// private ArrayList<LuceneIndex> luceneIndexes;
//
// public void registerLuceneIndex(LuceneIndex luceneIndex) {
// if (luceneIndexes == null) {
// luceneIndexes = new ArrayList<LuceneIndex>();
// }
// luceneIndexes.add(luceneIndex);
// }
//
// public boolean isDeltaRequired() {
// return true;//luceneIndexes != null;
// }
/**
* Return true if by default this property is set to fetch eager.
* Lob's usually default to fetch lazy.
@@ -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.
*/
@@ -6,9 +6,6 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -21,172 +18,156 @@ import com.avaje.ebeaninternal.server.type.DataBind;
*/
public interface IdBinder {
/**
* Initialise the binder.
*/
public void initialise();
/**
* Convert the Id value as a Lucene string term.
*/
public String writeTerm(Object idValue);
/**
* Initialise the binder.
*/
public void initialise();
/**
* Convert the Lucene string term into an Id value.
*/
public Object readTerm(String idTermValue);
/**
* Write the Id value to binary DataOuput.
*/
public void writeData(DataOutput dataOutput, Object idValue) throws IOException;
/**
* Write the Id value to binary DataOuput.
*/
public void writeData(DataOutput dataOutput, Object idValue) throws IOException;
/**
* Read the Id value from the binary DataInput.
*/
public Object readData(DataInput dataInput) throws IOException;
/**
* Read the Id value from the binary DataInput.
*/
public Object readData(DataInput dataInput) throws IOException;
/**
* Return the name(s) of the Id property(s). Comma delimited if there is more
* than one.
* <p>
* This can be used to include in a query.
* </p>
*/
public String getIdProperty();
/**
* Adds RDN's to the LdapName using the id value.
*/
public void createLdapNameById(LdapName name, Object id) throws InvalidNameException;
/**
* Find a BeanProperty that is mapped to the database column.
*/
public BeanProperty findBeanProperty(String dbColumnName);
/**
* Adds RDN's to the LdapName using the id value from the bean.
*/
public void createLdapNameByBean(LdapName name, Object bean) throws InvalidNameException;
/**
* Return the number of scalar properties for this id.
*/
public int getPropertyCount();
/**
* Return the name(s) of the Id property(s).
* Comma delimited if there is more than one.
* <p>
* This can be used to include in a query.
* </p>
*/
public String getIdProperty();
/**
* Return false if the id is a simple scalar and false if it is embedded or
* concatenated.
*/
public boolean isComplexId();
/**
* Find a BeanProperty that is mapped to the database column.
*/
public BeanProperty findBeanProperty(String dbColumnName);
/**
* Return the number of scalar properties for this id.
*/
public int getPropertyCount();
/**
* Return false if the id is a simple scalar and false if it is embedded or concatenated.
*/
public boolean isComplexId();
/**
* Return the default order by that may need to be used if the query includes a many property.
*/
public String getDefaultOrderBy();
public String getOrderBy(String pathPrefix, boolean ascending);
/**
* Return the values as an array of scalar bindable values.
* <p>
* For concatenated keys that use an Embedded bean or multiple id properties
* this determines the field values are returns them as an Object array.
* </p>
* <p>
* Added primarily for Query.addWhere().add(Expr.idEq()) support.
* </p>
*/
public Object[] getBindValues(Object idValue);
/**
* Return the id values for a given bean.
*/
public Object[] getIdValues(Object bean);
/**
* Build a string of the logical expressions.
* <p>
* Typically used to build a id = ? string.
* </p>
*/
public String getAssocOneIdExpr(String prefix, String operator);
/**
* Return the logical id in expression taking into account embedded id's.
*/
public String getAssocIdInExpr(String prefix);
/**
* Return the default order by that may need to be used if the query includes
* a many property.
*/
public String getDefaultOrderBy();
/**
* Binds an id value to a prepared statement.
*/
public void bindId(DataBind dataBind, Object value) throws SQLException;
public String getOrderBy(String pathPrefix, boolean ascending);
/**
* Bind the id value to a SqlUpdate statement.
*/
public void bindId(DefaultSqlUpdate sqlUpdate, Object value);
public void addIdInBindValue(SpiExpressionRequest request, Object value);
/**
* Return the sql for binding the id using an IN clause.
*/
public String getBindIdInSql(String baseTableAlias);
/**
* Return the binding expression (like "?" or "(?,?)")for the Id.
*/
public String getIdInValueExpr(int size);
/**
* Same as getIdInValueExpr but for delete by id.
*/
public String getIdInValueExprDelete(int size);
public void buildSelectExpressionChain(String prefix, List<String> selectChain);
/**
* Read the id value from the result set and set it to the bean also returning it.
*/
public Object readSet(DbReadContext ctx, Object bean) throws SQLException;
/**
* Return the values as an array of scalar bindable values.
* <p>
* For concatenated keys that use an Embedded bean or multiple id properties
* this determines the field values are returns them as an Object array.
* </p>
* <p>
* Added primarily for Query.addWhere().add(Expr.idEq()) support.
* </p>
*/
public Object[] getBindValues(Object idValue);
/**
* Ignore the appropriate number of scalar properties for this id.
*/
public void loadIgnore(DbReadContext ctx);
/**
* Read the id value from the result set and return it.
*/
public Object read(DbReadContext ctx) throws SQLException;
/**
* Return the id values for a given bean.
*/
public Object[] getIdValues(Object bean);
/**
* Append to the select clause.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery);
/**
* Return the sql for binding the id to. This includes table alias and columns that make up the id.
*/
public String getBindIdSql(String baseTableAlias);
/**
* Return the id properties in flat form.
*/
public BeanProperty[] getProperties();
/**
* Cast or convert the Id value if necessary and optionally set it.
* <p>
* The Id value is not assumed to be the correct type so it is converted to
* the correct type. Typically this is because we could get a Integer, Long
* or BigDecimal depending on the JDBC driver and situation.
* </p>
* <p>
* If the bean is not null, then the value is set to the bean.
* </p>
*/
public Object convertSetId(Object idValue, Object bean);
/**
* Build a string of the logical expressions.
* <p>
* Typically used to build a id = ? string.
* </p>
*/
public String getAssocOneIdExpr(String prefix, String operator);
/**
* Return the logical id in expression taking into account embedded id's.
*/
public String getAssocIdInExpr(String prefix);
/**
* Binds an id value to a prepared statement.
*/
public void bindId(DataBind dataBind, Object value) throws SQLException;
/**
* Bind the id value to a SqlUpdate statement.
*/
public void bindId(DefaultSqlUpdate sqlUpdate, Object value);
public void addIdInBindValue(SpiExpressionRequest request, Object value);
/**
* Return the sql for binding the id using an IN clause.
*/
public String getBindIdInSql(String baseTableAlias);
/**
* Return the binding expression (like "?" or "(?,?)")for the Id.
*/
public String getIdInValueExpr(int size);
/**
* Same as getIdInValueExpr but for delete by id.
*/
public String getIdInValueExprDelete(int size);
public void buildSelectExpressionChain(String prefix, List<String> selectChain);
/**
* Read the id value from the result set and set it to the bean also returning
* it.
*/
public Object readSet(DbReadContext ctx, Object bean) throws SQLException;
/**
* Ignore the appropriate number of scalar properties for this id.
*/
public void loadIgnore(DbReadContext ctx);
/**
* Read the id value from the result set and return it.
*/
public Object read(DbReadContext ctx) throws SQLException;
/**
* Append to the select clause.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery);
/**
* Return the sql for binding the id to. This includes table alias and columns
* that make up the id.
*/
public String getBindIdSql(String baseTableAlias);
/**
* Return the id properties in flat form.
*/
public BeanProperty[] getProperties();
/**
* Cast or convert the Id value if necessary and optionally set it.
* <p>
* The Id value is not assumed to be the correct type so it is converted to
* the correct type. Typically this is because we could get a Integer, Long or
* BigDecimal depending on the JDBC driver and situation.
* </p>
* <p>
* If the bean is not null, then the value is set to the bean.
* </p>
*/
public Object convertSetId(Object idValue, Object bean);
}
@@ -6,11 +6,6 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -99,23 +94,6 @@ public final class IdBinderEmbedded implements IdBinder {
return sb.toString();
}
public void createLdapNameById(LdapName name, Object id) throws InvalidNameException {
for (int i = 0; i < props.length; i++) {
Object v = props[i].getValue(id);
Rdn rdn = new Rdn(props[i].getDbColumn(), v);
name.add(rdn);
}
}
public void createLdapNameByBean(LdapName name, Object bean) throws InvalidNameException {
Object id = embIdProperty.getValue(bean);
createLdapNameById(name, id);
}
public BeanDescriptor<?> getIdBeanDescriptor() {
return idDesc;
}
@@ -228,42 +206,6 @@ public final class IdBinderEmbedded implements IdBinder {
return idInValueSql;
}
/**
* Convert the lucene string term value into embedded id.
*/
public Object readTerm(String idTermValue) {
String[] split = idTermValue.split("|");
if (split.length != props.length){
String msg = "Failed to split ["+idTermValue+"] using | for id.";
throw new PersistenceException(msg);
}
Object embId = idDesc.createBean();
for (int i = 0; i < props.length; i++) {
Object v = props[i].getScalarType().parse(split[i]);
props[i].setValue(embId, v);
}
return embId;
}
/**
* Write the embedded id as a Lucene string term value.
*/
public String writeTerm(Object idValue) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < props.length; i++) {
Object v = props[i].getValue(idValue);
String formatValue = props[i].getScalarType().format(v);
if (i > 0){
sb.append("|");
}
sb.append(formatValue);
}
return sb.toString();
}
public Object[] getIdValues(Object bean) {
bean = embIdProperty.getValue(bean);
Object[] bindvalues = new Object[props.length];
@@ -6,9 +6,6 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -21,132 +18,118 @@ import com.avaje.ebeaninternal.server.type.DataBind;
*/
public final class IdBinderEmpty implements IdBinder {
private static final String bindIdSql = "";
private static final BeanProperty[] properties = new BeanProperty[0];
public IdBinderEmpty() {
private static final String bindIdSql = "";
}
public void initialise(){
}
public Object readTerm(String idTermValue) {
return null;
}
public String writeTerm(Object idValue) {
return null;
}
public String getOrderBy(String pathPrefix, boolean ascending){
return pathPrefix;
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
}
public void createLdapNameById(LdapName name, Object id) throws InvalidNameException {
}
public void createLdapNameByBean(LdapName name, Object bean) throws InvalidNameException {
}
public int getPropertyCount() {
return 0;
}
public String getIdProperty() {
return null;
}
private static final BeanProperty[] properties = new BeanProperty[0];
public BeanProperty findBeanProperty(String dbColumnName) {
return null;
}
public boolean isComplexId(){
return true;
}
public IdBinderEmpty() {
}
public String getDefaultOrderBy() {
// this should never happen?
return "";
}
public BeanProperty[] getProperties() {
return properties;
}
public void initialise() {
public String getBindIdSql(String baseTableAlias) {
return bindIdSql;
}
public String getAssocOneIdExpr(String prefix, String operator){
return null;
}
public String getAssocIdInExpr(String prefix) {
return null;
}
public void addIdInBindValue(SpiExpressionRequest request, Object value) {
}
public String getIdInValueExprDelete(int size) {
return getIdInValueExpr(size);
}
public String getIdInValueExpr(int size) {
return "";
}
}
public String getBindIdInSql(String baseTableAlias) {
return null;
}
public Object[] getIdValues(Object bean){
return null;
}
public Object[] getBindValues(Object idValue){
return new Object[]{idValue};
}
public void bindId(DefaultSqlUpdate sqlUpdate, Object value) {
}
public void bindId(DataBind dataBind, Object value) throws SQLException {
}
public void loadIgnore(DbReadContext ctx) {
}
public String getOrderBy(String pathPrefix, boolean ascending) {
return pathPrefix;
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
}
public int getPropertyCount() {
return 0;
}
public String getIdProperty() {
return null;
}
public BeanProperty findBeanProperty(String dbColumnName) {
return null;
}
public boolean isComplexId() {
return true;
}
public String getDefaultOrderBy() {
// this should never happen?
return "";
}
public BeanProperty[] getProperties() {
return properties;
}
public String getBindIdSql(String baseTableAlias) {
return bindIdSql;
}
public String getAssocOneIdExpr(String prefix, String operator) {
return null;
}
public String getAssocIdInExpr(String prefix) {
return null;
}
public void addIdInBindValue(SpiExpressionRequest request, Object value) {
}
public String getIdInValueExprDelete(int size) {
return getIdInValueExpr(size);
}
public String getIdInValueExpr(int size) {
return "";
}
public String getBindIdInSql(String baseTableAlias) {
return null;
}
public Object[] getIdValues(Object bean) {
return null;
}
public Object[] getBindValues(Object idValue) {
return new Object[] { idValue };
}
public void bindId(DefaultSqlUpdate sqlUpdate, Object value) {
}
public void bindId(DataBind dataBind, Object value) throws SQLException {
}
public void loadIgnore(DbReadContext ctx) {
}
public Object readSet(DbReadContext ctx, Object bean) throws SQLException {
return null;
}
public Object read(DbReadContext ctx) throws SQLException {
return null;
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
}
public Object convertSetId(Object idValue, Object bean) {
return idValue;
}
public Object readData(DataInput dataOutput) throws IOException {
return null;
}
public void writeData(DataOutput dataOutput, Object idValue) throws IOException {
}
public Object readSet(DbReadContext ctx, Object bean) throws SQLException {
return null;
}
public Object read(DbReadContext ctx) throws SQLException {
return null;
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
}
public Object convertSetId(Object idValue, Object bean){
return idValue;
}
public Object readData(DataInput dataOutput) throws IOException {
return null;
}
public void writeData(DataOutput dataOutput, Object idValue) throws IOException {
}
}
@@ -8,9 +8,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
@@ -83,24 +80,6 @@ public final class IdBinderMultiple implements IdBinder {
return sb.toString();
}
public void createLdapNameById(LdapName name, Object id) throws InvalidNameException {
if (id instanceof Map<?,?> == false){
throw new RuntimeException("Expecting a Map for concatinated key");
}
Map<?,?> mapId = (Map<?,?>)id;
for (int i = 0; i < props.length; i++) {
Object v = mapId.get(props[i].getName());
if (v == null){
throw new RuntimeException("No value in Map for key "+props[i].getName());
}
Rdn rdn = new Rdn(props[i].getDbColumn(), v);
name.add(rdn);
}
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
@@ -109,19 +88,11 @@ public final class IdBinderMultiple implements IdBinder {
}
}
public void createLdapNameByBean(LdapName name, Object bean) throws InvalidNameException {
for (int i = 0; i < props.length; i++) {
Object v = props[i].getValue(bean);
Rdn rdn = new Rdn(props[i].getDbColumn(), v);
name.add(rdn);
}
}
public int getPropertyCount() {
return props.length;
}
public int getPropertyCount() {
return props.length;
}
public String getIdProperty() {
return idProperties;
@@ -229,40 +200,6 @@ public final class IdBinderMultiple implements IdBinder {
}
}
public Object readTerm(String idTermValue) {
String[] split = idTermValue.split("|");
if (split.length != props.length){
String msg = "Failed to split ["+idTermValue+"] using | for id.";
throw new PersistenceException(msg);
}
Map<String, Object> uidMap = new LinkedHashMap<String, Object>();
for (int i = 0; i < props.length; i++) {
Object v = props[i].getScalarType().parse(split[i]);
uidMap.put(props[i].getName(), v);
}
return uidMap;
}
@SuppressWarnings("unchecked")
public String writeTerm(Object idValue) {
Map<String, ?> uidMap = (Map<String, ?>) idValue;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < props.length; i++) {
Object v = uidMap.get(props[i].getName());
String formatValue = props[i].getScalarType().format(v);
if (i > 0){
sb.append("|");
}
sb.append(formatValue);
}
return sb.toString();
}
public Object readData(DataInput dataInput) throws IOException {
LinkedHashMap<String,Object> map = new LinkedHashMap<String, Object>();
@@ -6,10 +6,6 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.core.InternString;
@@ -48,14 +44,6 @@ public final class IdBinderSimple implements IdBinder {
// do nothing
}
public Object readTerm(String idTermValue) {
return scalarType.parse(idTermValue);
}
public String writeTerm(Object idValue) {
return scalarType.format(idValue);
}
public String getOrderBy(String pathPrefix, boolean ascending){
StringBuilder sb = new StringBuilder();
@@ -73,16 +61,6 @@ public final class IdBinderSimple implements IdBinder {
idProperty.buildSelectExpressionChain(prefix, selectChain);
}
public void createLdapNameById(LdapName name, Object id) throws InvalidNameException {
Rdn rdn = new Rdn(idProperty.getDbColumn(), id);
name.add(rdn);
}
public void createLdapNameByBean(LdapName name, Object bean) throws InvalidNameException {
Object id = idProperty.getValue(bean);
createLdapNameById(name, id);
}
/**
* Returns 1.
@@ -13,7 +13,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.Query.UseIndex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.IdGenerator;
@@ -22,7 +24,6 @@ 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.MetaAutoFetchStatistic;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
@@ -34,8 +35,6 @@ import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Describes Beans including their deployment information.
@@ -58,8 +57,6 @@ public class DeployBeanDescriptor<T> {
private static final Logger logger = LoggerFactory.getLogger(DeployBeanDescriptor.class);
private static final String META_BEAN_PREFIX = MetaAutoFetchStatistic.class.getName().substring(0, 20);
/**
* Map of BeanProperty Linked so as to preserve order.
*/
@@ -151,8 +148,6 @@ public class DeployBeanDescriptor<T> {
*/
private BeanFinder<T> beanFinder;
private UseIndex useIndex;
/**
* The table joins for this bean. Server side only.
*/
@@ -181,20 +176,6 @@ public class DeployBeanDescriptor<T> {
return Modifier.isAbstract(beanType.getModifiers());
}
/**
* Return the default UseIndex strategy.
*/
public UseIndex getUseIndex() {
return useIndex;
}
/**
* Set the default UseIndex strategy.
*/
public void setUseIndex(UseIndex useIndex) {
this.useIndex = useIndex;
}
public boolean isScalaObject() {
Class<?>[] interfaces = beanType.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
@@ -247,9 +228,6 @@ public class DeployBeanDescriptor<T> {
*/
public boolean checkReadAndWriteMethods() {
if (isMeta()) {
return true;
}
boolean missingMethods = false;
Iterator<DeployBeanProperty> it = propMap.values().iterator();
@@ -289,22 +267,11 @@ public class DeployBeanDescriptor<T> {
public EntityType getEntityType() {
if (entityType == null) {
entityType = isMeta() ? EntityType.META : EntityType.ORM;
entityType = EntityType.ORM;
}
return entityType;
}
/**
* Return true if this is a Meta entity bean.
* <p>
* The Meta entity beans are not based on real tables but get meta information
* from memory such as all the entity bean meta data.
* </p>
*/
private boolean isMeta() {
return beanType.getName().startsWith(META_BEAN_PREFIX);
}
public void add(DRawSqlMeta rawSqlMeta) {
rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta);
if ("default".equals(rawSqlMeta.getName())) {
@@ -788,26 +755,6 @@ public class DeployBeanDescriptor<T> {
return selectClause.substring(0, selectClause.length() - 1);
}
/**
* Return an Array of properties to include in default fetch (for LDAP).
*/
public String[] getDefaultSelectDbArray(Set<String> defaultSelect) {
ArrayList<String> list = new ArrayList<String>();
for (DeployBeanProperty p : propMap.values()) {
if (defaultSelect != null) {
if (defaultSelect.contains(p.getName())) {
// properties in defaultSelect
list.add(p.getDbColumn());
}
} else if (!p.isTransient() && p.isDbRead()) {
// non transient db properties
list.add(p.getDbColumn());
}
}
return list.toArray(new String[list.size()]);
}
/**
* Parse the include separating by comma or semicolon.
*/
@@ -6,10 +6,7 @@ import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.avaje.ebean.Query.UseIndex;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.annotation.EntityConcurrencyMode;
import com.avaje.ebean.annotation.NamedUpdate;
@@ -54,18 +51,6 @@ public class AnnotationClass extends AnnotationParser {
}
}
private boolean isXmlElement(Class<?> cls) {
XmlRootElement rootElement = cls.getAnnotation(XmlRootElement.class);
if (rootElement != null) {
return true;
}
XmlType xmlType = cls.getAnnotation(XmlType.class);
if (xmlType != null) {
return true;
}
return false;
}
private void read(Class<?> cls) {
Entity entity = cls.getAnnotation(Entity.class);
@@ -77,9 +62,6 @@ public class AnnotationClass extends AnnotationParser {
} else {
descriptor.setName(entity.name());
}
} else if (isXmlElement(cls)) {
descriptor.setName(cls.getSimpleName());
descriptor.setEntityType(EntityType.XMLELEMENT);
}
Embeddable embeddable = cls.getAnnotation(Embeddable.class);
@@ -152,11 +134,6 @@ public class AnnotationClass extends AnnotationParser {
cacheOptions.setNaturalKey(propName);
}
}
if (!UseIndex.DEFAULT.equals(cacheStrategy.useIndex())) {
// a specific text index strategy has been defined
descriptor.setUseIndex(cacheStrategy.useIndex());
}
}
private void readNamedQueries(NamedQueries namedQueries) {
@@ -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.
*/
@@ -14,44 +14,39 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
*/
public abstract class AbstractExpression implements SpiExpression {
private static final long serialVersionUID = 4072786211853856174L;
protected final String propName;
protected final FilterExprPath pathPrefix;
protected AbstractExpression(FilterExprPath pathPrefix, String propName) {
this.pathPrefix = pathPrefix;
this.propName = propName;
}
private static final long serialVersionUID = 4072786211853856174L;
public String getPropertyName() {
if (pathPrefix == null){
return propName;
} else {
String path = pathPrefix.getPath();
if (path == null || path.length() == 0){
return propName;
} else {
return path+"."+propName;
}
}
}
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
protected final String propName;
String propertyName = getPropertyName();
if (propertyName != null){
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName);
if (elProp != null && elProp.containsMany()){
manyWhereJoin.add(elProp);
}
}
}
protected ElPropertyValue getElProp(SpiExpressionRequest request) {
protected AbstractExpression(String propName) {
this.propName = propName;
}
String propertyName = getPropertyName();
return request.getBeanDescriptor().getElGetValue(propertyName);
public String getPropertyName() {
return propName;
}
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
String propertyName = getPropertyName();
if (propertyName != null) {
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName);
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);
}
}
}
}
protected ElPropertyValue getElProp(SpiExpressionRequest request) {
String propertyName = getPropertyName();
return request.getBeanDescriptor().getElGetValue(propertyName);
}
}
@@ -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;
@@ -18,24 +20,12 @@ class AllEqualsExpression implements SpiExpression {
private final Map<String, Object> propMap;
private final FilterExprPath pathPrefix;
AllEqualsExpression(FilterExprPath pathPrefix, Map<String, Object> propMap) {
this.pathPrefix = pathPrefix;
AllEqualsExpression(Map<String, Object> propMap) {
this.propMap = propMap;
}
protected String name(String propName) {
if (pathPrefix == null) {
return propName;
} else {
String path = pathPrefix.getPath();
if (path == null || path.length() == 0) {
return propName;
} else {
return path + "." + propName;
}
}
return propName;
}
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
@@ -105,29 +95,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;
@@ -14,8 +15,8 @@ class BetweenExpression extends AbstractExpression {
private final Object valueLow;
BetweenExpression(FilterExprPath pathPrefix, String propertyName, Object valLo, Object valHigh) {
super(pathPrefix, propertyName);
BetweenExpression(String propertyName, Object valLo, Object valHigh) {
super(propertyName);
this.valueLow = valLo;
this.valueHigh = valHigh;
}
@@ -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() {
@@ -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;
@@ -9,8 +10,6 @@ import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
/**
* Between expression where a value is between two properties.
*
* @author rbygrave
*/
class BetweenPropertyExpression implements SpiExpression {
@@ -18,29 +17,18 @@ class BetweenPropertyExpression implements SpiExpression {
private static final String BETWEEN = " between ";
private final FilterExprPath pathPrefix;
private final String lowProperty;
private final String highProperty;
private final Object value;
BetweenPropertyExpression(FilterExprPath pathPrefix, String lowProperty, String highProperty, Object value) {
this.pathPrefix = pathPrefix;
BetweenPropertyExpression(String lowProperty, String highProperty, Object value) {
this.lowProperty = lowProperty;
this.highProperty = highProperty;
this.value = value;
}
protected String name(String propName) {
if (pathPrefix == null) {
return propName;
} else {
String path = pathPrefix.getPath();
if (path == null || path.length() == 0) {
return propName;
} else {
return path + "." + propName;
}
}
return propName;
}
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
@@ -65,15 +53,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() {
@@ -1,17 +1,18 @@
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;
private final String value;
CaseInsensitiveEqualExpression(FilterExprPath pathPrefix, String propertyName, String value) {
super(pathPrefix, propertyName);
CaseInsensitiveEqualExpression(String propertyName, String value) {
super(propertyName);
this.value = value.toLowerCase();
}
@@ -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() {

Some files were not shown because too many files have changed in this diff Show More