Compare commits

..
Author SHA1 Message Date
Rob Bygrave e4306ce1ab [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1 2014-04-02 21:59:46 +13:00
Rob Bygrave 444b656780 Update pom to 3.3.1 2014-04-02 21:58:13 +13:00
Rob Bygrave fcbba23441 Test case for #83 - Bug: Stateless update cascading to OneToOne or
ManyToOne incorrectly tries to INSERT rather than UPDATE
2014-04-02 21:44:41 +13:00
Rob Bygrave 18961047a1 Fix for #80 - Exposing discriminator value in entity bean throws
exception when updating or deleting
2014-03-31 22:49:44 +13:00
Rob Bygrave 34347fc3e1 Fix for #77 - NPE when lazy loading on a OneToMany that is not a leaf 2014-03-31 22:06:10 +13:00
Rob Bygrave 189e0a2ea9 Part of fix for #79 - moving deactivate() and notifyCommit() into finally block 2014-03-21 21:54:47 +13:00
Rob Bygrave 182e027521 Further changes for #79 - returning/cleanup of bad connection 2014-03-21 02:42:41 +13:00
Rob Bygrave f85fa2c21d Fix for #79 - Rollback in case of connection closed - deactivate() not called when connection.rollback() fails 2014-03-20 20:14:14 +13:00
Rob Bygrave 5a5f479568 Merge pull request #81 from rvowles/master
assume that if includes are specified then turn default includes off
2014-03-16 15:02:28 +13:00
Rob Bygrave 9ca2486c9c Change defaultJarMatch back to default to false (as per previous release) 2014-03-16 14:31:21 +13:00
Richard Vowles 656b239925 assume that if includes are specified then turn default includes off 2014-03-16 13:11:51 +13:00
Rob Bygrave 5d46904341 Fix for introduced issue with sub-query expression 2014-03-16 00:05:43 +13:00
Rob Bygrave f0dd069ed2 Fix for #70 - @PrivateOwned makes Ebean try to remove non persistent objects from DB and throws OptimisticLockException 2014-03-15 13:48:56 +13:00
Rob Bygrave 5bd454bdc0 Merge pull request #76 from icaromuniz/master
Adding test case TestPrivateOwnedIgnoreTransientOrphan related to issue #70
2014-03-15 13:14:17 +13:00
Rob Bygrave eeebaf9a08 Fix for #78 - Wrong collection used in excludeJar 2014-03-15 12:53:58 +13:00
Rob Bygrave 636a148509 Fix for #79 - Rollback in case of connection closed 2014-03-15 12:45:44 +13:00
Icaro Muniz 170cd6051e Adding test case TestPrivateOwnedIgnoreTransientOrphan 2014-02-26 09:23:35 -03:00
Rob Bygrave 522fc2384c [maven-release-plugin] prepare for next development iteration 2014-01-31 02:12:20 +13:00
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
Rob Bygrave 93304949e8 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.4 2013-09-11 16:26:36 +12:00
Rob Bygrave 1a292275d8 Json Timestamp parsing - Add support for long values 2013-09-11 14:20:36 +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
Rob Bygrave f71eac5f1e [maven-release-plugin] prepare for next development iteration 2013-08-02 12:18:45 +12:00
Rob Bygrave 3f9c3c1db5 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.3 2013-08-02 12:18:36 +12:00
Rob Bygrave b4df899314 Move agent dependency to 3.2.2 (Fix for Issue 44 @Transactional
handling)
2013-08-02 12:16:41 +12:00
Rob Bygrave 9c7f62e8b1 Allow more flexibility with JSON marshalling of non-entity beans 2013-08-02 12:16:00 +12:00
Rob Bygrave bf8574833a Remove Thread UncaughtExceptionHandler as part of fix for pull/44
@Transactional not rolling back for uncaught RuntimeException - This
change is not required for 44 but is nice cleanup as
UncaughtExceptionHandler is no used.
2013-08-02 12:14:17 +12:00
Rob Bygrave 6f1bb5a9d7 Merge pull request #44 from hei1233212000/master
added a test case to show the issue that the user record does NOT rolback when there is an exception thrown
2013-08-01 16:45:15 -07:00
harry.chan 2f7b4e3a24 added a test case to show the issue that the user record does NOT
rolback when there is an exception thrown
2013-08-01 11:42:38 +08:00
Rob Bygrave 44d95dc06c Fix for Issue 43 - Support for JSON unmarshalling of @XmlRootElement
beans.
2013-07-29 23:24:30 +12:00
Robin Bygrave 6f579ba890 [maven-release-plugin] prepare for next development iteration 2013-07-12 21:10:49 +12:00
Robin Bygrave cd1772fa05 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.2 2013-07-12 21:10:37 +12:00
Robin Bygrave 1fa7a0a45d Make SNAPSHOT again after failed release prepare 2013-07-12 21:08:16 +12:00
Robin Bygrave 01548da0c5 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.2 2013-07-12 20:53:55 +12:00
Robin Bygrave a9ff65f7b5 Make SNAPSHOT again after failed release prepare 2013-07-12 20:51:26 +12:00
Robin Bygrave 7d23c93411 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.2 2013-07-12 20:33:13 +12:00
Robin Bygrave 243864fef7 Fix for Issue 35 Connection pool leak with findFutureRowCount(),
findFutureList() and findFutureIds()
2013-07-01 20:17:38 +12:00
Rob Bygrave c0b83b939d Merge pull request #33 from arielscarpinelli/master
Tests for ... Join fetching related objects that use inheritance causes wrong SQL
2013-06-30 14:55:39 -07:00
Robin Bygrave c176e4f05e Fix for Issue 34 - JSR 303 Validation groups are ignored for DDL
generation (of @NotNull)
2013-07-01 09:50:42 +12:00
Ariel Scarpinelli c3b26104f3 Tests BUG 408 2013-06-29 10:50:47 -03:00
Robin Bygrave 4f2f9a2e99 Fix for Issue 32 Deleted bean not removed from PersistenceContext 2013-06-27 23:23:20 +12:00
Robin Bygrave 4bbbad9b3b Fix for Issue 30 Invalidating L2 Cache on deletion for referenced
OneToMany relations not working
2013-06-27 23:13:06 +12:00
Robin Bygrave 488b856470 Merge fix from Peter Fu, avaje-ebeanorm/pull/9 request plus associated
test
2013-06-23 15:19:46 +12:00
Rob Bygrave 7cd71559b6 Merge pull request #31 from outersky/master
Json parser now supports unicode escape sequence
2013-06-19 04:06:51 -07:00
Robin Bygrave 0c373f99c6 Modified query to fetch the ordered column - possible restriction in h2 2013-06-19 22:53:33 +12:00
Robin Bygrave a32ca484c5 Merge branch 'master' of https://github.com/dastultz/avaje-ebeanorm into dastultz-master 2013-06-19 22:41:56 +12:00
Robin Bygrave 9ad4931c12 Add fix and test for BUG 408 from Eddie 2013-06-19 22:35:42 +12:00
outersky 251076e30d reformat code 2013-06-03 13:49:34 +08:00
outersky 93d3638f6c support unicode escape sequence for json
like: {"name":"\u60a8\u597d"}
2013-06-03 13:45:39 +08:00
Daryl Stultz 0d3b1c625f Expose order by columns omitted from select. 2013-05-10 13:07:46 -04:00
Daryl Stultz 2de57f7b35 Exposed bug 417 (and 408?) 2013-05-08 14:32:57 -04:00
Rob Bygrave 60a1a4bb5b Merge pull request #21 from rvowles/master
Support for WAR classpath entries and issues around DDL gen
2013-05-03 02:52:17 -07:00
Richard Vowles d2f532ad3c prevent Ebean from issuing duplicate SQL when generating the database so it doesnt fail. A problem for classes with the same generator 2013-05-03 20:06:18 +12:00
Richard Vowles 26a8af8c5b add support for having a WAR classpath entry in the classpath 2013-05-03 20:05:07 +12:00
Rob Bygrave d8ac8e1694 Merge pull request #20 from NorthFury/cleanup
Cleanup
2013-05-02 02:40:23 -07:00
Robin Bygrave 7e6a6e3875 Delete BeanWithTimezone.java, extra class after rename 2013-05-02 21:30:33 +12:00
Robert Biter 703ee59792 removed dead code in TableJoin 2013-05-02 11:05:49 +03:00
Robert Biter 464cb38b8b consistent initialization for the StringBuilder in DefaultDbSqlContext 2013-05-02 11:04:45 +03:00
Robert Biter eb61246254 removed getter for the StringBuilder in DefaultDbSqlContext 2013-05-02 11:02:25 +03:00
Robert Biter a4dda42650 removed redundant comments in DefaultDbSqlContext 2013-05-02 10:59:47 +03:00
North fbfa593bdb fixed build (deleted duplicate file BeanWithTimezone.java) 2013-05-02 10:56:21 +03:00
Robin Bygrave a3dc2a55f4 Fix for Issue 19 - Wrong database configuration for SQLite /
AUTOINCREMENT position in generated DDL
2013-05-01 21:35:07 +12:00
Robin Bygrave 2f97c5c36e Remove unused ServerConfig debugSql options, Update Tests to use agentloader 2013-04-29 22:42:27 +12:00
Rob Bygrave bb2479c9e7 Update readme with maven dependency 2013-04-29 09:03:54 +12:00
Robin Bygrave e79a5d4332 Remove LogLevel enum and associated code 2013-04-28 00:29:30 +12:00
Robin Bygrave 513a61503e Fix for commented out code for setConcurrencyMode(). Bah!! 2013-04-28 00:14:03 +12:00
Robin Bygrave 925036925c [maven-release-plugin] prepare for next development iteration 2013-04-26 23:27:25 +12:00
488 changed files with 15418 additions and 13686 deletions
+1
View File
@@ -6,3 +6,4 @@
target/
logs/
log/
/mydb.db
+11 -1
View File
@@ -1,4 +1,14 @@
avaje-ebeanorm-server
=====================
The internal server implementation of EbeanORM API
The internal server implementation of EbeanORM API
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.5</version>
</dependency>
+38 -3
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.1</version>
<version>3.3.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -84,12 +84,33 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>3.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.153</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
@@ -108,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>
@@ -119,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>
@@ -126,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>
@@ -135,7 +170,7 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>3.2.1</version>
<version>3.2.2</version>
<executions>
<execution>
<id>main</id>
@@ -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.
*/
@@ -1,25 +0,0 @@
package com.avaje.ebean;
/**
* The transaction log level.
* <p>
* This is used to define how much Ebean should log such as generated SQL.
* </p>
*/
public enum LogLevel {
/**
* No logging.
*/
NONE,
/**
* Log only a summary level.
*/
SUMMARY,
/**
* Log generated SQL/DML and binding variables.
*/
SQL
}
+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> {
+2 -31
View File
@@ -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.
*/
@@ -51,29 +45,6 @@ public interface Transaction {
*/
public void setReadOnly(boolean readOnly);
// /**
// * Log a comment to the transaction log.
// */
// public void log(String msg);
/**
* Set the logLevel to use for this transaction.
*/
public void setLogLevel(LogLevel logLevel);
/**
* Return the logLevel this transaction is using.
*/
public LogLevel getLogLevel();
/**
* Deprecated in favour of using {@link #setLogLevel} Set this to false to
* disable logging for this transaction.
*
* @deprecated
*/
public void setLoggingOn(boolean isLoggingOn);
/**
* Commit the transaction.
*/
@@ -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);
@@ -6,7 +6,6 @@ import java.util.List;
import javax.sql.DataSource;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.LogLevel;
import com.avaje.ebean.annotation.Encrypted;
import com.avaje.ebean.cache.ServerCacheFactory;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -19,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;
/**
@@ -140,10 +140,6 @@ public class ServerConfig {
private boolean ddlRun;
private boolean debugSql;
private boolean debugLazyLoad;
private boolean useJtaTransactionManager;
/**
@@ -151,22 +147,6 @@ public class ServerConfig {
*/
private ExternalTransactionManager externalTransactionManager;
/**
* Set to true to log using java.util.logging and otherwise uses ebean
* transaction loggers.
*/
private boolean loggingToJavaLogger;
/**
* The directory transaction logs go (when loggingToJavaLogger is false).
*/
private String loggingDirectory = "logs";
/**
* The overall transaction logging level.
*/
private LogLevel loggingLevel = LogLevel.NONE;
/**
* Used to unwrap PreparedStatements to perform JDBC Driver specific functions
*/
@@ -212,6 +192,10 @@ public class ServerConfig {
private ServerCacheManager serverCacheManager;
private boolean collectQueryStatsByNode;
private boolean collectQueryOrigins;
/**
* Construct a Server Configuration for programmatically creating an
* EbeanServer.
@@ -780,139 +764,6 @@ public class ServerConfig {
this.dbEncrypt = dbEncrypt;
}
/**
* Return true to get the generated SQL queries output to the console.
* <p>
* To get the SQL and bind variables for insert update delete statements you
* should use transaction logging.
* </p>
*/
public boolean isDebugSql() {
return debugSql;
}
/**
* Set to true to get the generated SQL queries output to the console.
* <p>
* To get the SQL and bind variables for insert update delete statements you
* should use transaction logging.
* </p>
*/
public void setDebugSql(boolean debugSql) {
this.debugSql = debugSql;
}
/**
* Return true if there is debug logging on lazy loading events.
*/
public boolean isDebugLazyLoad() {
return debugLazyLoad;
}
/**
* Set to true to get debug logging on lazy loading events.
*/
public void setDebugLazyLoad(boolean debugLazyLoad) {
this.debugLazyLoad = debugLazyLoad;
}
/**
* Return the default transaction logging level.
* <p>
* The logging level can be changed on a per transaction basis.
* </p>
*/
public LogLevel getLoggingLevel() {
return loggingLevel;
}
/**
* Set the default transaction logging level.
* <p>
* The logging level can be changed on a per transaction basis.
* </p>
*/
public void setLoggingLevel(LogLevel logLevel) {
this.loggingLevel = logLevel;
}
/**
* Return the directory where transaction logs go.
*/
public String getLoggingDirectory() {
return loggingDirectory;
}
/**
* Return the transaction log directory substituting any expressions such as
* ${catalina.base} etc.
*/
public String getLoggingDirectoryWithEval() {
return GlobalProperties.evaluateExpressions(loggingDirectory);
}
/**
* Set the directory that the transaction logs go in.
* <p>
* This will not be used if the transaction logging is going to java util
* logging (via {@link #setLoggingToJavaLogger(boolean)}).
* </p>
* <p>
* This can contain expressions like ${catalina.base} with environment
* variables, java system properties and entries in ebean.properties.
* </p>
* <p>
* e.g. ${catalina.base}/logs/trans
* </p>
*
* @param loggingDirectory
* the transaction log directory
*/
public void setLoggingDirectory(String loggingDirectory) {
this.loggingDirectory = loggingDirectory;
}
/**
* Return true if you want to use a java.util.logging.Logger to log
* transaction statements, bind values etc.
* <p>
* If this is false then the default transaction logger is used which logs the
* transaction details to separate transaction log files.
* </p>
*/
@Deprecated
public boolean isLoggingToJavaLogger() {
return loggingToJavaLogger;
}
/**
* Set this to true if you want transaction logging to use a
* java.util.logging.Logger to log the statements and bind variables etc
* rather than the default one which creates separate transaction log files.
*/
@Deprecated
public void setLoggingToJavaLogger(boolean transactionLogToJavaLogger) {
this.loggingToJavaLogger = transactionLogToJavaLogger;
}
/**
* Deprecated - please use isTransactionLogToJavaLogger();
*
* @deprecated
*/
public boolean isUseJuliTransactionLogger() {
return isLoggingToJavaLogger();
}
/**
* Deprecated - please use setTransactionLogToJavaLogger();
*
* @deprecated
*/
public void setUseJuliTransactionLogger(boolean transactionLogToJavaLogger) {
setLoggingToJavaLogger(transactionLogToJavaLogger);
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -1080,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.
@@ -1337,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);
@@ -1356,32 +1255,10 @@ public class ServerConfig {
ddlGenerate = p.getBoolean("ddl.generate", false);
ddlRun = p.getBoolean("ddl.run", false);
debugSql = p.getBoolean("debug.sql", false);
debugLazyLoad = p.getBoolean("debug.lazyload", false);
loggingLevel = getLogLevelValue(p);
String s = p.get("useJuliTransactionLogger", null);
s = p.get("loggingToJavaLogger", s);
loggingToJavaLogger = "true".equalsIgnoreCase(s);
s = p.get("log.directory", "logs");
loggingDirectory = p.get("logging.directory", s);
classes = getClasses(p);
}
private LogLevel getLogLevelValue(PropertySource p) {
// logging.level preferred but others parameters will work
String logValue = p.get("logging", "NONE");
logValue = p.get("log.level", logValue);
logValue = p.get("logging.level", logValue);
if (logValue.trim().equalsIgnoreCase("ALL")) {
logValue = "SQL";
}
return Enum.valueOf(LogLevel.class, logValue.toUpperCase());
}
private NamingConvention createNamingConvention(PropertySource p) {
NamingConvention nc = createInstance(p, NamingConvention.class, "namingconvention");
@@ -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);
}
}
@@ -18,9 +18,10 @@ public class DbDdlSyntax {
private String dropTableCascade;
private String dropIfExists;
private String newLine = "\r\n";
private String newLine = "\n";
private String identity = "auto_increment";
private String identitySuffix = "";
private String pkPrefix = "pk_";
@@ -56,6 +57,24 @@ public class DbDdlSyntax {
this.identity = identity;
}
/**
* Typically returns empty string but for SQLite and perhaps others
* the Identity/AutoIncrement clause comes after the primary key clause.
*/
public String getIdentitySuffix() {
return identitySuffix;
}
/**
* Set the identity clause that would appear after the primary key clause.
* <p>
* Only used for SQLite at this stage.
* </p>
*/
public void setIdentitySuffix(String identitySuffix) {
this.identitySuffix = identitySuffix;
}
/**
* Return the width for padding whitespace after column names.
*/
@@ -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"));
@@ -22,9 +22,15 @@ public class SQLitePlatform extends DatabasePlatform {
dbTypeMap.put(Types.BIT, new DbType("int default 0"));
dbTypeMap.put(Types.BOOLEAN, new DbType("int default 0"));
dbTypeMap.put(Types.BIGINT, new DbType("integer"));
dbTypeMap.put(Types.SMALLINT, new DbType("integer"));
dbDdlSyntax.setInlinePrimaryKeyConstraint(true);
dbDdlSyntax.setIdentity("AUTOINCREMENT");
// AutoIncrement goes the primary key declaration
dbDdlSyntax.setIdentity("");
dbDdlSyntax.setIdentitySuffix("AUTOINCREMENT");
dbDdlSyntax.setDisableReferentialIntegrity("PRAGMA foreign_keys = OFF");
dbDdlSyntax.setEnableReferentialIntegrity("PRAGMA foreign_keys = ON");
}
@@ -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();
}
}
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import com.avaje.ebean.TxScope;
@@ -45,9 +44,6 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
*/
private final ArrayList<Class<? extends Throwable>> rollbackFor;
private final UncaughtExceptionHandler originalUncaughtHandler;
/**
* Flag set when a rollback has occurred.
*/
@@ -65,11 +61,6 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
Thread t = Thread.currentThread();
originalUncaughtHandler = t.getUncaughtExceptionHandler();
t.setUncaughtExceptionHandler(this);
}
/**
@@ -81,13 +72,8 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
// rollback transaction if required
caughtThrowable(e);
// reinstate suspended transaction and
// original uncaughtExceptionHandler if required
// reinstate suspended transaction
onFinally();
if (originalUncaughtHandler != null){
originalUncaughtHandler.uncaughtException(thread, e);
}
}
/**
@@ -110,11 +96,7 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
* Also reinstate the suspended transaction if there was one.
*/
public void onFinally() {
try {
if (originalUncaughtHandler != null){
Thread.currentThread().setUncaughtExceptionHandler(originalUncaughtHandler);
}
try {
if (!rolledBack && created) {
transaction.commit();
}
@@ -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>
@@ -2,8 +2,12 @@ package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.LogLevel;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
@@ -12,6 +16,8 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
*/
public abstract class BeanRequest {
private static final Logger log = LoggerFactory.getLogger(BeanRequest.class);
/**
* The server processing the request.
*/
@@ -85,7 +91,14 @@ public abstract class BeanRequest {
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
transaction.rollback();
try {
transaction.rollback();
} catch (PersistenceException e) {
// Just log this and carry on. A previous exception has been
// thrown and if this rollback throws exception it likely means
// that the connection is broken (and the datasource and db will cleanup)
log.error("Error trying to rollack a transaction (after a prior exception thrown)", e);
}
}
}
@@ -97,6 +110,10 @@ public abstract class BeanRequest {
return ebeanServer;
}
public SpiEbeanServer getServer() {
return ebeanServer;
}
/**
* Return the Transaction associated with this request.
*/
@@ -115,13 +132,13 @@ public abstract class BeanRequest {
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal();
return transaction.isLogSql();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal();
return transaction.isLogSummary();
}
}
@@ -82,16 +82,22 @@ public class BootupClassPathSearch {
ClassPathSearchFilter filter = new ClassPathSearchFilter();
filter.addDefaultExcludePackages();
if (packages != null) {
if (packages != null && packages.size() > 0) {
for (String packageName : packages) {
filter.includePackage(packageName);
}
// if they specified include packages, they don't want by default to include everything
filter.setDefaultPackageMatch(false);
}
if (jars != null) {
if (jars != null && jars.size() > 0) {
for (String jarName : jars) {
filter.includeJar(jarName);
}
// if they specified jars to specifically include, they don't want everything included
filter.setDefaultJarMatch(false);
}
return filter;
@@ -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();
}
}
@@ -1,111 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.config.GlobalProperties;
/**
* Helper used in producing debug output on lazy loading.
*/
public class DebugLazyLoad {
private final String[] ignoreList;
private final boolean debug;
public DebugLazyLoad(boolean lazyLoadDebug) {
ignoreList = buildLazyLoadIgnoreList();
debug = lazyLoadDebug;
}
/**
* Return true if debugging is on.
*/
public boolean isDebug() {
return debug;
}
/**
* Return the StackTraceElement that is believed to be the line of code that
* triggered the lazy loading.
* <p>
* This is determined by going up the stack trace ignoring all the sections
* of java and Ebean code etc until you find code not in the ignore list -
* this is assumed to be your application code that triggered the lazy
* loading (it could be a third party layer such as a web template).
* </p>
*/
public StackTraceElement getStackTraceElement(Class<?> beanType) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (int i = 0; i < stackTrace.length; i++) {
if (isStackLine(stackTrace[i], beanType)) {
return stackTrace[i];
}
}
return null;
}
/**
* Return true if the code is expected to be application code.
*/
private boolean isStackLine(StackTraceElement element, Class<?> beanType) {
String stackClass = element.getClassName();
if (isBeanClass(beanType, stackClass)) {
return false;
}
for (int i = 0; i < ignoreList.length; i++) {
if (stackClass.startsWith(ignoreList[i])) {
return false;
}
}
return true;
}
/**
* Recurse up the inheritance checking to see if the stackClass is
* this beanType (or a parent type).
*/
private boolean isBeanClass(Class<?> beanType, String stackClass) {
if (stackClass.startsWith(beanType.getName())) {
return true;
}
Class<?> superCls = beanType.getSuperclass();
if (superCls.equals(Object.class)){
return false;
} else {
return isBeanClass(superCls, stackClass);
}
}
/**
* Build list of prefixes used to find the application code that triggered
* the lazy loading.
*/
private String[] buildLazyLoadIgnoreList() {
List<String> ignore = new ArrayList<String>();
// code that should be ignored when searching
// the stack trace elements
ignore.add("com.avaje.ebean");
ignore.add("java");
ignore.add("sun.reflect");
ignore.add("org.codehaus.groovy.runtime.");
String extraIgnore = GlobalProperties.get("debug.lazyload.ignore", null);
if (extraIgnore != null) {
String[] split = extraIgnore.split(",");
for (int i = 0; i < split.length; i++) {
ignore.add(split[i].trim());
}
}
return ignore.toArray(new String[ignore.size()]);
}
}
@@ -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();
@@ -1340,10 +1382,9 @@ public final class DefaultServer implements SpiEbeanServer {
Transaction newTxn = createTransaction();
CallableQueryRowCount<T> call = new CallableQueryRowCount<T>(this, copy, newTxn);
FutureTask<Integer> futureTask = new FutureTask<Integer>(call);
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<T>(copy, futureTask);
backgroundExecutor.execute(futureTask);
QueryFutureRowCount<T> queryFuture = new QueryFutureRowCount<T>(call);
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@@ -1362,11 +1403,9 @@ public final class DefaultServer implements SpiEbeanServer {
Transaction newTxn = createTransaction();
CallableQueryIds<T> call = new CallableQueryIds<T>(this, copy, newTxn);
FutureTask<List<Object>> futureTask = new FutureTask<List<Object>>(call);
QueryFutureIds<T> queryFuture = new QueryFutureIds<T>(call);
QueryFutureIds<T> queryFuture = new QueryFutureIds<T>(copy, futureTask);
backgroundExecutor.execute(futureTask);
backgroundExecutor.execute(queryFuture.getFutureTask());
return queryFuture;
}
@@ -1376,6 +1415,7 @@ public final class DefaultServer implements SpiEbeanServer {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
spiQuery.setFutureFetch(true);
// transfer the persistence content from the transaction
if (spiQuery.getPersistenceContext() == null) {
if (t != null) {
spiQuery.setPersistenceContext(((SpiTransaction) t).getPersistenceContext());
@@ -1387,14 +1427,13 @@ public final class DefaultServer implements SpiEbeanServer {
}
}
// Create a new transaction solely to execute the findList() at some future time
Transaction newTxn = createTransaction();
CallableQueryList<T> call = new CallableQueryList<T>(this, query, newTxn);
CallableQueryList<T> call = new CallableQueryList<T>(this, spiQuery, newTxn);
QueryFutureList<T> queryFuture = new QueryFutureList<T>(call);
backgroundExecutor.execute(queryFuture.getFutureTask());
FutureTask<List<T>> futureTask = new FutureTask<List<T>>(call);
backgroundExecutor.execute(futureTask);
return new QueryFutureList<T>(query, futureTask);
return queryFuture;
}
public <T> PagingList<T> findPagingList(Query<T> query, Transaction t, int pageSize) {
@@ -1921,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++) {
@@ -2070,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>
@@ -69,8 +69,6 @@ public class InternalConfiguration {
private final BeanDescriptorManager beanDescriptorManager;
private final DebugLazyLoad debugLazyLoad;
private final TransactionManager transactionManager;
private final TransactionScopeManager transactionScopeManager;
@@ -115,23 +113,18 @@ public class InternalConfiguration {
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.debugLazyLoad = new DebugLazyLoad(serverConfig.isDebugLazyLoad());
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
serverConfig, beanDescriptorManager, this.getBootupClasses());
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder,
backgroundExecutor);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder, backgroundExecutor);
ExternalTransactionManager externalTransactionManager = serverConfig
.getExternalTransactionManager();
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
externalTransactionManager = new JtaTransactionManager();
}
if (externalTransactionManager != null) {
externalTransactionManager.setTransactionManager(transactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager,
externalTransactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
} else {
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
@@ -170,7 +163,6 @@ public class InternalConfiguration {
}
public Persister createPersister(SpiEbeanServer server) {
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
}
@@ -246,10 +238,6 @@ public class InternalConfiguration {
return clusterManager;
}
public DebugLazyLoad getDebugLazyLoad() {
return debugLazyLoad;
}
public SpiBackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
@@ -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);
}
}
@@ -176,23 +176,26 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
return beanPersistListener != null;
}
public void notifyCache() {
if (notifyCache) {
switch (type) {
case INSERT:
beanDescriptor.cacheInsert(idValue, this);
break;
case UPDATE:
beanDescriptor.cacheUpdate(idValue, this);
break;
case DELETE:
beanDescriptor.cacheDelete(idValue, this);
break;
default:
throw new IllegalStateException("Invalid type "+type);
}
}
}
/**
* Notify/Update the local L2 cache after the transaction has successfully committed.
*/
public void notifyCache() {
if (notifyCache) {
switch (type) {
case INSERT:
beanDescriptor.cacheInsert(idValue, this);
break;
case UPDATE:
beanDescriptor.cacheUpdate(idValue, this);
break;
case DELETE:
// Bean deleted from cache early via postDelete()
break;
default:
throw new IllegalStateException("Invalid type " + type);
}
}
}
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
@@ -510,6 +513,14 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
}
}
public void postDelete() {
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.cacheDelete(idValue, this);
}
/**
* Post processing.
*/
@@ -1,173 +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")) {
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;
}
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);
}
}
@@ -21,136 +21,130 @@ import org.slf4j.LoggerFactory;
public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
private static final Logger logger = LoggerFactory.getLogger(CreateTableColumnVisitor.class);
private final DdlGenContext ctx;
private final DbDdlSyntax ddl;
private final DdlGenContext ctx;
private final CreateTableVisitor parent;
private final DbDdlSyntax ddl;
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
this.parent = parent;
this.ctx = ctx;
this.ddl = ctx.getDdlSyntax();
}
private final CreateTableVisitor parent;
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
this.parent = parent;
this.ctx = ctx;
this.ddl = ctx.getDdlSyntax();
}
@Override
public void visitMany(BeanPropertyAssocMany<?> p) {
if (p.isManyToMany()){
if (p.getMappedBy() != null) {
// only create on other 'owning' side
} else {
TableJoin intersectionTableJoin = p.getIntersectionTableJoin();
// check if the intersection table has already been created
String intTable = intersectionTableJoin.getTable();
if (ctx.isProcessIntersectionTable(intTable)){
// build the create table and fkey constraints
// putting the DDL into ctx for later output as we are
// in the middle of rendering the create table DDL
new CreateIntersectionTable(ctx, p).build();
}
}
}
}
@Override
public void visitMany(BeanPropertyAssocMany<?> p) {
if (p.isManyToMany()) {
if (p.getMappedBy() != null) {
// only create on other 'owning' side
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
visitScalar(p);
}
} else {
TableJoin intersectionTableJoin = p.getIntersectionTableJoin();
public void visitCompound(BeanPropertyCompound p) {
// do nothing
}
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
visitScalar(p);
}
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
String uqConstraintName = "uq_"+ table+ "_"+column;
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()){
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
// check if the intersection table has already been created
String intTable = intersectionTableJoin.getTable();
if (ctx.isProcessIntersectionTable(intTable)) {
// build the create table and fkey constraints
// putting the DDL into ctx for later output as we are
// in the middle of rendering the create table DDL
new CreateIntersectionTable(ctx, p).build();
}
uqConstraintName = ctx.removeQuotes(uqConstraintName);
uqConstraintName = StringHelper.replaceString(uqConstraintName, " ", "_");
StringBuilder constraintExpr = new StringBuilder();
constraintExpr.append("constraint ")
.append(uqConstraintName)
.append(" unique (");
return constraintExpr;
}
}
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
}
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
visitScalar(p);
}
ImportedId importedId = p.getImportedId();
public void visitCompound(BeanPropertyCompound p) {
// do nothing
}
TableJoinColumn[] columns = p.getTableJoin().columns();
if (columns.length == 0){
String msg = "No join columns for "+p.getFullBeanName();
throw new RuntimeException(msg);
}
StringBuilder constraintExpr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), columns[0].getLocalDbColumn());
for (int i = 0; i < columns.length; i++) {
String dbCol = columns[i].getLocalDbColumn();
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
if (i > 0){
constraintExpr.append(", ");
}
constraintExpr.append(dbCol);
if (parent.isDbColumnWritten(dbCol)) {
continue;
}
parent.writeColumnName(dbCol, p);
visitScalar(p);
}
BeanProperty importedProperty = importedId.findMatchImport(dbCol);
if (importedProperty != null) {
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
String columnDefn = ctx.getColumnDefn(importedProperty);
ctx.write(columnDefn);
String uqConstraintName = "uq_" + table + "_" + column;
} else {
throw new RuntimeException("Imported BeanProperty not found?");
}
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()) {
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
}
if (!p.isNullable()) {
ctx.write(" not null");
}
ctx.write(",").writeNewLine();
}
constraintExpr.append(")");
if (p.isOneToOne()){
if (ddl.isAddOneToOneUniqueContraint()){
parent.addUniqueConstraint( constraintExpr.toString());
}
}
}
uqConstraintName = ctx.removeQuotes(uqConstraintName);
uqConstraintName = StringHelper.replaceString(uqConstraintName, " ", "_");
StringBuilder constraintExpr = new StringBuilder();
constraintExpr.append("constraint ").append(uqConstraintName).append(" unique (");
return constraintExpr;
}
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
ImportedId importedId = p.getImportedId();
TableJoinColumn[] columns = p.getTableJoin().columns();
if (columns.length == 0) {
String msg = "No join columns for " + p.getFullBeanName();
throw new RuntimeException(msg);
}
StringBuilder constraintExpr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), columns[0].getLocalDbColumn());
for (int i = 0; i < columns.length; i++) {
String dbCol = columns[i].getLocalDbColumn();
if (i > 0) {
constraintExpr.append(", ");
}
constraintExpr.append(dbCol);
if (parent.isDbColumnWritten(dbCol)) {
continue;
}
parent.writeColumnName(dbCol, p);
BeanProperty importedProperty = importedId.findMatchImport(dbCol);
if (importedProperty != null) {
String columnDefn = ctx.getColumnDefn(importedProperty);
ctx.write(columnDefn);
} else {
throw new RuntimeException("Imported BeanProperty not found?");
}
if (!p.isNullable()) {
ctx.write(" not null");
}
ctx.write(",").writeNewLine();
}
constraintExpr.append(")");
if (p.isOneToOne()) {
if (ddl.isAddOneToOneUniqueContraint()) {
parent.addUniqueConstraint(constraintExpr.toString());
}
}
}
@Override
public void visitScalar(BeanProperty p) {
if (p.isSecondaryTable()) {
return;
}
if (p.isSecondaryTable()) {
return;
}
if (parent.isDbColumnWritten(p.getDbColumn())) {
return;
@@ -161,7 +155,8 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
String columnDefn = ctx.getColumnDefn(p);
ctx.write(columnDefn);
if (isIdentity(p)) {
boolean identity = isIdentity(p);
if (identity) {
writeIdentity();
}
@@ -172,23 +167,25 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
ctx.write(" not null");
}
if (p.isUnique() && !p.isId()){
parent.addUniqueConstraint(createUniqueConstraint(p));
}
if (identity) {
writeIdentitySuffix();
}
if (p.isUnique() && !p.isId()) {
parent.addUniqueConstraint(createUniqueConstraint(p));
}
parent.addCheckConstraint(p);
ctx.write(",").writeNewLine();
}
private String createUniqueConstraint(BeanProperty p) {
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
expr.append(p.getDbColumn()).append(")");
return expr.toString();
}
private String createUniqueConstraint(BeanProperty p) {
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
expr.append(p.getDbColumn()).append(")");
return expr.toString();
}
protected void writeIdentity() {
String identity = ddl.getIdentity();
@@ -197,27 +194,33 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
}
}
protected boolean isIdentity(BeanProperty p) {
if (p.isId()) {
try {
IdType idType = p.getBeanDescriptor().getIdType();
if (idType.equals(IdType.IDENTITY)){
int jdbcType = p.getScalarType().getJdbcType();
if (jdbcType == Types.INTEGER || jdbcType == Types.BIGINT || jdbcType == Types.SMALLINT) {
return true;
}
}
} catch (Exception e){
String msg = "Error determining identity on property "+p.getFullBeanName();
logger.error(msg, e);
}
}
return false;
}
protected void writeIdentitySuffix() {
String identity = ddl.getIdentitySuffix();
if (identity != null && identity.length() > 0) {
ctx.write(" ").write(identity);
}
}
protected boolean isIdentity(BeanProperty p) {
if (p.isId()) {
try {
IdType idType = p.getBeanDescriptor().getIdType();
if (idType.equals(IdType.IDENTITY)) {
int jdbcType = p.getScalarType().getJdbcType();
if (jdbcType == Types.INTEGER || jdbcType == Types.BIGINT || jdbcType == Types.SMALLINT) {
return true;
}
}
} catch (Exception e) {
String msg = "Error determining identity on property " + p.getFullBeanName();
logger.error(msg, e);
}
}
return false;
}
}
@@ -31,75 +31,75 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
public class DdlGenerator implements SpiEbeanPlugin {
private static final Logger logger = LoggerFactory.getLogger(DdlGenerator.class);
private SpiEbeanServer server;
private DatabasePlatform dbPlatform;
private SpiEbeanServer server;
private int summaryLength = 80;
private DatabasePlatform dbPlatform;
private boolean generateDdl;
private boolean runDdl;
private int summaryLength = 80;
private String dropContent;
private String createContent;
private boolean generateDdl;
private boolean runDdl;
private NamingConvention namingConvention;
private String dropContent;
private String createContent;
public void setup(SpiEbeanServer server, DatabasePlatform dbPlatform, ServerConfig serverConfig) {
this.server = server;
this.dbPlatform = dbPlatform;
this.generateDdl = serverConfig.isDdlGenerate();
this.runDdl = serverConfig.isDdlRun();
this.namingConvention = serverConfig.getNamingConvention();
}
private NamingConvention namingConvention;
/**
* Generate the DDL and then run the DDL based on property settings (ebean.ddl.generate and ebean.ddl.run etc).
*/
public void execute(boolean online) {
generateDdl();
if (online){
runDdl();
}
}
public void setup(SpiEbeanServer server, DatabasePlatform dbPlatform, ServerConfig serverConfig) {
this.server = server;
this.dbPlatform = dbPlatform;
this.generateDdl = serverConfig.isDdlGenerate();
this.runDdl = serverConfig.isDdlRun();
this.namingConvention = serverConfig.getNamingConvention();
}
/**
* Generate the DDL drop and create scripts if the properties have been set.
*/
public void generateDdl() {
if (generateDdl) {
writeDrop(getDropFileName());
writeCreate(getCreateFileName());
}
}
/**
* Generate the DDL and then run the DDL based on property settings
* (ebean.ddl.generate and ebean.ddl.run etc).
*/
public void execute(boolean online) {
generateDdl();
if (online) {
runDdl();
}
}
/**
* Run the DDL drop and DDL create scripts if properties have been set.
*/
public void runDdl() {
/**
* Generate the DDL drop and create scripts if the properties have been set.
*/
public void generateDdl() {
if (generateDdl) {
writeDrop(getDropFileName());
writeCreate(getCreateFileName());
}
}
if (runDdl) {
try {
if (dropContent == null){
dropContent = readFile(getDropFileName());
}
if (createContent == null){
createContent = readFile(getCreateFileName());
}
runScript(true, dropContent);
runScript(false, createContent);
/**
* Run the DDL drop and DDL create scripts if properties have been set.
*/
public void runDdl() {
} catch (IOException e){
String msg = "Error reading drop/create script from file system";
throw new RuntimeException(msg, e);
}
}
}
if (runDdl) {
try {
if (dropContent == null) {
dropContent = readFile(getDropFileName());
}
if (createContent == null) {
createContent = readFile(getCreateFileName());
}
runScript(true, dropContent);
runScript(false, createContent);
} catch (IOException e) {
String msg = "Error reading drop/create script from file system";
throw new RuntimeException(msg, e);
}
}
}
protected void writeDrop(String dropFile) {
// String c = generateDropDdl();
try {
String c = generateDropDdl();
writeFile(dropFile, c);
@@ -110,224 +110,230 @@ public class DdlGenerator implements SpiEbeanPlugin {
}
}
protected void writeCreate(String createFile) {
protected void writeCreate(String createFile) {
// String c = generateCreateDdl();
try {
String c = generateCreateDdl();
writeFile(createFile, c);
try {
String c = generateCreateDdl();
writeFile(createFile, c);
} catch (IOException e) {
String msg = "Error generating Create DDL";
throw new PersistenceException(msg, e);
}
}
} catch (IOException e) {
String msg = "Error generating Create DDL";
throw new PersistenceException(msg, e);
}
}
public String generateDropDdl() {
public String generateDropDdl() {
DdlGenContext ctx = createContext();
DdlGenContext ctx = createContext();
DropTableVisitor drop = new DropTableVisitor(ctx);
VisitorUtil.visit(server, drop);
DropTableVisitor drop = new DropTableVisitor(ctx);
VisitorUtil.visit(server, drop);
DropSequenceVisitor dropSequence = new DropSequenceVisitor(ctx);
VisitorUtil.visit(server, dropSequence);
DropSequenceVisitor dropSequence = new DropSequenceVisitor(ctx);
VisitorUtil.visit(server, dropSequence);
ctx.flush();
dropContent = ctx.getContent();
return dropContent;
}
ctx.flush();
dropContent = ctx.getContent();
return dropContent;
}
public String generateCreateDdl() {
public String generateCreateDdl() {
DdlGenContext ctx = createContext();
CreateTableVisitor create = new CreateTableVisitor(ctx);
VisitorUtil.visit(server, create);
DdlGenContext ctx = createContext();
CreateTableVisitor create = new CreateTableVisitor(ctx);
VisitorUtil.visit(server, create);
CreateSequenceVisitor createSequence = new CreateSequenceVisitor(ctx);
VisitorUtil.visit(server, createSequence);
CreateSequenceVisitor createSequence = new CreateSequenceVisitor(ctx);
VisitorUtil.visit(server, createSequence);
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
VisitorUtil.visit(server, fkeys);
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
VisitorUtil.visit(server, fkeys);
ctx.flush();
createContent = ctx.getContent();
return createContent;
}
ctx.flush();
createContent = ctx.getContent();
return createContent;
}
protected String getDropFileName() {
return server.getName() + "-drop.sql";
}
protected String getDropFileName() {
return server.getName() + "-drop.sql";
}
protected String getCreateFileName() {
return server.getName() + "-create.sql";
}
protected String getCreateFileName() {
return server.getName() + "-create.sql";
}
protected DdlGenContext createContext() {
return new DdlGenContext(dbPlatform, namingConvention);
}
protected DdlGenContext createContext() {
return new DdlGenContext(dbPlatform, namingConvention);
}
protected void writeFile(String fileName, String fileContent) throws IOException {
protected void writeFile(String fileName, String fileContent) throws IOException {
File f = new File(fileName);
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
try {
fw.write(fileContent);
fw.flush();
} finally {
fw.close();
}
}
FileWriter fw = new FileWriter(f);
try {
fw.write(fileContent);
fw.flush();
} finally {
fw.close();
}
}
protected String readFile(String fileName) throws IOException {
protected String readFile(String fileName) throws IOException {
File f = new File(fileName);
if (!f.exists()){
return null;
}
File f = new File(fileName);
if (!f.exists()) {
return null;
}
StringBuilder buf = new StringBuilder();
StringBuilder buf = new StringBuilder();
FileReader fr = new FileReader(f);
LineNumberReader lr = new LineNumberReader(fr);
try {
String s = null;
while ((s = lr.readLine()) != null){
buf.append(s).append("\n");
}
} finally {
lr.close();
}
FileReader fr = new FileReader(f);
LineNumberReader lr = new LineNumberReader(fr);
try {
String s = null;
while ((s = lr.readLine()) != null) {
buf.append(s).append("\n");
}
} finally {
lr.close();
}
return buf.toString();
}
return buf.toString();
}
/**
* Execute all the DDL statements in the script.
*/
public void runScript(boolean expectErrors, String content) {
/**
* Execute all the DDL statements in the script.
*/
public void runScript(boolean expectErrors, String content) {
StringReader sr = new StringReader(content);
List<String> statements = parseStatements(sr);
StringReader sr = new StringReader(content);
List<String> statements = parseStatements(sr);
Transaction t = server.createTransaction();
try {
Connection connection = t.getConnection();
Transaction t = server.createTransaction();
try {
Connection connection = t.getConnection();
logger.info("Running DDL");
logger.info("Running DDL");
runStatements(expectErrors, statements, connection);
runStatements(expectErrors, statements, connection);
logger.info("Running DDL Complete");
logger.info("Running DDL Complete");
t.commit();
t.commit();
} catch (Exception e){
String msg = "Error: "+e.getMessage();
throw new PersistenceException(msg, e);
} finally {
t.end();
}
}
} catch (Exception e) {
String msg = "Error: " + e.getMessage();
throw new PersistenceException(msg, e);
} finally {
t.end();
}
}
/**
* Execute the list of statements.
*/
private void runStatements(boolean expectErrors, List<String> statements, Connection c) {
/**
* Execute the list of statements.
*/
private void runStatements(boolean expectErrors, List<String> statements, Connection c) {
List<String> noDuplicates = new ArrayList<String>();
for (int i = 0; i < statements.size(); i++) {
String xOfy = (i + 1) + " of " + statements.size();
runStatement(expectErrors, xOfy, statements.get(i), c);
}
}
for (String statement : statements) {
if (!noDuplicates.contains(statement)) {
noDuplicates.add(statement);
}
}
/**
* Execute the statement.
*/
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) {
for (int i = 0; i < noDuplicates.size(); i++) {
String xOfy = (i + 1) + " of " + noDuplicates.size();
runStatement(expectErrors, xOfy, noDuplicates.get(i), c);
}
}
PreparedStatement pstmt = null;
try {
/**
* Execute the statement.
*/
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) {
// trim and remove trailing ; or /
stmt = stmt.trim();
if (stmt.endsWith(";")){
stmt = stmt.substring(0, stmt.length()-1);
} else if (stmt.endsWith("/")) {
stmt = stmt.substring(0, stmt.length()-1);
}
PreparedStatement pstmt = null;
try {
logger.trace("executing "+oneOf+" "+ getSummary(stmt));
// trim and remove trailing ; or /
stmt = stmt.trim();
if (stmt.endsWith(";")) {
stmt = stmt.substring(0, stmt.length() - 1);
} else if (stmt.endsWith("/")) {
stmt = stmt.substring(0, stmt.length() - 1);
}
pstmt = c.prepareStatement(stmt);
pstmt.execute();
logger.trace("executing " + oneOf + " " + getSummary(stmt));
} catch (Exception e) {
if (expectErrors){
logger.info(" ... ignoring error executing "+getSummary(stmt)+" error: "+e.getMessage());
e.printStackTrace();
} else {
String msg = "Error executing stmt[" + stmt+"] error["+e.getMessage()+"]";
throw new RuntimeException(msg, e);
}
} finally {
if (pstmt != null){
try {
pstmt.close();
} catch (SQLException e){
logger.error("Error closing pstmt", e);
}
}
}
}
pstmt = c.prepareStatement(stmt);
pstmt.execute();
/**
* Break up the sql in reader into a list of statements using the semi-colon
* character;
*/
protected List<String> parseStatements(StringReader reader) {
} catch (Exception e) {
if (expectErrors) {
logger.info(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
e.printStackTrace();
} else {
String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
throw new RuntimeException(msg, e);
}
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
logger.error("Error closing pstmt", e);
}
}
}
}
try {
BufferedReader br = new BufferedReader(reader);
/**
* Break up the sql in reader into a list of statements using the semi-colon
* character;
*/
protected List<String> parseStatements(StringReader reader) {
ArrayList<String> statements = new ArrayList<String>();
try {
BufferedReader br = new BufferedReader(reader);
StringBuilder sb = new StringBuilder();
String s;
while ((s = br.readLine()) != null) {
s = s.trim();
int semiPos = s.indexOf(';');
if (semiPos == -1) {
sb.append(s).append(" ");
ArrayList<String> statements = new ArrayList<String>();
} else if (semiPos == s.length()-1) {
// semicolon at end of line
sb.append(s);
statements.add(sb.toString().trim());
sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
String s;
while ((s = br.readLine()) != null) {
s = s.trim();
int semiPos = s.indexOf(';');
if (semiPos == -1) {
sb.append(s).append(" ");
} else {
// semicolon in middle of line
String preSemi = s.substring(0, semiPos);
sb.append(preSemi);
statements.add(sb.toString().trim());
sb = new StringBuilder();
sb.append(s.substring(semiPos+1));
} else if (semiPos == s.length() - 1) {
// semicolon at end of line
sb.append(s);
statements.add(sb.toString().trim());
sb = new StringBuilder();
}
}
} else {
// semicolon in middle of line
String preSemi = s.substring(0, semiPos);
sb.append(preSemi);
statements.add(sb.toString().trim());
sb = new StringBuilder();
sb.append(s.substring(semiPos + 1));
return statements;
} catch (IOException e) {
throw new PersistenceException(e);
}
}
}
}
private String getSummary(String s){
if (s.length() > summaryLength){
return s.substring(0, summaryLength).trim()+"...";
}
return s;
}
return statements;
} catch (IOException e) {
throw new PersistenceException(e);
}
}
private String getSummary(String s) {
if (s.length() > summaryLength) {
return s.substring(0, summaryLength).trim() + "...";
}
return s;
}
}
@@ -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;
@@ -71,19 +77,17 @@ import com.avaje.ebeaninternal.server.type.TypeManager;
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>();
@@ -92,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
}
/**
@@ -101,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;
@@ -341,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;
@@ -367,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();
@@ -385,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();
@@ -564,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>
@@ -803,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.
@@ -853,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();
@@ -965,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);
@@ -1055,7 +1041,7 @@ public class BeanDescriptor<T> {
for (int i = 0; i < propertiesOneImported.length; i++) {
BeanPropertyAssocMany<?> many = propertiesOneImported[i].getRelationshipProperty();
if (many != null) {
propertiesOneImported[i].cacheDelete(true, deleteRequest);
propertiesOneImported[i].cacheDelete(true, deleteRequest.getBean());
}
}
}
@@ -1148,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.
*/
@@ -1177,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);
}
@@ -1318,10 +1325,8 @@ public class BeanDescriptor<T> {
public EntityBean createEntityBean() {
try {
// Note factoryType is used indirectly via beanReflect
EntityBean eb = (EntityBean) beanReflect.createEntityBean();
return eb;
return (EntityBean) beanReflect.createEntityBean();
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -2308,6 +2313,9 @@ public class BeanDescriptor<T> {
for (int j = 0; j < propertiesNonTransient.length; j++) {
propertiesNonTransient[j].jsonWrite(ctx, bean);
}
for (int j = 0; j < propertiesTransient.length; j++) {
propertiesTransient[j].jsonWrite(ctx, bean);
}
}
}
@@ -2372,9 +2380,13 @@ public class BeanDescriptor<T> {
}
@SuppressWarnings("unchecked")
private T createJsonBean() {
return (T)createEntityBean();
}
private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) {
T bean = (T) createEntityBean();
T bean = createJsonBean();
ctx.pushBean(bean, path, this);
do {
@@ -2432,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) {
@@ -610,6 +590,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
secondaryPropsJoins(info);
}
// Set inheritance info
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
setInheritanceInfo(info);
}
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
DeployBeanDescriptor<?> deployBeanDescriptor = info.getDescriptor();
Integer key = getUniqueHash(deployBeanDescriptor);
@@ -617,6 +602,33 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
/**
* Sets the inheritance info. ~EMG fix for join problem
*
* @param info the new inheritance info
*/
private void setInheritanceInfo(DeployBeanInfo<?> info) {
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
if (assoc != null){
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
}
}
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
if (assoc != null){
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
}
}
}
private Integer getUniqueHash(DeployBeanDescriptor<?> deployBeanDescriptor) {
int hashCode = deployBeanDescriptor.getFullName().hashCode();
@@ -1430,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.
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -139,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);
@@ -195,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())
@@ -211,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;
@@ -464,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){
@@ -492,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();
@@ -505,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("=? ");
}
@@ -752,7 +810,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
Object value = getValueIntercept(bean);
if (value != null){
ctx.pushParentBeanMany(bean);
help.jsonWrite(ctx, name, value, include != null);
if (help != null){
help.jsonWrite(ctx, name, value, include != null);
} else {
ctx.toJson(name, (Collection<?>)value);
}
ctx.popParentBeanMany();
}
}
@@ -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.
@@ -8,7 +8,7 @@ public interface DbSqlContext {
/**
* Add a join to the sql query.
*/
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
public void pushSecondaryTableAlias(String alias);
@@ -38,6 +38,10 @@ public class ManyType {
}
}
public boolean isMap() {
return Underlying.MAP.equals(underlying);
}
/**
* Return the matching Query type.
*/
@@ -40,6 +40,8 @@ public final class TableJoin {
*/
private final BeanCascadeInfo cascadeInfo;
private final InheritInfo inheritInfo;
/**
* Properties as an array.
*/
@@ -59,6 +61,7 @@ public final class TableJoin {
this.table = InternString.intern(deploy.getTable());
this.type = InternString.intern(deploy.getType());
this.cascadeInfo = deploy.getCascadeInfo();
this.inheritInfo = deploy.getInheritInfo();
DeployTableJoinColumn[] deployCols = deploy.columns();
this.columns = new TableJoinColumn[deployCols.length];
@@ -79,28 +82,6 @@ public final class TableJoin {
}
/**
* Create a tableJoin based on this object but with different alias.
*/
public TableJoin createWithAlias(String localAlias, String foreignAlias) {
return new TableJoin(this, localAlias, foreignAlias);
}
/**
* Construct a copy but with different table alias'.
*/
private TableJoin(TableJoin join, String localAlias, String foreignAlias) {
// copy the immutable fields
this.importedPrimaryKey = join.importedPrimaryKey;
this.table = join.table;
this.type = join.type;
this.cascadeInfo = join.cascadeInfo;
this.properties = join.properties;
this.columns = join.columns;
}
public String toString() {
StringBuilder sb = new StringBuilder(30);
sb.append(type).append(" ").append(table).append(" ");
@@ -189,8 +170,10 @@ public final class TableJoin {
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
ctx.addJoin(forceOuterJoin ? LEFT_OUTER : type, table, columns(), a1, a2);
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2, inheritance);
return forceOuterJoin || LEFT_OUTER.equals(type);
}
@@ -198,6 +181,7 @@ public final class TableJoin {
* Explicitly add a (non-outer) join.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
ctx.addJoin(JOIN, table, columns(), a1, a2);
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
ctx.addJoin(JOIN, table, columns(), a1, a2, inheritance);
}
}
@@ -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);
}

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