Compare commits

...
Author SHA1 Message Date
rbygrave 0f26160e1b [maven-release-plugin] prepare release avaje-ebeanorm-4.3.1 2014-11-27 01:00:53 +13:00
rbygrave cb66aaf66c Change pom to attach javadocs as jar for deploy to central 2014-11-27 00:58:47 +13:00
rbygrave 78b1c05de1 [maven-release-plugin] prepare for next development iteration 2014-11-26 23:54:47 +13:00
rbygrave c70a4f80eb [maven-release-plugin] prepare release avaje-ebeanorm-4.3.0 2014-11-26 23:54:21 +13:00
rbygrave cc788e281f No effective change - javadoc update 2014-11-26 23:53:06 +13:00
rbygrave d55a7b26f8 No effective change - javadoc warnings 2014-11-26 23:08:01 +13:00
rbygrave 2195d7ecbc #211 - ENH: Add support for File as a bean type ... for streaming into/from db 2014-11-26 22:45:33 +13:00
rbygrave 983391af94 #210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control 2014-11-26 20:24:14 +13:00
rbygrave 478053263f #210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control 2014-11-26 20:13:30 +13:00
rbygrave 1cd31e13b9 bump pom to 4.3.0-SNAPSHOT 2014-11-25 23:55:24 +13:00
rbygrave 374d7faae9 bump pom to 4.3.0-SNAPSHOT 2014-11-25 23:54:46 +13:00
rbygrave 6a6cff2236 Fix for #209 - refresh() ... does not refresh OneToMany or ManyToMany properties 2014-11-25 23:40:21 +13:00
rbygrave d5d6ac675b Add support for java.time types for use with @CreatedTimestamp and @UpdatedTimestamp 2014-11-24 20:41:46 +13:00
rbygrave 8ea1f51c7f Remove deprecated Expression.join() methods 2014-11-23 00:53:48 +13:00
rbygrave 6ecb77dcc2 Fix for #206 - Reuse SqlUpdate when binding a list that can vary in size 2014-11-22 23:40:14 +13:00
rbygrave 216806074c Remove PersistenceContextScope.NONE for now 2014-11-22 12:46:40 +13:00
rbygrave 65cb2ae94d #203: ENH: Add Transaction hook ... for preCommit(), postCommit(), postRollback() 2014-11-22 12:14:21 +13:00
rbygrave 6677a6199c Fix for #207: Add support for PeristenceContextScope QUERY and NONE (in addition to the existing TRANSACTION scope) 2014-11-20 00:53:32 +13:00
rbygrave 55eaa0b484 Fix for #204: Revert change from #163 - Add back support for DataSource with autocommit=true 2014-11-19 20:10:50 +13:00
rbygrave 353f662d70 Fix for #205: For JsonContext API wrap checked IOException in runtime JsonIOException 2014-11-19 20:00:31 +13:00
rbygrave c95d76d6f6 Fix for #201: Change BeanPersistListener to use isRegisterFor() method rather than generics type (just like BeanPersistController) 2014-11-19 19:43:34 +13:00
Rob Bygrave e04d6979d1 Merge pull request #202 from ebean-orm/java8
Add support for java.time types, tidy up ScalarTypes
2014-11-19 00:13:37 +13:00
rbygrave b8078083b1 Add MonthDay support, ScalarTypeMonthDay 2014-11-18 23:57:53 +13:00
rbygrave 07cdb69a5b Add Period support via CompoundTypePeriod, rename server/reflect to server/properties 2014-11-18 23:09:26 +13:00
rbygrave 98f6e1e6af Update ScalarTypeMonth and ScalarTypeDayOfWeek 2014-11-18 20:06:13 +13:00
rbygrave fd4d757f87 WIP Java8 types - ScalarTypeDuration etc 2014-11-18 01:20:26 +13:00
rbygrave b71b0fc8e2 WIP Java8 types - ScalarTypeDuration etc 2014-11-17 01:09:43 +13:00
rbygrave 4303bcfd13 No effective change - tidy up on DeployCreateProperties 2014-11-17 00:23:30 +13:00
rbygrave 7f30dc7b1e WIP Java8 types - ScalarTypeDuration etc 2014-11-17 00:05:20 +13:00
rbygrave 4a882b537c #197 - Add Query findEach() findEachWhile() ... as better method names for findVisit(). More consistent with forEach() methods 2014-11-14 21:26:32 +13:00
rbygrave 641385aab5 ScalarTypeLocalDate update - some test ordering issues 2014-11-14 01:01:32 +13:00
rbygrave 33ab7eb7a5 Add ScalarTypeLocalDate 2014-11-13 23:13:51 +13:00
Rob Bygrave f6674f8e3b [maven-release-plugin] prepare for next development iteration 2014-11-13 01:28:18 +13:00
Rob Bygrave 4e8b3f22de [maven-release-plugin] prepare release avaje-ebeanorm-4.2.0 2014-11-13 01:27:58 +13:00
Rob Bygrave 55e43ddc36 Add whitespace for MANIFEST.MF 2014-11-13 01:26:47 +13:00
Rob Bygrave 97d75c06a1 Revert MANIFEST.MF - invalid header 2014-11-13 01:15:47 +13:00
Rob Bygrave adcfa939e4 revert MANIFEST.MF - invalid jackson part 2014-11-13 01:11:07 +13:00
Rob Bygrave 8a4d78a3f5 Fix JSON for ScalarTypePostgresHstore with field name and nulls 2014-11-13 00:49:20 +13:00
Rob Bygrave 04e77d9949 Bump pom to 4.2.0-SNAPSHOT 2014-11-12 23:30:31 +13:00
Rob Bygrave 76e7b8d199 Add to Model the apply(PathProperties) method 2014-11-12 23:06:26 +13:00
Rob Bygrave 00f85a42f8 JSON Refactor - move EJson into text.json package 2014-11-12 22:56:42 +13:00
Rob Bygrave e649a70509 make SimpleTextParser class not public 2014-11-12 22:35:48 +13:00
Rob Bygrave 2ec50d2c71 Add query.apply(PathProperties) 2014-11-12 22:24:37 +13:00
Rob Bygrave eaee3a9c02 JSON refactor - tidy up 2014-11-12 22:10:28 +13:00
Rob Bygrave 221272328c WIP JSON refactor - tidy up ScalarTypes 2014-11-12 00:17:24 +13:00
Rob Bygrave 1677917465 Move from javax.json to jackson-core for parser/generator 2014-11-10 20:31:11 +13:00
Rob Bygrave bfefdbac41 Merge from JSON refactoring branch 2014-11-06 23:32:27 +13:00
Rob Bygrave 58755a51a9 JSON Refactor - compound type support 2014-11-06 23:18:20 +13:00
Rob Bygrave b313809894 WIP json refactor - initial tidy 2014-11-05 23:15:09 +13:00
rbygrave 0f3b1eb812 Tidy up test - TestInfoOneToOne, oneToOne cascade update 2014-10-29 23:45:24 +13:00
rbygrave 306cc4adf4 Add test for oneToOne cascade update 2014-10-29 23:43:11 +13:00
rbygrave ed88978173 No effective change - javadoc tweak on params 2014-10-29 23:41:40 +13:00
rbygrave ae266968b4 [maven-release-plugin] prepare for next development iteration 2014-10-29 00:11:43 +13:00
rbygrave aa86454cc4 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.9 2014-10-29 00:11:20 +13:00
rbygrave c99bec4940 Add First/Second/SuperSecond test 2014-10-29 00:07:25 +13:00
rbygrave 7426274ef6 bump agent to 4.1.9 2014-10-28 23:46:20 +13:00
Rob Bygrave 130b7e3327 Add another self referencing test - SelfParent 2014-10-20 23:06:50 +13:00
Rob Bygrave de201715fc #196 - Add support for using java.net.InetAddress in entity beans 2014-10-20 22:36:18 +13:00
Rob Bygrave 241f6f3d6d #196 - Add support for using java.net.InetAddress in entity beans 2014-10-20 22:31:22 +13:00
Rob Bygrave 1210aca027 Add test for SqlUpdate in various transaction scopes 2014-10-20 20:18:17 +13:00
Rob Bygrave e653c34211 Fix for #188 - ClassPathSearch not searching in running Jar 2014-10-16 23:15:16 +13:00
Rob Bygrave 131750cf85 No effective change - format 2014-10-16 22:58:13 +13:00
Rob Bygrave 17b16fd5d9 Merge pull request #194 from nedge/master
FIX for ManyToOne targetType & OSGI MANIFEST
2014-10-15 19:55:17 +13:00
Eddie Mc Greal 1065a84709 FIX for ManyToOne targetType & OSGI MANIFEST 2014-10-14 08:10:31 +02:00
Rob Bygrave 13791a2e58 Update version in readme 2014-10-08 22:13:48 +13:00
Rob Bygrave 277703d750 [maven-release-plugin] prepare for next development iteration 2014-10-08 21:44:25 +13:00
Rob Bygrave 637cbc6489 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.8 2014-10-08 21:44:15 +13:00
Rob Bygrave 5d09ca16d6 Fix for #192 - Backslash literal in LIKE expression - Postgres (and H2, MySql) behaviour 2014-10-08 21:42:39 +13:00
Rob Bygrave 6ed1f25e22 [maven-release-plugin] prepare for next development iteration 2014-09-10 00:02:41 +12:00
Rob Bygrave b8ae443074 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.7 2014-09-10 00:02:31 +12:00
Rob Bygrave ab9762bbd9 Fix for #190 - javax.persistence.PersistenceException: Server [null] was not found? at EntityBeanIntercept.loadBean(EntityBeanIntercept.java:693) 2014-09-10 00:00:49 +12:00
Rob Bygrave 2c90b239cd [maven-release-plugin] prepare for next development iteration 2014-08-22 00:45:30 +12:00
Rob Bygrave 13806767a3 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.6 2014-08-22 00:45:20 +12:00
Rob Bygrave 40e43b9d5f update agent and maven plugin versions to 4.1.6 2014-08-22 00:44:08 +12:00
Rob Bygrave 29f1e63146 Fix for #186 - Fix for Lazy load when fetched optional ManyToOne bean is null in database 2014-08-22 00:41:12 +12:00
rbygrave 5cf008b521 Fix for #182 Modify pom removing provided scope from slf4j-api dependency 2014-08-12 23:30:41 +12:00
rbygrave 3e788d4598 [maven-release-plugin] prepare for next development iteration 2014-08-01 21:43:53 +12:00
rbygrave 8a29454893 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.5 2014-08-01 21:43:31 +12:00
rbygrave 62c083a6ea Add LongMaxUpdater, change CQueryPlanStats to use LongMaxUpdater 2014-07-29 23:48:38 +12:00
rbygrave 7f8e4be396 Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-07-29 23:44:38 +12:00
rbygrave 2ad39974e8 No effective change - code format and javadoc tidy 2014-07-29 23:44:03 +12:00
Rob Bygrave 664e95d87b Merge pull request #180 from rvbiljouw/master
Index annotation for generating single-column indexes in DDL
2014-07-29 22:45:39 +12:00
Rick van Biljouw 5cf668f65b Disallow Index annotation on fields that have a relationship annotation (OneToMany, ManyToOne or OneToOne) 2014-07-28 19:44:25 +01:00
Rick van Biljouw 31f56621ff Added @Index annotation to generate single-column indexes 2014-07-28 18:43:50 +01:00
rbygrave 11a6cd04ea Update README with latest version 2014-07-26 00:12:41 +12:00
rbygrave fdb573bfe2 Fix for #177 - EbeanServer.refresh() not loading/refreshing @Lob property unless it is annotated with @Basic(fetch=FetchType.EAGER) 2014-07-26 00:09:26 +12:00
rbygrave ae0e1fe691 No effective change - remove some incorrect javadoc 2014-07-25 23:21:14 +12:00
rbygrave 0511e01273 [maven-release-plugin] prepare for next development iteration 2014-07-24 19:45:56 +12:00
rbygrave f3bd38536c [maven-release-plugin] prepare release avaje-ebeanorm-4.1.4 2014-07-24 19:45:30 +12:00
rbygrave 139a558165 Add test TestPropertyChangeListener 2014-07-21 22:59:31 +12:00
rbygrave 53b40f7ce0 Update .gitignore for h2 db directory 2014-07-21 22:32:05 +12:00
rbygrave e50656871d Fix for #175 - Change to use System.getProperty("ebean.serverfactory")
rather than GlobalProperties
2014-07-21 22:31:01 +12:00
rbygrave f7ece0f13c Fix for #174 - Incorrect SQL generated with syntax error at the "order by" clause 2014-07-21 21:49:31 +12:00
rbygrave 562c6fafc9 Update maven-enhancer to 4.1.5, improve test - TestNotEnhancedMappedSuper 2014-07-19 19:41:57 +12:00
rbygrave 63e9f603ec Code cleanup - move old iterator/while loops to for loops 2014-07-19 19:10:13 +12:00
rbygrave 0c4fc4eea3 Fix for #173 - Ability to use a MappedSuperClass without being enhanced as long as it doesn't have persistent fields 2014-07-19 17:49:03 +12:00
rbygrave 2dca1bdf5b Fix for #172 - Regression: @Where predicates not applied in eager fetch (but are on lazy load) 2014-07-18 23:35:14 +12:00
rbygrave 6403a83e0e No effective change - format only 2014-07-18 22:52:49 +12:00
Rob Bygrave b57b91ffef [maven-release-plugin] prepare for next development iteration 2014-07-16 23:18:02 +12:00
Rob Bygrave 4ba5702211 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.3 2014-07-16 23:17:06 +12:00
Rob Bygrave 2e7aeae004 Bump version in README 2014-07-16 23:14:52 +12:00
rbygrave 3f2676ab49 Fix for #168 - ENH: DataSource - add support for configuring autocommit=true 2014-07-16 21:58:32 +12:00
rbygrave 3f29acb25c Fix for #169 - Caused by: java.lang.ClassCastException: org.example.Order cannot be cast to org.example.Customer 2014-07-15 23:09:08 +12:00
rbygrave 6d1a41f979 Fix for #167 - Ebean try to do update child nodes instead of inserting them on cascading save resulting in OptimisticLockException 2014-07-15 22:05:56 +12:00
Rob Bygrave aee46955a7 Add test which saves parent and child using same pk value 2014-07-10 23:27:18 +12:00
rbygrave 38a93b9814 Update README with new version 2014-07-10 22:41:38 +12:00
rbygrave 5f2ad0dc62 [maven-release-plugin] prepare for next development iteration 2014-07-10 21:41:01 +12:00
Rob Bygrave 0ceda6406e WIP on JSON refactor 2014-06-28 03:09:10 +12:00
407 changed files with 19549 additions and 10437 deletions
+1
View File
@@ -6,6 +6,7 @@
target/
logs/
log/
/db
/mydb.db
+1 -4
View File
@@ -1,15 +1,12 @@
[![Stories in Ready](https://badge.waffle.io/ebean-orm/avaje-ebeanorm.png?label=ready&title=Ready)](https://waffle.io/ebean-orm/avaje-ebeanorm)
avaje-ebeanorm
==============
- Release - 4.0.2 - May 19th: https://github.com/ebean-orm/avaje-ebeanorm/wiki/4.0.2-Release
- Release - 4.0.3 and 4.0.4 contained some bug fixes plus the Model and Finder objects.
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.0.4</version>
<version>4.3.0</version>
</dependency>
+29 -17
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.1.2</version>
<version>4.3.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -48,6 +48,18 @@
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
@@ -77,13 +89,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
@@ -94,7 +99,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.1.2</version>
<version>4.1.9</version>
<scope>test</scope>
</dependency>
@@ -177,7 +182,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
@@ -186,7 +191,7 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.1.2</version>
<version>4.1.9</version>
<executions>
<!-- Not going to enhance Model bean -->
<execution>
@@ -218,8 +223,16 @@
</includes>
</configuration>
</plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
@@ -227,19 +240,18 @@
<configuration>
<doctitle>Ebean 4</doctitle>
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
<!-- <excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames> -->
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>site</phase>
<goals>
<goal>aggregate</goal>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
+343 -287
View File
@@ -63,34 +63,38 @@ import com.avaje.ebean.text.json.JsonContext;
* created automatically they are configured using information in the
* ebean.properties file.
* </p>
*
* <pre>{@code
*
* // fetch shipped orders (and also their customer)
* List<Order> list = Ebean.find(Order.class)
* .fetch("customer")
* .where()
* .eq("status.code", Order.Status.SHIPPED)
* .findList();
*
* // read/use the order list ...
* for (Order order : list) {
* Customer customer = order.getCustomer();
* ...
* }
*
* }</pre>
*
* <pre class="code">
* // fetch shipped orders (and also their customer)
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .fetch(&quot;customer&quot;)
* .where()
* .eq(&quot;status.code&quot;, Order.Status.SHIPPED)
* .findList();
* <pre>{@code
*
* // fetch order 10, modify and save
* Order order = Ebean.find(Order.class, 10);
*
* // read/use the order list ...
* for (Order order : list) {
* Customer customer = order.getCustomer();
* ...
* }
* </pre>
* OrderStatus shipped = Ebean.getReference(OrderStatus.class,"SHIPPED");
* order.setStatus(shipped);
* order.setShippedDate(shippedDate);
* ...
*
* <pre class="code">
* // fetch order 10, modify and save
* Order order = Ebean.find(Order.class, 10);
*
* OrderStatus shipped = Ebean.getReference(OrderStatus.class,&quot;SHIPPED&quot;);
* order.setStatus(shipped);
* order.setShippedDate(shippedDate);
* ...
*
* // implicitly creates a transaction and commits
* Ebean.save(order);
* </pre>
* // implicitly creates a transaction and commits
* Ebean.save(order);
*
* }</pre>
*
* <p>
* When you have multiple databases and need access to a specific one the
@@ -98,20 +102,22 @@ import com.avaje.ebean.text.json.JsonContext;
* specific database.
* </p>
*
* <pre class="code">
* // Get access to the Human Resources EbeanServer/Database
* EbeanServer hrDb = Ebean.getServer(&quot;hr&quot;);
* <pre> {@code
*
* // Get access to the Human Resources EbeanServer/Database
* EbeanServer hrDb = Ebean.getServer("hr");
*
*
* // fetch contact 3 from the HR database
* Contact contact = hrDb.find(Contact.class, 3);
* // fetch contact 3 from the HR database
* Contact contact = hrDb.find(Contact.class, 3);
*
* contact.setName(&quot;I'm going to change&quot;);
* ...
* contact.setName("I'm going to change");
* ...
*
* // save the contact back to the HR database
* hrDb.save(contact);
* </pre>
* // save the contact back to the HR database
* hrDb.save(contact);
*
* }</pre>
*/
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
@@ -242,12 +248,12 @@ public final class Ebean {
* Ebean.
* </p>
*
* <pre class="code">
* // use the &quot;hr&quot; database
* EbeanServer hrDatabase = Ebean.getServer(&quot;hr&quot;);
* <pre>{@code
* // use the "hr" database
* EbeanServer hrDatabase = Ebean.getServer("hr");
*
* Person person = hrDatabase.find(Person.class, 10);
* </pre>
* }</pre>
*
* @param name
* the name of the server, use null for the 'default server'
@@ -281,7 +287,7 @@ public final class Ebean {
* Register the server with this Ebean singleton. Specify if the registered
* server is the primary/default server.
*/
protected static void register(EbeanServer server, boolean isPrimaryServer) {
public static void register(EbeanServer server, boolean isPrimaryServer) {
serverMgr.register(server, isPrimaryServer);
}
@@ -323,22 +329,24 @@ public final class Ebean {
* etc.
* </p>
*
* <pre class="code">
* // start a transaction (stored in a ThreadLocal)
* Ebean.beginTransaction();
* try {
* Order order = Ebean.find(Order.class,10); ...
* <pre>{@code
*
* // start a transaction (stored in a ThreadLocal)
* Ebean.beginTransaction();
* try {
* Order order = Ebean.find(Order.class,10); ...
*
* Ebean.save(order);
*
* Ebean.save(order);
* Ebean.commitTransaction();
*
* Ebean.commitTransaction();
*
* } finally {
* // rollback if we didn't commit
* // i.e. an exception occurred before commitTransaction().
* Ebean.endTransaction();
* }
* </pre>
* } finally {
* // rollback if we didn't commit
* // i.e. an exception occurred before commitTransaction().
* Ebean.endTransaction();
* }
*
* }</pre>
*
* <p>
* If you want to externalise the transaction management then you should be
@@ -372,6 +380,19 @@ public final class Ebean {
return serverMgr.getPrimaryServer().currentTransaction();
}
/**
* Register a TransactionCallback on the currently active transaction.
* <p/>
* If there is no currently active transaction then a PersistenceException is thrown.
*
* @param transactionCallback the transaction callback to be registered with the current transaction
*
* @throws PersistenceException if there is no currently active transaction
*/
public static void register(TransactionCallback transactionCallback) throws PersistenceException {
serverMgr.getPrimaryServer().register(transactionCallback);
}
/**
* Commit the current transaction.
*/
@@ -397,17 +418,19 @@ public final class Ebean {
* Code example:
* </p>
*
* <pre class="code">
* Ebean.beginTransaction();
* try {
* // do some fetching and or persisting
* // commit at the end Ebean.commitTransaction();
* <pre>{@code
* Ebean.beginTransaction();
* try {
* // do some fetching and or persisting
*
* // commit at the end
* Ebean.commitTransaction();
*
* } finally {
* // if commit didn't occur then rollback the transaction
* Ebean.endTransaction();
* }
* </pre>
* } finally {
* // if commit didn't occur then rollback the transaction
* Ebean.endTransaction();
* }
* }</pre>
*/
public static void endTransaction() {
serverMgr.getPrimaryServer().endTransaction();
@@ -440,15 +463,15 @@ public final class Ebean {
* saving an order will also save all its details.
* </p>
*
* <pre class="code">
* public class Order { ...
* <pre>{@code
* public class Order { ...
*
* &#064;OneToMany(cascade=CascadeType.ALL, mappedBy=&quot;order&quot;)
* &#064;JoinColumn(name=&quot;order_id&quot;)
* List&lt;OrderDetail&gt; details;
* ...
* }
* </pre>
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* @JoinColumn(name="order_id")
* List<OrderDetail> details;
* ...
* }
* }</pre>
*
* <p>
* When a save cascades via a OneToMany or ManyToMany Ebean will automatically
@@ -485,16 +508,16 @@ public final class Ebean {
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
* <pre>{@code
*
* Customer customer = Ebean.find(Customer, id);
* Customer customer = Ebean.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* Ebean.markAsDirty(customer);
* Ebean.save(customer);
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* Ebean.markAsDirty(customer);
* Ebean.save(customer);
*
* </pre>
* }</pre>
*/
public static void markAsDirty(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().markAsDirty(bean);
@@ -523,15 +546,15 @@ public final class Ebean {
* properties are included instead.
* </p>
*
* <pre class="code">
* <pre>{@code
*
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName(&quot;ModifiedNameNoOCC&quot;);
* ebeanServer.update(customer);
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName("ModifiedNameNoOCC");
* ebeanServer.update(customer);
*
* </pre>
* }</pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
@@ -665,12 +688,14 @@ public final class Ebean {
/**
* Refresh a 'many' property of a bean.
*
* <pre class="code">
* Order order = ...;
* ...
* // refresh the order details...
* Ebean.refreshMany(order, &quot;details&quot;);
* </pre>
* <pre>{@code
*
* Order order = ...;
* ...
* // refresh the order details...
* Ebean.refreshMany(order, "details");
*
* }</pre>
*
* @param bean
* the entity bean containing the List Set or Map to refresh.
@@ -687,16 +712,18 @@ public final class Ebean {
* This is sometimes described as a proxy (with lazy loading).
* </p>
*
* <pre class="code">
* Product product = Ebean.getReference(Product.class, 1);
* <pre>{@code
*
* Product product = Ebean.getReference(Product.class, 1);
*
* // You can get the id without causing a fetch/lazy load
* Integer productId = product.getId();
* // You can get the id without causing a fetch/lazy load
* Integer productId = product.getId();
*
* // If you try to get any other property a fetch/lazy loading will occur
* // This will cause a query to execute...
* String name = product.getName();
* </pre>
* // If you try to get any other property a fetch/lazy loading will occur
* // This will cause a query to execute...
* String name = product.getName();
*
* }</pre>
*
* @param beanType
* the type of entity bean
@@ -726,23 +753,23 @@ public final class Ebean {
* not invoke a DB query.
* </p>
*
* <pre class="code">
* <pre>{@code
*
* // find orders and their customers
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .fetch(&quot;customer&quot;)
* .orderBy(&quot;id&quot;)
* // find orders and their customers
* List<Order> list = Ebean.find(Order.class)
* .fetch("customer")
* .orderBy("id")
* .findList();
*
* // sort by customer name ascending, then by order shipDate
* // ... then by the order status descending
* Ebean.sort(list, &quot;customer.name, shipDate, status desc&quot;);
* // sort by customer name ascending, then by order shipDate
* // ... then by the order status descending
* Ebean.sort(list, "customer.name, shipDate, status desc");
*
* // sort by customer name descending (with nulls low)
* // ... then by the order id
* Ebean.sort(list, &quot;customer.name desc nullsLow, id&quot;);
* // sort by customer name descending (with nulls low)
* // ... then by the order id
* Ebean.sort(list, "customer.name desc nullsLow, id");
*
* </pre>
* }</pre>
*
* @param list
* the list of entity beans
@@ -756,41 +783,43 @@ public final class Ebean {
/**
* Find a bean using its unique id. This will not use caching.
*
* <pre class="code">
* // Fetch order 1
* Order order = Ebean.find(Order.class, 1);
* </pre>
* <pre>{@code
* // Fetch order 1
* Order order = Ebean.find(Order.class, 1);
* }</pre>
*
* <p>
* If you want more control over the query then you can use createQuery() and
* Query.findUnique();
* </p>
*
* <pre class="code">
* // ... additionally fetching customer, customer shipping address,
* // order details, and the product associated with each order detail.
* // note: only product id and name is fetch (its a &quot;partial object&quot;).
* // note: all other objects use &quot;*&quot; and have all their properties fetched.
* <pre>{@code
* // ... additionally fetching customer, customer shipping address,
* // order details, and the product associated with each order detail.
* // note: only product id and name is fetch (its a "partial object").
* // note: all other objects use "*" and have all their properties fetched.
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.setId(1);
* query.fetch(&quot;customer&quot;);
* query.fetch(&quot;customer.shippingAddress&quot;);
* query.fetch(&quot;details&quot;);
* Query<Order> query = Ebean.find(Order.class)
* .setId(1)
* .fetch("customer")
* .fetch("customer.shippingAddress")
* .fetch("details")
* .query();
*
* // fetch associated products but only fetch their product id and name
* query.fetch(&quot;details.product&quot;, &quot;name&quot;);
* // fetch associated products but only fetch their product id and name
* query.fetch("details.product", "name");
*
* // traverse the object graph...
* // traverse the object graph...
*
* Order order = query.findUnique();
* Customer customer = order.getCustomer();
* Address shippingAddress = customer.getShippingAddress();
* List&lt;OrderDetail&gt; details = order.getDetails();
* OrderDetail detail0 = details.get(0);
* Product product = detail0.getProduct();
* String productName = product.getName();
* </pre>
* Order order = query.findUnique();
* Customer customer = order.getCustomer();
* Address shippingAddress = customer.getShippingAddress();
* List<OrderDetail> details = order.getDetails();
* OrderDetail detail0 = details.get(0);
* Product product = detail0.getProduct();
* String productName = product.getName();
*
* }</pre>
*
* @param beanType
* the type of entity bean to fetch
@@ -802,7 +831,7 @@ public final class Ebean {
}
/**
* Create a <a href="SqlQuery.html">SqlQuery</a> for executing native sql
* Create a SqlQuery for executing native sql
* query statements.
* <p>
* Note that you can use raw SQL with entity beans, refer to the SqlSelect
@@ -860,15 +889,17 @@ public final class Ebean {
* deployment orm xml file.
* </p>
*
* <pre class="code">
* // Use a namedQuery
* UpdateSql update = Ebean.createNamedSqlUpdate(&quot;update.topic.count&quot;);
* <pre>{@code
*
* // Use a namedQuery
* UpdateSql update = Ebean.createNamedSqlUpdate("update.topic.count");
*
* update.setParameter(&quot;count&quot;, 1);
* update.setParameter(&quot;topicId&quot;, 50);
* update.setParameter("count", 1);
* update.setParameter("topicId", 50);
*
* int modifiedCount = update.execute();
* </pre>
* int modifiedCount = update.execute();
*
* }</pre>
*/
public static SqlUpdate createNamedSqlUpdate(String namedQuery) {
return serverMgr.getPrimaryServer().createNamedSqlUpdate(namedQuery);
@@ -883,12 +914,14 @@ public final class Ebean {
* need to bind required parameters and then execute the query.
* </p>
*
* <pre class="code">
* // example
* Query&lt;Order&gt; query = Ebean.createNamedQuery(Order.class, &quot;new.for.customer&quot;);
* query.setParameter(&quot;customerId&quot;, 23);
* List&lt;Order&gt; newOrders = query.findList();
* </pre>
* <pre>{@code
*
* // example
* Query<Order> query = Ebean.createNamedQuery(Order.class, "new.for.customer");
* query.setParameter("customerId", 23);
* List<Order> newOrders = query.findList();
*
* }</pre>
*
* @param beanType
* the class of entity to be fetched
@@ -911,14 +944,15 @@ public final class Ebean {
* moved to {@link #createNamedQuery(Class, String)}.
* </p>
*
* <pre class="code">
* <pre>{@code
*
* String q = &quot;find order fetch details where status = :st&quot;;
* String q = "find order fetch details where status = :st";
*
* List&lt;Order&gt; newOrders = Ebean.createQuery(Order.class, q)
* .setParameter(&quot;st&quot;, Order.Status.NEW)
* List<Order> newOrders = Ebean.>findOrder.class, q)
* .setParameter("st", Order.Status.NEW)
* .findList();
* </pre>
*
* }</pre>
*
* @param query
* the object query
@@ -943,40 +977,43 @@ public final class Ebean {
* Example named updates:
* </p>
*
* <pre class="code">
* package app.data;
* <pre>{@code
* package app.data;
*
* import ...
* import ...
*
* &#064;NamedUpdates(value = {
* &#064;NamedUpdate( name = &quot;setTitle&quot;,
* isSql = false,
* notifyCache = false,
* update = &quot;update topic set title = :title, postCount = :postCount where id = :id&quot;),
* &#064;NamedUpdate( name = &quot;setPostCount&quot;,
* notifyCache = false,
* update = &quot;update f_topic set post_count = :postCount where id = :id&quot;),
* &#064;NamedUpdate( name = &quot;incrementPostCount&quot;,
* notifyCache = false,
* isSql = false,
* update = &quot;update Topic set postCount = postCount + 1 where id = :id&quot;) })
* &#064;Entity
* &#064;Table(name = &quot;f_topic&quot;)
* public class Topic { ...
* </pre>
* @NamedUpdates(value = {
* @NamedUpdate( name = "setTitle",
* isSql = false,
* notifyCache = false,
* update = "update topic set title = :title, postCount = :postCount where id = :id"),
* @NamedUpdate( name = "setPostCount",
* notifyCache = false,
* update = "update f_topic set post_count = :postCount where id = :id"),
* @NamedUpdate( name = "incrementPostCount",
* notifyCache = false,
* isSql = false,
* update = "update Topic set postCount = postCount + 1 where id = :id") })
* @Entity
* @Table(name = "f_topic")
* public class Topic { ...
*
* }</pre>
*
* <p>
* Example using a named update:
* </p>
*
* <pre class="code">
* Update&lt;Topic&gt; update = Ebean.createNamedUpdate(Topic.class, &quot;setPostCount&quot;);
* update.setParameter(&quot;postCount&quot;, 10);
* update.setParameter(&quot;id&quot;, 3);
* <pre>{@code
*
* Update<Topic> update = Ebean.createNamedUpdate(Topic.class, "setPostCount");
* update.setParameter("postCount", 10);
* update.setParameter("id", 3);
*
* int rows = update.execute();
* System.out.println(&quot;rows updated: &quot; + rows);
* </pre>
* int rows = update.execute();
* System.out.println("rows updated: " + rows);
*
* }</pre>
*/
public static <T> Update<T> createNamedUpdate(Class<T> beanType, String namedUpdate) {
@@ -995,21 +1032,22 @@ public final class Ebean {
* An example:
* </p>
*
* <pre class="code">
* <pre>{@code
*
* // The bean name and properties - &quot;topic&quot;,&quot;postCount&quot; and &quot;id&quot;
* // The bean name and properties - "topic","postCount" and "id"
*
* // will be converted into their associated table and column names
* String updStatement = &quot;update topic set postCount = :pc where id = :id&quot;;
* // will be converted into their associated table and column names
* String updStatement = "update topic set postCount = :pc where id = :id";
*
* Update&lt;Topic&gt; update = Ebean.createUpdate(Topic.class, updStatement);
* Update<Topic> update = Ebean.createUpdate(Topic.class, updStatement);
*
* update.set(&quot;pc&quot;, 9);
* update.set(&quot;id&quot;, 3);
* update.set("pc", 9);
* update.set("id", 3);
*
* int rows = update.execute();
* System.out.println(&quot;rows updated:&quot; + rows);
* </pre>
* int rows = update.execute();
* System.out.println("rows updated:" + rows);
*
* }</pre>
*/
public static <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate) {
@@ -1040,38 +1078,37 @@ public final class Ebean {
* which is was created.
* </p>
*
* <pre class="code">
* // Find order 2 additionally fetching the customer, details and details.product
* // name.
* <pre>{@code
* // Find order 2 additionally fetching the customer, details and details.product
* // name.
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.fetch(&quot;customer&quot;);
* query.fetch(&quot;details&quot;);
* query.fetch(&quot;detail.product&quot;, &quot;name&quot;);
* query.setId(2);
* Order order = Ebean.find(Order.class)
* .fetch("customer")
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
*
* Order order = query.findUnique();
* // Find order 2 additionally fetching the customer, details and details.product
* // name.
* // Note: same query as above but using the query language
* // Note: using a named query would be preferred practice
*
* // Find order 2 additionally fetching the customer, details and details.product
* // name.
* // Note: same query as above but using the query language
* // Note: using a named query would be preferred practice
* String oql = "find order fetch customer fetch details fetch details.product (name) where id = :orderId ";
*
* String oql = &quot;find order fetch customer fetch details fetch details.product (name) where id = :orderId &quot;;
* Query<Order> query = Ebean.find(Order.class);
* query.setQuery(oql);
* query.setParameter("orderId", 2);
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.setQuery(oql);
* query.setParameter(&quot;orderId&quot;, 2);
* Order order = query.findUnique();
*
* Order order = query.findUnique();
* // Using a named query
* Query<Order> query = Ebean.find(Order.class, "with.details");
* query.setParameter("orderId", 2);
*
* // Using a named query
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class, &quot;with.details&quot;);
* query.setParameter(&quot;orderId&quot;, 2);
* Order order = query.findUnique();
*
* Order order = query.findUnique();
*
* </pre>
* }</pre>
*
* @param beanType
* the class of entity to be fetched
@@ -1131,19 +1168,21 @@ public final class Ebean {
* Example:
* </p>
*
* <pre class="code">
* // example that uses 'named' parameters
* String s = &quot;UPDATE f_topic set post_count = :count where id = :id&quot;
* <pre>{@code
*
* // example that uses 'named' parameters
* String s = "UPDATE f_topic set post_count = :count where id = :id"
*
* SqlUpdate update = Ebean.createSqlUpdate(s);
* SqlUpdate update = Ebean.createSqlUpdate(s);
*
* update.setParameter(&quot;id&quot;, 1);
* update.setParameter(&quot;count&quot;, 50);
* update.setParameter("id", 1);
* update.setParameter("count", 50);
*
* int modifiedCount = Ebean.execute(update);
* int modifiedCount = Ebean.execute(update);
*
* String msg = &quot;There where &quot; + modifiedCount + &quot;rows updated&quot;;
* </pre>
* String msg = "There where " + modifiedCount + "rows updated";
*
* }</pre>
*
* @param sqlUpdate
* the update sql potentially with bind values
@@ -1164,19 +1203,21 @@ public final class Ebean {
* Example:
* </p>
*
* <pre class="code">
* String sql = &quot;{call sp_order_modify(?,?,?)}&quot;;
* <pre>{@code
*
* String sql = "{call sp_order_modify(?,?,?)}";
*
* CallableSql cs = Ebean.createCallableSql(sql);
* cs.setParameter(1, 27);
* cs.setParameter(2, &quot;SHIPPED&quot;);
* cs.registerOut(3, Types.INTEGER);
* CallableSql cs = Ebean.createCallableSql(sql);
* cs.setParameter(1, 27);
* cs.setParameter(2, "SHIPPED");
* cs.registerOut(3, Types.INTEGER);
*
* Ebean.execute(cs);
* Ebean.execute(cs);
*
* // read the out parameter
* Integer returnValue = (Integer) cs.getObject(3);
* </pre>
* // read the out parameter
* Integer returnValue = (Integer) cs.getObject(3);
*
* }</pre>
*
* @see CallableSql
* @see Ebean#execute(SqlUpdate)
@@ -1192,18 +1233,19 @@ public final class Ebean {
* semantics.
* </p>
*
* <pre class="code">
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* Ebean.execute(scope, new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* ...
*
* }
* });
* </pre>
* <pre>{@code
*
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* Ebean.execute(scope, new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* ...
* }
* });
*
* }</pre>
*/
public static void execute(TxScope scope, TxRunnable r) {
serverMgr.getPrimaryServer().execute(scope, r);
@@ -1216,20 +1258,22 @@ public final class Ebean {
* exception (checked or runtime).
* </p>
*
* <pre class="code">
* Ebean.execute(new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
* <pre>{@code
*
* Ebean.execute(new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName(&quot;u1 mod&quot;);
* u2.setName(&quot;u2 mod&quot;);
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* Ebean.save(u1);
* Ebean.save(u2);
* }
* });
* </pre>
* Ebean.save(u1);
* Ebean.save(u2);
* }
* });
*
* }</pre>
*/
public static void execute(TxRunnable r) {
serverMgr.getPrimaryServer().execute(r);
@@ -1242,18 +1286,20 @@ public final class Ebean {
* semantics.
* </p>
*
* <pre class="code">
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* Ebean.execute(scope, new TxCallable&lt;String&gt;() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* ...
* return u1.getEmail();
* }
* });
* </pre>
* <pre>{@code
*
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* Ebean.execute(scope, new TxCallable<String>() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* ...
* return u1.getEmail();
* }
* });
*
* }</pre>
*
*/
public static <T> T execute(TxScope scope, TxCallable<T> c) {
@@ -1271,22 +1317,24 @@ public final class Ebean {
* (and you specify the return type via generics).
* </p>
*
* <pre class="code">
* Ebean.execute(new TxCallable&lt;String&gt;() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
* <pre>{@code
*
* Ebean.execute(new TxCallable<String>() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName(&quot;u1 mod&quot;);
* u2.setName(&quot;u2 mod&quot;);
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* Ebean.save(u1);
* Ebean.save(u2);
* Ebean.save(u1);
* Ebean.save(u2);
*
* return u1.getEmail();
* }
* });
* </pre>
* return u1.getEmail();
* }
* });
*
* }</pre>
*/
public static <T> T execute(TxCallable<T> c) {
return serverMgr.getPrimaryServer().execute(c);
@@ -1307,7 +1355,7 @@ public final class Ebean {
* </p>
* <p>
* If there is a transaction then this information is placed into the current
* transactions event information. When the transaction is commited this
* transactions event information. When the transaction is committed this
* information is registered (with the transaction manager). If this
* transaction is rolled back then none of the transaction event information
* registers including the information you put in via this method.
@@ -1383,10 +1431,18 @@ public final class Ebean {
}
/**
* Create a JsonContext that will use the default configuration options.
* Return the JsonContext for reading/writing JSON.
*/
public static JsonContext json() {
return serverMgr.getPrimaryServer().json();
}
/**
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public static JsonContext createJsonContext() {
return serverMgr.getPrimaryServer().createJsonContext();
return json();
}
}
+78 -40
View File
@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Set;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -48,18 +49,18 @@ import com.avaje.ebean.text.json.JsonContext;
* Example: Get a EbeanServer
* </p>
*
* <pre class="code">
* <pre>{@code
* // Get access to the Human Resources EbeanServer/Database
* EbeanServer hrServer = Ebean.getServer(&quot;HR&quot;);
* EbeanServer hrServer = Ebean.getServer("HR");
*
*
* // fetch contact 3 from the HR database Contact contact =
* hrServer.find(Contact.class, new Integer(3));
*
* contact.setStatus(&quot;INACTIVE&quot;); ...
* contact.setStatus("INACTIVE"); ...
*
* // save the contact back to the HR database hrServer.save(contact);
* </pre>
* }</pre>
*
* <p>
* <b>EbeanServer has more API than Ebean</b><br/>
@@ -127,7 +128,6 @@ public interface EbeanServer {
*/
public MetaInfoManager getMetaInfoManager();
/**
* Return the BeanState for a given entity bean.
* <p>
@@ -191,15 +191,15 @@ public interface EbeanServer {
* moved to {@link #createNamedQuery(Class, String)}.
* </p>
*
* <pre class="code">
* <pre>{@code
* EbeanServer ebeanServer = ... ;
* String q = "find order fetch details where status = :st";
*
* List&lt;Order&gt; newOrders
* List<Order> newOrders
* = ebeanServer.createQuery(Order.class, q)
* .setParameter("st", Order.Status.NEW)
* .findList();
* </pre>
* }</pre>
*
* @param query
* the object query
@@ -207,16 +207,14 @@ public interface EbeanServer {
public <T> Query<T> createQuery(Class<T> beanType, String query);
/**
* Create a query for an entity bean (refer {@link Ebean#createQuery(Class)}
* ).
* Create a query for an entity bean (refer {@link Ebean#createQuery(Class)}).
*
* @see Ebean#createQuery(Class)
*/
public <T> Query<T> createQuery(Class<T> beanType);
/**
* Create a query for a type of entity bean (the same as
* {@link EbeanServer#createQuery(Class)}).
* Create a query for a type of entity bean (the same as {@link EbeanServer#createQuery(Class)}).
*/
public <T> Query<T> find(Class<T> beanType);
@@ -272,8 +270,7 @@ public interface EbeanServer {
public SqlQuery createSqlQuery(String sql);
/**
* Create a named sql query (refer {@link Ebean#createNamedSqlQuery(String)}
* ).
* Create a named sql query (refer {@link Ebean#createNamedSqlQuery(String)}).
* <p>
* The query statement will be defined in a deployment orm xml file.
* </p>
@@ -283,8 +280,7 @@ public interface EbeanServer {
public SqlQuery createNamedSqlQuery(String namedQuery);
/**
* Create a sql update for executing native dml statements (refer
* {@link Ebean#createSqlUpdate(String)}).
* Create a sql update for executing native dml statements (refer {@link Ebean#createSqlUpdate(String)}).
*
* @see Ebean#createSqlUpdate(String)
*/
@@ -296,8 +292,7 @@ public interface EbeanServer {
public CallableSql createCallableSql(String callableSql);
/**
* Create a named sql update (refer {@link Ebean#createNamedSqlUpdate(String)}
* ).
* Create a named sql update (refer {@link Ebean#createNamedSqlUpdate(String)}).
* <p>
* The statement (an Insert Update or Delete statement) will be defined in a
* deployment orm xml file.
@@ -307,6 +302,17 @@ public interface EbeanServer {
*/
public SqlUpdate createNamedSqlUpdate(String namedQuery);
/**
* Register a TransactionCallback on the currently active transaction.
* <p/>
* If there is no currently active transaction then a PersistenceException is thrown.
*
* @param transactionCallback The transaction callback to be registered with the current transaction.
*
* @throws PersistenceException If there is no currently active transaction
*/
public void register(TransactionCallback transactionCallback) throws PersistenceException;
/**
* Create a new transaction that is not held in TransactionThreadLocal.
* <p>
@@ -338,8 +344,7 @@ public interface EbeanServer {
public Transaction beginTransaction(TxIsolation isolation);
/**
* Returns the current transaction or null if there is no current transaction
* in scope.
* Returns the current transaction or null if there is no current transaction in scope.
*/
public Transaction currentTransaction();
@@ -367,15 +372,19 @@ public interface EbeanServer {
* <p>
* Code example:
*
* <pre class="code">
* Ebean.startTransaction(); try { // do some fetching
* and or persisting
* <pre>{@code
* ebeanServer.startTransaction();
* try {
* // do some fetching and or persisting ...
*
* // commit at the end Ebean.commitTransaction();
* // commit at the end
* ebeanServer.commitTransaction();
*
* } finally { // if commit didn't occur then rollback the transaction
* Ebean.endTransaction(); }
* </pre>
* } finally {
* // if commit didn't occur then rollback the transaction
* ebeanServer.endTransaction();
* }
* }</pre>
*
* </p>
*
@@ -417,7 +426,7 @@ public interface EbeanServer {
* <p>
* This will not perform a query against the database.
* </p>
* <pre class="code">
* <pre>{@code
* Product product = Ebean.getReference(Product.class, 1);
*
* // You can get the id without causing a fetch/lazy load
@@ -426,14 +435,14 @@ public interface EbeanServer {
* // If you try to get any other property a fetch/lazy loading will occur
* // This will cause a query to execute...
* String name = product.getName();
* </pre>
* }</pre>
*
* @param beanType
* the type of entity bean
* @param id
* the id value
*/
public <T> T getReference(Class<T> beanType, Object uid);
public <T> T getReference(Class<T> beanType, Object id);
/**
* Return the number of 'top level' or 'root' entities this query should
@@ -457,6 +466,30 @@ public interface EbeanServer {
* Execute the query visiting the results. This is similar to findIterate in
* that not all the result beans need to be held in memory at the same time
* and as such is go for processing large queries.
*
* @see Query#findEach(QueryEachConsumer)
*/
public <T> void findEach(Query<T> query, QueryEachConsumer<T> consumer, Transaction transaction);
/**
* Execute the query visiting the results. This is similar to findIterate in
* that not all the result beans need to be held in memory at the same time
* and as such is go for processing large queries.
*
* @see Query#findEachWhile(QueryEachWhileConsumer)
*/
public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction transaction);
/**
* Deprecated in favor of #findEachWhile which is functionally exactly the same
* but has a much better name.
* <p>
* Execute the query visiting the results. This is similar to findIterate in
* that not all the result beans need to be held in memory at the same time
* and as such is go for processing large queries.
* </p>
*
* @deprecated
*/
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction transaction);
@@ -489,7 +522,7 @@ public interface EbeanServer {
*
* @param query
* the query to execute the row count on
* @param t
* @param transaction
* the transaction (can be null).
* @return a Future object for the row count query
*/
@@ -505,7 +538,7 @@ public interface EbeanServer {
*
* @param query
* the query to execute the fetch Id's on
* @param t
* @param transaction
* the transaction (can be null).
* @return a Future object for the list of Id's
*/
@@ -521,7 +554,7 @@ public interface EbeanServer {
*
* @param query
* the query to execute in the background
* @param t
* @param transaction
* the transaction (can be null).
* @return a Future object for the list result of the query
* @deprecated
@@ -538,7 +571,7 @@ public interface EbeanServer {
*
* @param query
* the query to execute in the background
* @param t
* @param transaction
* the transaction (can be null).
* @return a Future object for the list result of the query
* @deprecated
@@ -846,7 +879,7 @@ public interface EbeanServer {
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
* <pre>{@code
*
* Customer customer = ebeanServer.find(Customer, id);
*
@@ -855,7 +888,7 @@ public interface EbeanServer {
* ebeanServer.markAsDirty(customer);
* ebeanServer.save(customer);
*
* </pre>
* }</pre>
*/
public void markAsDirty(Object bean);
@@ -882,15 +915,15 @@ public interface EbeanServer {
* properties are included instead.
* </p>
*
* <pre class="code">
* <pre>{@code
*
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName(&quot;ModifiedNameNoOCC&quot;);
* customer.setName("ModifiedNameNoOCC");
* ebeanServer.update(customer);
*
* </pre>
* }</pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
@@ -1119,9 +1152,14 @@ public interface EbeanServer {
public void runCacheWarming(Class<?> beanType);
/**
* Create a JsonContext that will use the default configuration options.
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public JsonContext createJsonContext();
/**
* Return the JsonContext for reading/writing JSON.
*/
public JsonContext json();
}
@@ -6,8 +6,6 @@ import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates EbeanServer instances.
@@ -26,14 +24,9 @@ import org.slf4j.LoggerFactory;
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*
* @author Rob Bygrave
*
*/
public class EbeanServerFactory {
private static final Logger logger = LoggerFactory.getLogger(EbeanServerFactory.class);
private static BootupEbeanManager serverFactory = createServerFactory();
/**
@@ -69,27 +62,10 @@ public class EbeanServerFactory {
private static BootupEbeanManager createServerFactory() {
// String d___ =
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
String implClassName = System.getProperty("ebean.serverfactory", dflt);
int delaySecs = GlobalProperties.getInt("ebean.start.delay", 0);
if (delaySecs > 0) {
try {
// perhaps useful to delay the startup to give time to
// attach a debugger when running in a server like tomcat.
String m = "Ebean sleeping " + delaySecs + " seconds due to ebean.start.delay";
logger.info(m);
Thread.sleep(delaySecs * 1000);
} catch (InterruptedException e) {
String m = "Interrupting debug.start.delay of " + delaySecs;
logger.error(m, e);
}
}
try {
// use a client side implementation?
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
} catch (Exception ex) {
throw new RuntimeException(ex);
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
@@ -87,6 +89,11 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setOrderBy(String orderBy);
/**
* Apply the path properties to the query replacing the select and fetch clauses.
*/
public Query<T> apply(PathProperties pathProperties);
/**
* Execute the query iterating over the results.
*
@@ -95,9 +102,25 @@ public interface ExpressionList<T> extends Serializable {
public QueryIterator<T> findIterate();
/**
* Execute the query visiting the results.
*
* @see Query#findVisit(QueryResultVisitor)
* Execute the query process the beans one at a time.
*
* @see Query#findEach(QueryEachConsumer)
*/
public void findEach(QueryEachConsumer<T> consumer);
/**
* Execute the query processing the beans one at a time with the ability to
* stop processing before reading all the beans.
*
* @see Query#findEachWhile(QueryEachWhileConsumer)
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Deprecated in favor of #findEachWhile which is functionally exactly the same
* but has a much better name.
*
* @deprecated
*/
public void findVisit(QueryResultVisitor<T> visitor);
@@ -238,22 +261,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> select(String properties);
/**
* Specify a property (associated bean) to join and <em>fetch</em> including
* all its properties.
*
* @see Query#join(String)
*/
public Query<T> join(String assocProperties);
/**
* Specify a property (associated bean) to join and <em>fetch</em> with its
* specific properties to include (aka partial object).
*
* @see Query#join(String,String)
*/
public Query<T> join(String assocProperty, String assocProperties);
/**
* Set the first row to fetch.
*
+68 -28
View File
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -176,11 +178,11 @@ public abstract class Model {
* Typically a Finder is defined as a public static field on an entity bean class to provide a
* nice way to write queries.
*
* @param I
* @param <I>
* The Id type. This is most often a {@link Long} but is also often a {@link UUID} or
* {@link String}.
*
* @param T
*
* @param <T>
* The bean type
*/
public static class Finder<I, T> {
@@ -265,7 +267,7 @@ public abstract class Model {
* Retrieves an entity by ID.
*
* <p>
* Equivilent to {@link EbeanServer#find(Class, Object)}
* Equivalent to {@link EbeanServer#find(Class, Object)}
*/
public T byId(I id) {
return db().find(type, id);
@@ -275,7 +277,7 @@ public abstract class Model {
* Creates an entity reference for this ID.
*
* <p>
* Equivilent to {@link EbeanServer#getReference(Class, Object)}
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
*/
public T ref(I id) {
return db().getReference(type, id);
@@ -285,7 +287,7 @@ public abstract class Model {
* Creates a filter for sorting and filtering lists of entities locally without going back to
* the database.
* <p>
* Equivilent to {@link EbeanServer#filter(Class)}
* Equivalent to {@link EbeanServer#filter(Class)}
*/
public Filter<T> filter() {
return db().filter(type);
@@ -294,12 +296,19 @@ public abstract class Model {
/**
* Creates a query.
* <p>
* Equivilent to {@link EbeanServer#find(Class)}
* Equivalent to {@link EbeanServer#find(Class)}
*/
public Query<T> query() {
return db().find(type);
}
/**
* Creates a query applying the path properties to set the select and fetch clauses.
*/
public Query<T> apply(PathProperties pathProperties) {
return db().find(type).apply(pathProperties);
}
/**
* Returns the next identity value.
*
@@ -313,18 +322,49 @@ public abstract class Model {
/**
* Executes a query and returns the results as a list of IDs.
* <p>
* Equivilent to {@link Query#findIds()}
* Equivalent to {@link Query#findIds()}
*/
public List<Object> findIds() {
return query().findIds();
}
/**
* Execute the query consuming each bean one at a time.
* <p>
* This is generally used to process large queries where unlike findList
* you do not want to hold all the results in memory at once but instead
* process them one at a time (requiring far less memory).
* </p>
* Equivalent to {@link Query#findEach(QueryEachConsumer)}
*/
public void findEach(QueryEachConsumer<T> consumer) {
query().findEach(consumer);
}
/**
* Execute the query consuming each bean one at a time.
* <p>
* This is similar to #findEach except that you return boolean
* true to continue processing beans and return false to stop
* processing early.
* </p>
* <p>
* This is generally used to process large queries where unlike findList
* you do not want to hold all the results in memory at once but instead
* process them one at a time (requiring far less memory).
* </p>
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer) {
query().findEachWhile(consumer);
}
/**
* Retrieves all entities of the given type.
* <p>
* The same as {@link #all()}
* <p>
* Equivilent to {@link Query#findList()}
* Equivalent to {@link Query#findList()}
*/
public List<T> findList() {
return query().findList();
@@ -333,7 +373,7 @@ public abstract class Model {
/**
* Returns all the entities of the given type as a set.
* <p>
* Equivilent to {@link Query#findSet()}
* Equivalent to {@link Query#findSet()}
*/
public Set<T> findSet() {
return query().findSet();
@@ -342,7 +382,7 @@ public abstract class Model {
/**
* Retrieves all entities of the given type as a map of objects.
* <p>
* Equivilent to {@link Query#findMap()}
* Equivalent to {@link Query#findMap()}
*/
public Map<?, T> findMap() {
return query().findMap();
@@ -352,7 +392,7 @@ public abstract class Model {
* Executes the query and returns the results as a map of the objects specifying the map key
* property.
* <p>
* Equivilent to {@link Query#findMap(String, Class)}
* Equivalent to {@link Query#findMap(String, Class)}
*/
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
return query().findMap(keyProperty, keyType);
@@ -362,7 +402,7 @@ public abstract class Model {
* Return a PagedList of all entities of the given type (use where() to specify predicates as
* needed).
* <p>
* Equivilent to {@link Query#findPagedList(int, int)}
* Equivalent to {@link Query#findPagedList(int, int)}
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return query().findPagedList(pageIndex, pageSize);
@@ -371,7 +411,7 @@ public abstract class Model {
/**
* Executes a find row count query in a background thread.
* <p>
* Equivilent to {@link Query#findFutureRowCount()}
* Equivalent to {@link Query#findFutureRowCount()}
*/
public FutureRowCount<T> findFutureRowCount() {
return query().findFutureRowCount();
@@ -380,7 +420,7 @@ public abstract class Model {
/**
* Returns the total number of entities for this type. *
* <p>
* Equivilent to {@link Query#findRowCount()}
* Equivalent to {@link Query#findRowCount()}
*/
public int findRowCount() {
return query().findRowCount();
@@ -397,7 +437,7 @@ public abstract class Model {
* Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean,
* to load a partial object.
* <p>
* Equivilent to {@link Query#select(String)}
* Equivalent to {@link Query#select(String)}
*/
public Query<T> select(String fetchProperties) {
return query().select(fetchProperties);
@@ -406,7 +446,7 @@ public abstract class Model {
/**
* Specifies a path to load including all its properties.
* <p>
* Equivilent to {@link Query#fetch(String)}
* Equivalent to {@link Query#fetch(String)}
*/
public Query<T> fetch(String path) {
return query().fetch(path);
@@ -416,7 +456,7 @@ public abstract class Model {
* Additionally specifies a <code>FetchConfig</code> to specify a 'query join' and/or define the
* lazy loading query.
* <p>
* Equivilent to {@link Query#fetch(String, FetchConfig)}
* Equivalent to {@link Query#fetch(String, FetchConfig)}
*/
public Query<T> fetch(String path, FetchConfig joinConfig) {
return query().fetch(path, joinConfig);
@@ -426,7 +466,7 @@ public abstract class Model {
* Specifies a path to fetch with a specific list properties to include, to load a partial
* object.
* <p>
* Equivilent to {@link Query#fetch(String, String)}
* Equivalent to {@link Query#fetch(String, String)}
*/
public Query<T> fetch(String path, String fetchProperties) {
return query().fetch(path, fetchProperties);
@@ -436,7 +476,7 @@ public abstract class Model {
* Additionally specifies a <code>FetchConfig</code> to use a separate query or lazy loading to
* load this path.
* <p>
* Equivilent to {@link Query#fetch(String, String, FetchConfig)}
* Equivalent to {@link Query#fetch(String, String, FetchConfig)}
*/
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) {
return query().fetch(assocProperty, fetchProperties, fetchConfig);
@@ -446,7 +486,7 @@ public abstract class Model {
* Adds expressions to the <code>where</code> clause with the ability to chain on the
* <code>ExpressionList</code>.
* <p>
* Equivilent to {@link Query#where()}
* Equivalent to {@link Query#where()}
*/
public ExpressionList<T> where() {
return query().where();
@@ -458,7 +498,7 @@ public abstract class Model {
* <p>
* This is exactly the same as {@link #orderBy}.
* <p>
* Equivilent to {@link Query#order()}
* Equivalent to {@link Query#order()}
*/
public OrderBy<T> order() {
return query().order();
@@ -480,7 +520,7 @@ public abstract class Model {
* <p>
* This is exactly the same as {@link #order}.
* <p>
* Equivilent to {@link Query#orderBy()}
* Equivalent to {@link Query#orderBy()}
*/
public OrderBy<T> orderBy() {
return query().orderBy();
@@ -499,7 +539,7 @@ public abstract class Model {
/**
* Sets the first row to return for this query.
* <p>
* Equivilent to {@link Query#setFirstRow(int)}
* Equivalent to {@link Query#setFirstRow(int)}
*/
public Query<T> setFirstRow(int firstRow) {
return query().setFirstRow(firstRow);
@@ -508,7 +548,7 @@ public abstract class Model {
/**
* Sets the maximum number of rows to return in the query.
* <p>
* Equivilent to {@link Query#setMaxRows(int)}
* Equivalent to {@link Query#setMaxRows(int)}
*/
public Query<T> setMaxRows(int maxRows) {
return query().setMaxRows(maxRows);
@@ -521,7 +561,7 @@ public abstract class Model {
* Use this to perform a find byId query but with additional control over the query such as
* using select and fetch to control what parts of the object graph are returned.
* <p>
* Equivilent to {@link Query#setId(Object)}
* Equivalent to {@link Query#setId(Object)}
*/
public Query<T> setId(Object id) {
return query().setId(id);
@@ -530,7 +570,7 @@ public abstract class Model {
/**
* Create and return a new query using the OQL.
* <p>
* Equivilent to {@link EbeanServer#createQuery(Class, String)}
* Equivalent to {@link EbeanServer#createQuery(Class, String)}
*/
public Query<T> setQuery(String oql) {
return db().createQuery(type, oql);
@@ -539,7 +579,7 @@ public abstract class Model {
/**
* Create and return a new query based on the <code>RawSql</code>.
* <p>
* Equivilent to {@link Query#setRawSql(RawSql)}
* Equivalent to {@link Query#setRawSql(RawSql)}
*/
public Query<T> setRawSql(RawSql rawSql) {
return query().setRawSql(rawSql);
@@ -0,0 +1,35 @@
package com.avaje.ebean;
/**
* Defines the scope for PersistenceContext.
* <p/>
* Ebean has traditionally used Transaction scope for the PersistenceContext. This is used to change the scope to
* use (by default) and explicitly set the scope to use for an individual query.
*
* @see com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)
* @see com.avaje.ebean.Query#setPersistenceContextScope(PersistenceContextScope)
*/
public enum PersistenceContextScope {
/**
* PersistenceContext is scoped to the transaction.
* <p/>
* If a transaction spans 2 or more queries that fetch the same bean in terms of same type
* and same Id value then they share the same bean instance.
* <p/>
* You may want to change to use QUERY scope when you want a query executing in a transaction to effectively
* ignore beans that have already been loaded (by other queries in the same transaction) and instead get a
* 'fresh copy' of the bean.
*/
TRANSACTION,
/**
* PersistenceContext is scoped to the query.
* <p/>
* This means that for this query running in an existing transaction then it will effectively ignore any beans
* that have already been queried/loaded by prior queries in the same transaction.
* <p/>
* You may use QUERY scope on a query that is executed in a transaction and you want to get a 'fresh copy' of the bean.
*/
QUERY
}
+106 -19
View File
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@@ -299,6 +301,21 @@ public interface Query<T> extends Serializable {
*/
public Query<T> copy();
/**
* Specify the PersistenceContextScope to use for this query.
* <p/>
* When this is not set the 'default' configured on {@link com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)}
* is used - this value defaults to {@link com.avaje.ebean.PersistenceContextScope#TRANSACTION}.
* <p/>
* Note that the same persistence Context is used for subsequent lazy loading and query join queries.
* <p/>
* Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for
* queries executed as #findIterate, #findEach, #findEachWhile.
*
* @param scope The scope to use for this query and subsequent lazy loading.
*/
public Query<T> setPersistenceContextScope(PersistenceContextScope scope);
/**
* Return the ExpressionFactory used by this query.
*/
@@ -323,22 +340,32 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setAutofetch(boolean autofetch);
/**
* Set the default lazy loading batch size to use.
* <p>
* When lazy loading is invoked on beans loaded by this query then this sets the
* batch size used to load those beans.
*
* @param lazyLoadBatchSize the number of beans to lazy load in a single batch
*/
public Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);
/**
* Explicitly set a comma delimited list of the properties to fetch on the
* 'main' entity bean (aka partial object). Note that '*' means all
* properties.
*
*
* <pre class="code">
* Query&lt;Customer&gt; query = Ebean.createQuery(Customer.class);
*
*
* // Only fetch the customer id, name and status.
* // This is described as a &quot;Partial Object&quot;
* query.select(&quot;name, status&quot;);
* query.where(&quot;lower(name) like :custname&quot;).setParameter(&quot;custname&quot;, &quot;rob%&quot;);
*
*
* List&lt;Customer&gt; customerList = query.findList();
* </pre>
*
*
* @param fetchProperties
* the properties to fetch for this bean (* = all properties).
*/
@@ -413,6 +440,14 @@ public interface Query<T> extends Serializable {
*/
public Query<T> fetch(String path, FetchConfig joinConfig);
/**
* Apply the path properties replacing the select and fetch clauses.
* <p>
* This is typically used when the PathProperties is applied to both the query and the JSON output.
* </p>
*/
public Query<T> apply(PathProperties pathProperties);
/**
* Execute the query returning the list of Id's.
* <p>
@@ -436,37 +471,89 @@ public interface Query<T> extends Serializable {
*/
public QueryIterator<T> findIterate();
/**
* This is deprecated in favor of #findEachWhile.
* <p>
* This is functionally exactly the same as #findEachWhile. It is
* replaced by findEachWhile because the method name is much better.
* </p>
*
* @param visitor
* the visitor used to process the queried beans.
*
* @deprecated
*/
public void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the query processing the beans one at a time.
* <p>
* This method is appropriate to process very large query results as the
* beans are consumed one at a time and do not need to be held in memory
* (unlike #findList #findSet etc)
* </p>
* <p>
* Compared with #findEachWhile this will always process all the beans where as
* #findEachWhile provides a way to stop processing the query result early before
* all the beans have been read.
* </p>
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the QueryEachConsumer (SAM) interface which is better suited to use
* with Java8 closures.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* });
* </pre>
*
* @param consumer
* the consumer used to process the queried beans.
*/
public void findEach(QueryEachConsumer<T> consumer);
/**
* Execute the query using callbacks to a visitor to process the resulting
* beans one at a time.
* <p>
* Similar to findIterate() this query method does not require all the result
* beans to be all held in memory at once and as such is useful for processing
* large queries.
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the QueryEachWhileConsumer (SAM) interface which is better suited to use
* with Java8 closures.
* </p>
*
*
* <pre class="code">
*
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit(new QueryResultVisitor&lt;Customer&gt;() {
*
* public boolean accept(Customer customer) {
*
* query.findEachWhile((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* return true;
* }
*
* // return true to continue processing or false to stop
* return (customer.getId() < 40);
* });
* </pre>
*
* @param visitor
* the visitor used to process the queried beans.
*
* @param consumer
* the consumer used to process the queried beans.
*/
public void findVisit(QueryResultVisitor<T> visitor);
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Execute the query returning the list of objects.
@@ -0,0 +1,41 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* });
* </pre>
*
* @param <T>
* the type of entity bean being queried.
*/
public interface QueryEachConsumer<T> {
/**
* Process the bean.
*
* @param bean
* the entity bean to process
*/
public void accept(T bean);
}
@@ -0,0 +1,45 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
* <p/>
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findEachWhile((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
*
* // return true to continue processing or false to stop
* return (customer.getId() < 40);
* });
* </pre>
*
* @param <T> the type of entity bean being queried.
*/
public interface QueryEachWhileConsumer<T> {
/**
* Process the bean and return true if you want to continue processing more
* beans. Return false if you want to stop processing further.
*
* @param bean the entity bean to process
* @return true to continue processing more beans or false to stop.
*/
public boolean accept(T bean);
}
@@ -1,6 +1,6 @@
package com.avaje.ebean;
public class SimpleTextParser {
class SimpleTextParser {
private final String oql;
private final char[] chars;
@@ -10,9 +10,7 @@ public class SimpleTextParser {
private String word;
private String lowerWord;
private int openParenthesisCount;
public SimpleTextParser(String oql) {
SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
@@ -22,10 +20,6 @@ public class SimpleTextParser {
return pos;
}
public String getOql() {
return oql;
}
public String getWord() {
return word;
}
@@ -104,7 +98,7 @@ public class SimpleTextParser {
private void moveToClose() {
pos++;
openParenthesisCount = 0;
int openParenthesisCount = 0;
for (; pos < eof; pos++) {
char c = chars[pos];
@@ -143,11 +137,7 @@ public class SimpleTextParser {
if (isOperator(c)) {
return !isOperator;
}
if (c == '(') {
return true;
}
return isOperator;
return c == '(' || isOperator;
}
private boolean isOperator(char c) {
@@ -91,6 +91,11 @@ public interface SqlUpdate {
*/
public String getSql();
/**
* Return the generated sql that has named parameters converted to positioned parameters.
*/
public String getGeneratedSql();
/**
* Return the timeout used to execute this statement.
*/
@@ -35,6 +35,11 @@ public interface Transaction extends Closeable {
*/
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
/**
* Register a TransactionCallback with this transaction.
*/
public void register(TransactionCallback callback);
/**
* Return true if this transaction is read only.
*/
@@ -0,0 +1,34 @@
package com.avaje.ebean;
/**
* Provides a callback that can be registered with a Transaction.
* <p/>
* The callback methods are called just prior to and after the transaction performs a commit or rollback.
* <p/>
* A typical use of TransactionCallback would be to clean up non-transactional resources like files. For example,
* when processing files on postCommit/postRollback clean up the associated files. As another example when
* on postCommit of a delete remove associated resources from the file system or remote service.
*/
public interface TransactionCallback {
/**
* Perform processing just prior to the transaction commit.
*/
void preCommit();
/**
* Perform processing just after the transaction commit.
*/
void postCommit();
/**
* Perform processing just prior to the transaction rollback.
*/
void preRollback();
/**
* Perform processing just after the transaction rollback.
*/
void postRollback();
}
@@ -0,0 +1,42 @@
package com.avaje.ebean;
/**
* Adapter that can be extended for easier implementation of TransactionCallback.
* <p/>
* Provides 'no operation' implementation for each of the TransactionCallback methods. It is expected that this
* class is extended and override the methods you need to.
*/
public abstract class TransactionCallbackAdapter implements TransactionCallback {
/**
* Perform processing just prior to the transaction commit.
*/
@Override
public void preCommit() {
// do nothing - override as necessary
}
/**
* Perform processing just after the transaction commit.
*/
@Override
public void postCommit() {
// do nothing - override as necessary
}
/**
* Perform processing just prior to the transaction rollback.
*/
@Override
public void preRollback() {
// do nothing - override as necessary
}
/**
* Perform processing just after the transaction rollback.
*/
@Override
public void postRollback() {
// do nothing - override as necessary
}
}
@@ -0,0 +1,24 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation for declaring an index on a single column.
*
* @author rvbiljouw
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Index {
/**
* Name of the index
*
* @return index name
*/
String value() default "";
}
@@ -55,21 +55,6 @@ import com.avaje.ebean.TxType;
* Ebean.save(order);
* }
* </pre>
*
* <p>
* During development and testing you can set a debug level which will log the
* transaction begin, commit and rollback events so that you can easily confirm
* it is behaving as you would expect.
* </p>
*
* <pre class="code">
* ## in ebean.properties file
*
* ## Log transaction begins and ends etc
* ## (0=NoLogging 1=minimal ... 9=logAll)
* ebean.debug.transaction=3
*
* </pre>
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@@ -31,6 +31,13 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Reset the collection back to an empty state ready for reloading.
* <p>
* This is done as part of bean refresh.
*/
public void reset(EntityBean ownerBean, String propertyName);
/**
* Return true if the collection is empty and untouched. Used to detect if a
* collection was 'cleared' deliberately or just un-initialised.
@@ -47,14 +54,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public String getPropertyName();
/**
* Return the index position of this collection in the lazy/query loader.
* <p>
* Used for batch loading of collections.
* </p>
*/
public int getLoaderIndex();
/**
* Check after the lazy load that the underlying collection is not null
* (handle case where join to many not outer).
@@ -108,11 +107,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public void internalAdd(Object bean);
/**
* Returns the underlying List Set or Map object.
*/
public Object getActualCollection();
/**
* Return the number of elements in the List Set or Map.
*/
@@ -39,8 +39,6 @@ public final class EntityBeanIntercept implements Serializable {
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
private int beanLoaderIndex;
private String ebeanServerName;
@@ -200,34 +198,22 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Return the index position for batch loading via BeanLoader.
* Set the BeanLoader with PersistenceContext.
*/
public int getBeanLoaderIndex() {
return beanLoaderIndex;
}
/**
* Set Lazy Loading by ebeanServerName.
* <p>
* This is for reference beans created by themselves.
* </p>
*/
public void setBeanLoaderByServerName(String ebeanServerName) {
this.beanLoaderIndex = 0;
this.beanLoader = null;
this.ebeanServerName = ebeanServerName;
}
/**
* Set the BeanLoader for general lazy loading.
*/
public void setBeanLoader(int index, BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoaderIndex = index;
public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoader = beanLoader;
this.persistenceContext = ctx;
this.ebeanServerName = beanLoader.getName();
}
/**
* Set the BeanLoader.
*/
public void setBeanLoader(BeanLoader beanLoader) {
this.beanLoader = beanLoader;
this.ebeanServerName = beanLoader.getName();
}
public boolean isFullyLoadedBean() {
return fullyLoadedBean;
}
@@ -38,12 +38,12 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
/**
* The owning bean (used for lazy fetch).
*/
protected final EntityBean ownerBean;
protected EntityBean ownerBean;
/**
* The name of this property in the owning bean (used for lazy fetch).
*/
protected final String propertyName;
protected String propertyName;
protected ModifyHolder<E> modifyHolder;
@@ -62,8 +62,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
* Constructor not non-lazy loading collection.
*/
public AbstractBeanCollection() {
this.ownerBean = null;
this.propertyName = null;
}
/**
@@ -85,10 +83,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
return propertyName;
}
public int getLoaderIndex() {
return loaderIndex;
}
public ExpressionList<?> getFilterMany() {
return filterMany;
}
@@ -46,6 +46,14 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
super(loader, ownerBean, propertyName);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.list = null;
this.touched = false;
}
@Override
public boolean isEmptyAndUntouched() {
return !touched && (list == null || list.isEmpty());
@@ -130,13 +138,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
return list;
}
/**
* Returns the underlying list.
*/
public Object getActualCollection() {
return list;
}
/**
* Return true if the underlying list is populated.
*/
@@ -39,7 +39,15 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) {
super(ebeanServer, ownerBean, propertyName);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.map = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (map == null || map.isEmpty());
}
@@ -146,15 +154,8 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
/**
* Returns the underlying map.
*/
public Object getActualCollection() {
return map;
}
public String toString() {
StringBuffer sb = new StringBuffer(50);
StringBuilder sb = new StringBuilder(50);
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -40,6 +40,14 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
super(loader, ownerBean, propertyName);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.set = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (set == null || set.isEmpty());
}
@@ -32,6 +32,8 @@ public class DataSourceConfig {
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
@@ -131,6 +133,20 @@ public class DataSourceConfig {
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
/**
* Return autoCommit setting.
*/
public boolean isAutoCommit() {
return autoCommit;
}
/**
* Set to true to turn on autoCommit.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return the minimum number of connections the pool should maintain.
@@ -450,6 +466,7 @@ public class DataSourceConfig {
String dbUrl = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", dbUrl);
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
@@ -0,0 +1,29 @@
package com.avaje.ebean.config;
/**
* Configuration for JSON features.
*/
public abstract class JsonConfig {
/**
* Defined the format used for DateTime types.
*/
public enum DateTime {
/**
* Format as epoch millis.
*/
MILLIS,
/**
* Format as epoch with nanos.
*/
NANOS,
/**
* Format as ISO-8601 date format.
*/
ISO8601
}
}
@@ -1,11 +1,10 @@
package com.avaje.ebean.config;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
/**
* A map like structure of properties.
@@ -68,9 +67,8 @@ final class PropertyMap implements Serializable {
}
synchronized void putAll(Map<String, String> keyValueMap) {
Iterator<Entry<String, String>> it = keyValueMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@@ -1,20 +1,18 @@
package com.avaje.ebean.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper used to load the PropertyMap.
*/
@@ -81,9 +79,7 @@ final class PropertyMapLoader {
}
// put values in initially without any evaluation
Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object> entry = it.next();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = ((String) entry.getKey()).toLowerCase();
String val = ((String) entry.getValue());
if (val != null) {
@@ -1,25 +1,21 @@
package com.avaje.ebean.config;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.annotation.Encrypted;
import com.avaje.ebean.cache.ServerCacheFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.GlobalProperties.PropertySource;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
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.event.*;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.util.ClassUtil;
import com.fasterxml.jackson.core.JsonFactory;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
/**
* The configuration used for creating a EbeanServer.
@@ -120,6 +116,11 @@ public class ServerConfig {
*/
private AutofetchConfig autofetchConfig = new AutofetchConfig();
/**
* The JSON format used for DateTime types. Default to millis.
*/
private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.MILLIS;
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
@@ -175,6 +176,13 @@ public class ServerConfig {
*/
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
/**
* Set to true if the DataSource uses autoCommit.
* <p>
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
private boolean autoCommitMode;
/**
* The data source JNDI name if using a JNDI DataSource.
*/
@@ -209,10 +217,10 @@ public class ServerConfig {
* Setting to indicate if UUID should be stored as binary(16) or varchar(40).
*/
private boolean uuidStoreAsBinary;
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<?>> persistListeners = new ArrayList<BeanPersistListener<?>>();
private List<BeanPersistListener> persistListeners = new ArrayList<BeanPersistListener>();
private List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
private List<BulkTableEventListener> bulkTableEventListeners = new ArrayList<BulkTableEventListener>();
private List<ServerConfigStartup> configStartupListeners = new ArrayList<ServerConfigStartup>();
@@ -233,14 +241,56 @@ public class ServerConfig {
private boolean collectQueryStatsByNode;
private boolean collectQueryOrigins;
/**
* Construct a Server Configuration for programmatically creating an
* EbeanServer.
* The default PersistenceContextScope used if one is not explicitly set on a query.
*/
private PersistenceContextScope persistenceContextScope = PersistenceContextScope.TRANSACTION;
private JsonFactory jsonFactory;
private boolean localTimeWithNanos;
private boolean durationWithNanos;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
public ServerConfig() {
}
/**
* Return the Jackson JsonFactory to use.
* <p>
* If not set a default implmentation will be used.
*/
public JsonFactory getJsonFactory() {
return jsonFactory;
}
/**
* Set the Jackson JsonFactory to use.
* <p>
* If not set a default implmentation will be used.
*/
public void setJsonFactory(JsonFactory jsonFactory) {
this.jsonFactory = jsonFactory;
}
/**
* Return the JSON format used for DateTime types.
*/
public JsonConfig.DateTime getJsonDateTime() {
return jsonDateTime;
}
/**
* Set the JSON format to use for DateTime types.
*/
public void setJsonDateTime(JsonConfig.DateTime jsonDateTime) {
this.jsonDateTime = jsonDateTime;
}
/**
* Return the name of the EbeanServer.
@@ -582,6 +632,20 @@ public class ServerConfig {
public void setDataSourceJndiName(String dataSourceJndiName) {
this.dataSourceJndiName = dataSourceJndiName;
}
/**
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
*/
public boolean isAutoCommitMode() {
return autoCommitMode;
}
/**
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
public void setAutoCommitMode(boolean autoCommitMode) {
this.autoCommitMode = autoCommitMode;
}
/**
* Return a value used to represent TRUE in the database.
@@ -817,6 +881,43 @@ public class ServerConfig {
this.uuidStoreAsBinary = uuidStoreAsBinary;
}
/**
* Return true if LocalTime should be persisted with nanos precision.
*/
public boolean isLocalTimeWithNanos() {
return localTimeWithNanos;
}
/**
* Set to true if LocalTime should be persisted with nanos precision.
* <p>
* Otherwise it is persisted using java.sql.Time which is seconds precision.
* </p>
*/
public void setLocalTimeWithNanos(boolean localTimeWithNanos) {
this.localTimeWithNanos = localTimeWithNanos;
}
/**
* Return true if Duration should be persisted with nanos precision (SQL DECIMAL).
* <p>
* Otherwise it is persisted with second precision (SQL INTEGER).
* </p>
*/
public boolean isDurationWithNanos() {
return durationWithNanos;
}
/**
* Set to true if Duration should be persisted with nanos precision (SQL DECIMAL).
* <p>
* Otherwise it is persisted with second precision (SQL INTEGER).
* </p>
*/
public void setDurationWithNanos(boolean durationWithNanos) {
this.durationWithNanos = durationWithNanos;
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -1153,14 +1254,14 @@ public class ServerConfig {
* all the BeanPersistListener instances.
* </p>
*/
public void add(BeanPersistListener<?> beanPersistListener) {
public void add(BeanPersistListener beanPersistListener) {
persistListeners.add(beanPersistListener);
}
/**
* Return the BeanPersistListener instances.
*/
public List<BeanPersistListener<?>> getPersistListeners() {
public List<BeanPersistListener> getPersistListeners() {
return persistListeners;
}
@@ -1199,10 +1300,39 @@ public class ServerConfig {
* BeanPersistListener instances one at a time.
* </p>
*/
public void setPersistListeners(List<BeanPersistListener<?>> persistListeners) {
public void setPersistListeners(List<BeanPersistListener> persistListeners) {
this.persistListeners = persistListeners;
}
/**
* Return the default PersistenceContextScope to be used if one is not explicitly set on a query.
* <p/>
* The PersistenceContextScope can specified on each query via {@link com.avaje.ebean
* .Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)}. If it
* is not set on the query this default scope is used.
*
* @see com.avaje.ebean.Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)
*/
public PersistenceContextScope getPersistenceContextScope() {
// if somehow null return TRANSACTION scope
return persistenceContextScope == null ? PersistenceContextScope.TRANSACTION : persistenceContextScope;
}
/**
* Set the PersistenceContext scope to be used if one is not explicitly set on a query.
* <p/>
* This defaults to {@link PersistenceContextScope#TRANSACTION}.
* <p/>
* The PersistenceContextScope can specified on each query via {@link com.avaje.ebean
* .Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)}. If it
* is not set on the query this scope is used.
*
* @see com.avaje.ebean.Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)
*/
public void setPersistenceContextScope(PersistenceContextScope persistenceContextScope) {
this.persistenceContextScope = persistenceContextScope;
}
/**
* Load the settings from the ebean.properties file.
*/
@@ -1257,8 +1387,6 @@ public class ServerConfig {
/**
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
*
* @param p
*/
protected void loadAutofetchConfig(PropertySource p) {
autofetchConfig.loadSettings(p);
@@ -1281,6 +1409,7 @@ public class ServerConfig {
loadDataSourceSettings(p);
autoCommitMode = p.getBoolean("autoCommitMode", false);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
namingConvention = createNamingConvention(p);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
@@ -1315,16 +1444,26 @@ public class ServerConfig {
int batchSize = p.getInt("batch.size", 20);
persistBatchSize = p.getInt("persistBatchSize", batchSize);
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope","TRANSACTION"));
dataSourceJndiName = p.get("dataSourceJndiName", null);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", 20);
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
localTimeWithNanos = p.getBoolean("localTimeWithNanos", false);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
String jsonDateTimeFormat = p.get("jsonDateTime", null);
if (jsonDateTimeFormat != null) {
jsonDateTime = JsonConfig.DateTime.valueOf(jsonDateTimeFormat);
} else {
jsonDateTime = JsonConfig.DateTime.MILLIS;
}
ddlGenerate = p.getBoolean("ddl.generate", false);
ddlRun = p.getBoolean("ddl.run", false);
@@ -6,6 +6,7 @@ import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,40 +15,61 @@ import org.slf4j.LoggerFactory;
*/
public class DatabasePlatform {
/** The Constant logger. */
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
/** The open quote used by quoted identifiers. */
/**
* The open quote used by quoted identifiers.
*/
protected String openQuote = "\"";
/** The close quote used by quoted identifiers. */
/**
* The close quote used by quoted identifiers.
*/
protected String closeQuote = "\"";
/** For limit/offset, row_number etc limiting of SQL queries. */
/**
* For limit/offset, row_number etc limiting of SQL queries.
*/
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
/** Mapping of JDBC to Database types. */
/**
* Mapping of JDBC to Database types.
*/
protected DbTypeMap dbTypeMap = new DbTypeMap();
/** DB specific DDL syntax. */
/**
* DB specific DDL syntax.
*/
protected DbDdlSyntax dbDdlSyntax = new DbDdlSyntax();
/** Defines DB identity/sequence features. */
/**
* Defines DB identity/sequence features.
*/
protected DbIdentity dbIdentity = new DbIdentity();
/** The JDBC type to map booleans to (by default). */
/**
* The JDBC type to map booleans to (by default).
*/
protected int booleanDbType = Types.BOOLEAN;
/** The JDBC type to map Blob to. */
/**
* The JDBC type to map Blob to.
*/
protected int blobDbType = Types.BLOB;
/** The JDBC type to map Clob to. */
/**
* The JDBC type to map Clob to.
*/
protected int clobDbType = Types.CLOB;
/** For Oracle treat empty strings as null. */
/**
* For Oracle treat empty strings as null.
*/
protected boolean treatEmptyStringsAsNull;
/** The name. */
/**
* The database platform name.
*/
protected String name = "generic";
/**
@@ -57,6 +79,11 @@ public class DatabasePlatform {
*/
private static final char BACK_TICK = '`';
/**
* The like clause. Can be overridden to disable default escape character.
*/
protected String likeClause = "like ?";
protected DbEncrypt dbEncrypt;
protected boolean idInExpandedForm;
@@ -299,4 +326,13 @@ public class DatabasePlatform {
return sql;
}
/**
* Returns the like clause used by this database platform.
* <p>
* This may include an escape clause to disable a default escape character.
*/
public String getLikeClause() {
return likeClause;
}
}
@@ -14,6 +14,7 @@ public class H2Platform extends DatabasePlatform {
super();
this.name = "h2";
this.dbEncrypt = new H2DbEncrypt();
this.likeClause = "like ? escape''";
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
@@ -20,6 +20,7 @@ public class MySqlPlatform extends DatabasePlatform {
public MySqlPlatform() {
super();
this.name = "mysql";
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
@@ -23,6 +23,7 @@ public class PostgresPlatform extends DatabasePlatform {
public PostgresPlatform() {
super();
this.name = "postgres";
this.likeClause = "like ? escape''";
this.dbDdlSyntax = new PostgresDdlSyntax();
@@ -0,0 +1,78 @@
package com.avaje.ebean.event;
import java.util.Set;
/**
* Provides a base implementation of BeanPersistListener.
* <p>
* Objects extending this should override the methods then are interested in.
* The default inserted() updated() and deleted() methods return false and as such
* means other servers in the cluster are not notified.
* </p>
*/
public abstract class AbstractBeanPersistListener implements BeanPersistListener {
/**
* Notified that a bean has been inserted locally. Return true if you want the
* cluster to be notified of the event.
*
* @param bean The bean that was inserted.
*/
@Override
public boolean inserted(Object bean) {
return false;
}
/**
* Notified that a bean has been updated locally. Return true if you want the
* cluster to be notified of the event.
*
* @param bean The bean that was updated.
* @param updatedProperties The properties that were modified by this update.
*/
@Override
public boolean updated(Object bean, Set<String> updatedProperties) {
return false;
}
/**
* Notified that a bean has been deleted locally. Return true if you want the
* cluster to be notified of the event.
*
* @param bean The bean that was deleted.
*/
@Override
public boolean deleted(Object bean) {
return false;
}
/**
* Notify that a bean was inserted on another node of the cluster.
*
* @param id the id value of the inserted bean
*/
@Override
public void remoteInsert(Object id) {
// do nothing
}
/**
* Notify that a bean was updated on another node of the cluster.
*
* @param id the id value of the updated bean.
*/
@Override
public void remoteUpdate(Object id) {
// do nothing
}
/**
* Notify that a bean was deleted on another node of the cluster.
*
* @param id the id value of the deleted bean.
*/
@Override
public void remoteDelete(Object id) {
// do nothing
}
}
@@ -33,11 +33,17 @@ import com.avaje.ebean.config.ServerConfig;
* </p>
* <p>
* A BeanPersistListener is either found automatically via class path search or
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}.
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener<?>)}}.
* </p>
* @see ServerConfig#add(BeanPersistListener)
*/
public interface BeanPersistListener<T> {
public interface BeanPersistListener {
/**
* Return true if this BeanPersistListener should be registered for events
* on this entity type.
*/
public boolean isRegisterFor(Class<?> cls);
/**
* Notified that a bean has been inserted locally. Return true if you want the
@@ -46,7 +52,7 @@ public interface BeanPersistListener<T> {
* @param bean
* The bean that was inserted.
*/
public boolean inserted(T bean);
public boolean inserted(Object bean);
/**
* Notified that a bean has been updated locally. Return true if you want the
@@ -57,7 +63,7 @@ public interface BeanPersistListener<T> {
* @param updatedProperties
* The properties that were modified by this update.
*/
public boolean updated(T bean, Set<String> updatedProperties);
public boolean updated(Object bean, Set<String> updatedProperties);
/**
* Notified that a bean has been deleted locally. Return true if you want the
@@ -66,7 +72,7 @@ public interface BeanPersistListener<T> {
* @param bean
* The bean that was deleted.
*/
public boolean deleted(T bean);
public boolean deleted(Object bean);
/**
* Notify that a bean was inserted on another node of the cluster.
@@ -53,6 +53,9 @@ class PathPropertiesParser {
case '(':
return currentWord();
default:
if (pos == 1) {
return "";
}
}
} while (pos < eof);
throw new RuntimeException("Hit EOF while reading sectionTitle from " + startPos);
@@ -91,6 +94,10 @@ class PathPropertiesParser {
}
} while (pos < eof);
if (startPos < pos) {
String currentWord = source.substring(startPos, pos);
currentPathProps.addProperty(currentWord);
}
}
private void addSubpath() {
@@ -1,7 +1,7 @@
package com.avaje.ebean.text;
/**
* An exception occured typically in processing CSV, JSON or XML.
* An exception occurred typically in processing CSV, JSON or XML.
*
* @author rbygrave
*/
@@ -0,0 +1,115 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
/**
* Utility that converts between JSON content and simple java Maps/Lists.
*/
public class EJson {
/**
* Write the nested Map/List as json.
*/
public static String write(Object object) throws IOException {
return EJsonWriter.write(object);
}
/**
* Write the nested Map/List as json to the writer.
*/
public static void write(Object object, Writer writer) throws IOException {
EJsonWriter.write(object, writer);
}
/**
* Write the nested Map/List as json to the jsonGenerator.
*/
public static void write(Object object, JsonGenerator jsonGenerator) throws IOException {
EJsonWriter.write(object, jsonGenerator);
}
/**
* Parse the json and return as a Map.
*/
public static Map<String,Object> parseObject(String json) throws IOException {
return EJsonReader.parseObject(json);
}
/**
* Parse the json and return as a Map taking a reader.
*/
public static Map<String,Object> parseObject(Reader reader) throws IOException {
return EJsonReader.parseObject(reader);
}
/**
* Parse the json and return as a Map taking a JsonParser.
*/
public static Map<String,Object> parseObject(JsonParser parser) throws IOException {
return EJsonReader.parseObject(parser);
}
/**
* Parse the json and return as a Map taking a JsonParser and a starting token.
* <p>
* Used when the first token is checked to see if the value is null prior to calling this.
* </p>
*/
public static Map<String,Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return EJsonReader.parseObject(parser, token);
}
/**
* Parse the json and return as a List.
* @throws IOException
*/
public static List<Object> parseList(String json) throws IOException {
return EJsonReader.parseList(json);
}
/**
* Parse the json and return as a List taking a Reader.
* @throws IOException
*/
public static List<Object> parseList(Reader reader) throws IOException {
return EJsonReader.parseList(reader);
}
/**
* Parse the json and return as a List taking a JsonParser.
*/
public static List<Object> parseList(JsonParser parser) throws IOException {
return EJsonReader.parseList(parser);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(String json) throws IOException {
return EJsonReader.parse(json);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(Reader reader) throws IOException {
return EJsonReader.parse(reader);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(JsonParser parser) throws IOException {
return EJsonReader.parse(parser);
}
}
@@ -0,0 +1,282 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
class EJsonReader {
static JsonFactory json = new JsonFactory();
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(String json) throws IOException {
return (Map<String, Object>) parse(json);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(Reader reader) throws IOException {
return (Map<String, Object>) parse(reader);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(JsonParser parser) throws IOException {
return (Map<String, Object>) parse(parser);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return (Map<String, Object>)parse(parser, token);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(String json) throws IOException {
return (List<Object>) parse(json);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(Reader reader) throws IOException {
return (List<Object>) parse(reader);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(JsonParser parser) throws IOException {
return (List<Object>) parse(parser);
}
static Object parse(String json) throws IOException {
return parse(new StringReader(json));
}
static Object parse(Reader reader) throws IOException {
return parse(json.createParser(reader));
}
static Object parse(JsonParser parser) throws IOException {
return parse(parser, null);
}
static Object parse(JsonParser parser, JsonToken token) throws IOException {
return new EJsonReader(parser).parseJson(token);
}
private final JsonParser parser;
private int depth;
private Stack stack;
private Context currentContext;
EJsonReader(JsonParser parser) {
this.parser = parser;
}
private void startArray() {
depth++;
stack.push(currentContext);
currentContext = new ArrayContext();
}
private void startObject() {
depth++;
stack.push(currentContext);
currentContext = new ObjectContext();
}
private void endArray() {
end();
}
private void endObject() {
end();
}
private void end() {
depth--;
if (!stack.isEmpty()) {
currentContext = stack.pop(currentContext);
}
}
private void setValue(Object value) {
currentContext.setValue(value);
}
private void setValueNull() {
currentContext.setValueNull();
}
private Object parseJson(JsonToken token) throws IOException {
if (token == null) {
// no initial token so expect to read START_OBJECT or similar
token = parser.nextToken();
if (JsonToken.VALUE_NULL == token) {
return null;
}
}
stack = new Stack();
// it is a object or array, process the first JsonToken
processJsonToken(token);
// process the rest of the object or array
while (depth > 0) {
token = parser.nextToken();
processJsonToken(token);
}
return currentContext.getValue();
}
/**
* Process the JsonToken for objects and arrays.
*/
private void processJsonToken(JsonToken token) throws IOException {
switch (token) {
case START_ARRAY:
startArray();
break;
case START_OBJECT:
startObject();
break;
case FIELD_NAME:
currentContext.setKey(parser.getCurrentName());
break;
case VALUE_STRING:
setValue(parser.getValueAsString());
break;
case VALUE_NUMBER_INT:
setValue(parser.getLongValue());
break;
case VALUE_NUMBER_FLOAT:
setValue(parser.getDecimalValue());
break;
case VALUE_TRUE:
setValue(Boolean.TRUE);
break;
case VALUE_FALSE:
setValue(Boolean.FALSE);
break;
case VALUE_NULL:
setValueNull();
break;
case END_OBJECT:
endObject();
break;
case END_ARRAY:
endArray();
break;
default:
break;
}
}
private static final class Stack {
private Context head;
private void push(Context context) {
if (context != null) {
context.next = head;
head = context;
}
}
private Context pop(Context endingContext) {
if (head == null) {
throw new NoSuchElementException();
}
Context temp = head;
head = head.next;
temp.popContext(endingContext);
return temp;
}
private boolean isEmpty() {
return head == null;
}
}
private static abstract class Context {
Context next;
abstract void popContext(Context temp);
abstract Object getValue();
abstract void setKey(String key);
abstract void setValue(Object value);
abstract void setValueNull();
}
private static class ObjectContext extends Context {
private final Map<String, Object> map = new LinkedHashMap<String, Object>();
private String key;
public void popContext(Context temp) {
setValue(temp.getValue());
}
Object getValue() {
return map;
}
void setKey(String key) {
this.key = key;
}
void setValue(Object value) {
map.put(key, value);
}
void setValueNull() {
map.put(key, null);
}
}
private static class ArrayContext extends Context {
private final List<Object> values = new ArrayList<Object>();
public void popContext(Context temp) {
values.add(temp.getValue());
}
Object getValue() {
return values;
}
void setValue(Object value) {
values.add(value);
}
void setValueNull() {
// ignore
}
void setKey(String key) {
// not expected
}
}
}
@@ -0,0 +1,209 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
class EJsonWriter {
/**
* Base jsonFactory implementation used when it is not passed in.
*/
static JsonFactory jsonFactory = new JsonFactory();
static String write(Object object) throws IOException {
StringWriter writer = new StringWriter(200);
write(object, writer);
return writer.toString();
}
static void write(Object object, Writer writer) throws IOException {
JsonGenerator generator = jsonFactory.createGenerator(writer);
write(object, generator);
generator.close();
}
static void write(Object object, JsonGenerator jsonGenerator) {
new EJsonWriter(jsonGenerator).writeJson(object);
}
private final JsonGenerator jsonGenerator;
private EJsonWriter(JsonGenerator jsonGenerator) {
this.jsonGenerator = jsonGenerator;
}
private void writeJson(Object object) {
writeJson(null, object);
}
@SuppressWarnings("unchecked")
private void writeJson(String name, Object object) {
try {
if (object == null) {
writeNull(name);
} else if (object instanceof Map) {
writeMap(name, (Map<Object, Object>) object);
} else if (object instanceof Collection) {
writeCollection(name, (Collection<Object>) object);
} else if (object instanceof Boolean) {
writeBoolean(name, (Boolean) object);
} else if (object instanceof Number) {
writeNumber(name, (Number) object);
} else if (object instanceof Date) {
writeDate(name, (Date) object);
} else if (object instanceof String) {
writeString(name, (String) object);
} else if (object instanceof Map.Entry<?, ?>) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
writeJson(entry.getKey().toString(), entry.getValue());
} else {
writeString(name, object.toString());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void writeBoolean(String name, Boolean object) throws IOException {
if (name == null) {
jsonGenerator.writeBoolean(object);
} else {
jsonGenerator.writeBooleanField(name, object);
}
}
private void writeDate(String name, Date object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber(object.getTime());
} else {
jsonGenerator.writeNumberField(name, object.getTime());
}
}
private void writeNumber(String name, Number object) throws IOException {
if (object instanceof Long) {
writeLong(name, object);
} else if (object instanceof Integer) {
writeInteger(name, object);
} else if (object instanceof Double) {
writeDouble(name, object);
} else if (object instanceof BigDecimal) {
writeBigDecimal(name, object);
} else if (object instanceof BigInteger) {
writeBigInteger(name, object);
} else {
writeGeneralNumber(name, object);
}
}
private void writeGeneralNumber(String name, Number object) throws IOException {
writeBigDecimal(name, new BigDecimal(object.toString()));
}
private void writeBigDecimal(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((BigDecimal) object);
} else {
jsonGenerator.writeNumberField(name, (BigDecimal) object);
}
}
private void writeBigInteger(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((BigInteger) object);
} else {
jsonGenerator.writeNumberField(name, object.longValue());
}
}
private void writeDouble(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((Double) object);
} else {
jsonGenerator.writeNumberField(name, (Double) object);
}
}
private void writeLong(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((Long) object);
} else {
jsonGenerator.writeNumberField(name, (Long) object);
}
}
private void writeInteger(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((Integer) object);
} else {
jsonGenerator.writeNumberField(name, (Integer) object);
}
}
private void writeNull(String name) throws IOException {
if (name == null) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNullField(name);
}
}
private void writeString(String name, String object) throws IOException {
if (name == null) {
jsonGenerator.writeString(object);
} else {
jsonGenerator.writeStringField(name, object);
}
}
private void writeCollection(String name, Collection<Object> collection) throws IOException {
if (name != null) {
jsonGenerator.writeFieldName(name);
}
jsonGenerator.writeStartArray();
for (Object object : collection) {
writeJson(null, object);
}
jsonGenerator.writeEndArray();
}
private void writeMap(String name, Map<Object, Object> map) throws IOException {
if (name != null) {
jsonGenerator.writeFieldName(name);
}
jsonGenerator.writeStartObject();
Set<Entry<Object, Object>> entrySet = map.entrySet();
for (Entry<Object, Object> entry : entrySet) {
writeJson(entry.getKey().toString(), entry.getValue());
}
jsonGenerator.writeEndObject();
}
}
@@ -1,5 +1,8 @@
package com.avaje.ebean.text.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
@@ -7,125 +10,105 @@ import java.util.List;
/**
* Converts objects to and from JSON format.
*
* @author rbygrave
*/
public interface JsonContext {
/**
* Convert json string input into a Bean of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> T toBean(Class<T> rootType, String json);
public <T> T toBean(Class<T> rootType, String json) throws JsonIOException;
/**
* Convert json reader input into a Bean of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> T toBean(Class<T> rootType, Reader json);
/**
* Convert json string input into a Bean of a specific type with options.
*/
public <T> T toBean(Class<T> rootType, String json, JsonReadOptions options);
/**
* Convert json reader input into a Bean of a specific type with options.
*/
public <T> T toBean(Class<T> rootType, Reader json, JsonReadOptions options);
public <T> T toBean(Class<T> rootType, Reader json) throws JsonIOException;
/**
* Convert json string input into a list of beans of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> List<T> toList(Class<T> rootType, String json);
/**
* Convert json string input into a list of beans of a specific type with
* options.
*/
public <T> List<T> toList(Class<T> rootType, String json, JsonReadOptions options);
public <T> List<T> toList(Class<T> rootType, String json) throws JsonIOException;
/**
* Convert json reader input into a list of beans of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> List<T> toList(Class<T> rootType, Reader json);
/**
* Convert json reader input into a list of beans of a specific type with
* options.
*/
public <T> List<T> toList(Class<T> rootType, Reader json, JsonReadOptions options);
public <T> List<T> toList(Class<T> rootType, Reader json) throws JsonIOException;
/**
* Use the genericType to determine if this should be converted into a List or
* bean.
*
* @throws JsonIOException When IOException occurs
*/
public Object toObject(Type genericType, Reader json, JsonReadOptions options);
public Object toObject(Type genericType, Reader json) throws JsonIOException;
/**
* Use the genericType to determine if this should be converted into a List or
* bean.
*
* @throws JsonIOException When IOException occurs
*/
public Object toObject(Type genericType, String json, JsonReadOptions options);
public Object toObject(Type genericType, String json) throws JsonIOException;
/**
* Write the bean or collection in JSON format to the writer with default
* options.
*
* @param o
* the bean or collection of beans to write
* @param writer
* used to write the json output to
*
* @param value the bean or collection of beans to write
* @param writer used to write the json output to
* @throws JsonIOException When IOException occurs
*/
public void toJsonWriter(Object o, Writer writer);
/**
* With additional pretty output option.
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty);
public void toJson(Object value, Writer writer) throws JsonIOException;
/**
* With additional options to specify JsonValueAdapter and
* JsonWriteBeanVisitor's.
*
* @param o
* the bean or collection of beans to write
* @param writer
* used to write the json output to
* @param options
* additional options to control the JSON output
*
* @param value the bean or collection of beans to write
* @param writer used to write the json output to
* @param options additional options to control the JSON output
* @throws JsonIOException When IOException occurs
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options);
/**
* With additional JSONP callback function.
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options,
String callback);
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException;
/**
* Convert a bean or collection to json string using default options.
*
* @throws JsonIOException When IOException occurs
*/
public String toJsonString(Object o);
public String toJson(Object value) throws JsonIOException;
/**
* Convert a bean or collection to json string with pretty format using
* default options.
* Convert a bean or collection to json string.
*
* @throws JsonIOException When IOException occurs
*/
public String toJsonString(Object o, boolean pretty);
public String toJson(Object value, JsonWriteOptions options) throws JsonIOException;
/**
* Convert a bean or collection to json string using options.
*/
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options);
/**
* Convert a bean or collection to json string using a JSONP callback.
*/
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback);
/**
* Return true if the type is known as an Entity or Xml type or a List Set or
* Map of known bean types.
* Return true if the type is known as an Entity bean or a List Set or
* Map of entity beans.
*/
public boolean isSupportedType(Type genericType);
/**
* Create and return a new JsonGenerator for the given writer.
*
* @throws JsonIOException When IOException occurs
*/
public JsonGenerator createGenerator(Writer writer) throws JsonIOException;
/**
* Create and return a new JsonParser for the given reader.
*
* @throws JsonIOException When IOException occurs
*/
public JsonParser createParser(Reader reader) throws JsonIOException;
}
@@ -1,39 +0,0 @@
package com.avaje.ebean.text.json;
/**
* Marker interface for all the Raw JSON types.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public interface JsonElement {
/**
* Return true if this is a JSON primitive type (null, boolean, number or
* string).
*/
public boolean isPrimitive();
/**
* Return the string value of this primitive JSON element.
* <p>
* This can not be used for JsonElementObject or JsonElementArray.
* </p>
*/
public String toPrimitiveString();
public Object eval(String exp);
public int evalInt(String exp);
public String evalString(String exp);
public boolean evalBoolean(String exp);
}
@@ -1,112 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.ArrayList;
import java.util.List;
/**
* JSON Array element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementArray implements JsonElement {
private final List<JsonElement> values = new ArrayList<JsonElement>();
public List<JsonElement> getValues() {
return values;
}
public void add(JsonElement value) {
values.add(value);
}
public String toString() {
return values.toString();
}
public boolean isPrimitive() {
return false;
}
public String toPrimitiveString() {
return null;
}
private String[] split(String exp) {
int pos = exp.indexOf('.');
if (pos == -1) {
return new String[] { exp, null };
}
String exp0 = exp.substring(0, pos);
String exp1 = exp.substring(pos + 1);
return new String[] { exp0, exp1 };
}
public Object eval(String exp) {
String[] e = split(exp);
return eval(e[0], e[1]);
}
public int evalInt(String exp) {
String[] e = split(exp);
return evalInt(e[0], e[1]);
}
public String evalString(String exp) {
String[] e = split(exp);
return evalString(e[0], e[1]);
}
public boolean evalBoolean(String exp) {
// TODO Auto-generated method stub
return false;
}
private Object eval(String exp0, String exp1) {
if ("size".equals(exp0)) {
return values.size();
}
if ("isEmpty".equals(exp0)) {
return values.isEmpty();
}
int idx = Integer.parseInt(exp0);
JsonElement element = values.get(idx);
return element.eval(exp1);
}
private int evalInt(String exp0, String exp1) {
if ("size".equals(exp0)) {
return values.size();
}
if ("isEmpty".equals(exp0)) {
return values.isEmpty() ? 1 : 0;
}
int idx = Integer.parseInt(exp0);
JsonElement element = values.get(idx);
return element.evalInt(exp1);
}
private String evalString(String exp0, String exp1) {
if ("size".equals(exp0)) {
return String.valueOf(values.size());
}
if ("isEmpty".equals(exp0)) {
return String.valueOf(values.isEmpty());
}
int idx = Integer.parseInt(exp0);
JsonElement element = values.get(idx);
return element.evalString(exp1);
}
public String getString() {
return toString();
}
}
@@ -1,63 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON boolean element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementBoolean implements JsonElement {
public static final JsonElementBoolean TRUE = new JsonElementBoolean(true);
public static final JsonElementBoolean FALSE = new JsonElementBoolean(false);
private final Boolean value;
private JsonElementBoolean(Boolean value) {
this.value = value;
}
public Boolean getValue() {
return value;
}
public String toString() {
return Boolean.toString(value);
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return value.toString();
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on boolean");
}
return value;
}
public int evalInt(String exp) {
return value ? 1 : 0;
}
public String evalString(String exp) {
return toString();
}
public boolean evalBoolean(String exp) {
return value;
}
}
@@ -1,57 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON null element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementNull implements JsonElement {
public static final JsonElementNull NULL = new JsonElementNull();
private JsonElementNull() {
}
public String getValue() {
return "null";
}
public String toString() {
return "json null";
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return null;
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on null");
}
return null;
}
public int evalInt(String exp) {
return 0;
}
public String evalString(String exp) {
return null;
}
public boolean evalBoolean(String exp) {
return false;
}
}
@@ -1,67 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON number element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementNumber implements JsonElement {
private final String value;
public JsonElementNumber(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String toString() {
return value;
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return value;
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Double.parseDouble(value);
}
public int evalInt(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Integer.parseInt(value);
}
public String evalString(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return value;
}
public boolean evalBoolean(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Boolean.parseBoolean(value);
}
}
@@ -1,108 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* JSON Object element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementObject implements JsonElement {
private final Map<String, JsonElement> map = new LinkedHashMap<String, JsonElement>();
public void put(String key, JsonElement value) {
map.put(key, value);
}
private String[] split(String exp) {
int pos = exp.indexOf('.');
if (pos == -1) {
return new String[] { exp, null };
}
String exp0 = exp.substring(0, pos);
String exp1 = exp.substring(pos + 1);
return new String[] { exp0, exp1 };
}
public Object eval(String exp) {
String[] e = split(exp);
return eval(e[0], e[1]);
}
public int evalInt(String exp) {
String[] e = split(exp);
return evalInt(e[0], e[1]);
}
public String evalString(String exp) {
if (exp == null) {
return map.toString();
}
String[] e = split(exp);
return evalString(e[0], e[1]);
}
public boolean evalBoolean(String exp) {
String[] e = split(exp);
return evalBoolean(e[0], e[1]);
}
private Object eval(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? null : e.eval(exp1);
}
private int evalInt(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? 0 : e.evalInt(exp1);
}
private String evalString(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? "" : e.evalString(exp1);
}
private boolean evalBoolean(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? false : e.evalBoolean(exp1);
}
public JsonElement get(String key) {
return map.get(key);
}
public JsonElement getValue(String key) {
return map.get(key);
}
public Set<String> keySet() {
return map.keySet();
}
public Set<Map.Entry<String, JsonElement>> entrySet() {
return map.entrySet();
}
public String toString() {
return map.toString();
}
public boolean isPrimitive() {
return false;
}
public String toPrimitiveString() {
return null;
}
}
@@ -1,71 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON string element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementString implements JsonElement {
private final String value;
public JsonElementString(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String toString() {
return value;
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return value;
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return value;
}
public int evalInt(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return 0;
}
}
public String evalString(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return value;
}
public boolean evalBoolean(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Boolean.parseBoolean(exp);
}
}
@@ -0,0 +1,24 @@
package com.avaje.ebean.text.json;
/**
* Unchecked exception thrown when an IOException occurs in json processing.
* <p>
* Typically wraps the checked IOException.
* </p>
*/
public class JsonIOException extends RuntimeException {
/**
* Construct with an underlying cause.
*/
public JsonIOException(Throwable cause) {
super(cause);
}
/**
* Construct with a message.
*/
public JsonIOException(String message) {
super(message);
}
}
@@ -1,34 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.Map;
/**
* Provides for some custom handling of json content as it is read.
* <p>
* This visit method is called after all the known properties of the bean have
* been processed. Any JSON elements that could not be mapped to known bean
* properties are available in the unmapped Map.
* </p>
*
* @author rbygrave
*
* @param <T>
* The type of entity bean
*/
public interface JsonReadBeanVisitor<T> {
/**
* Visit the bean that has just been processed.
* <p>
* This provides a method of customising the bean and processing any custom
* JSON content.
* </p>
*
* @param bean
* the bean being processed
* @param unmapped
* Map of any JSON elements that didn't map to known bean properties
*/
public void visit(T bean, Map<String, JsonElement> unmapped);
}
@@ -1,71 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Provides the ability to customise the reading of JSON content.
* <p>
* You can optionally provide a custom JsonValueAdapter to handle specific
* formatting for Date and DateTime types.
* </p>
* <p>
* You can optionally register JsonReadBeanVisitors to customise the processing
* of the beans as they are processed and handle any custom JSON elements that
* could not be mapped to bean properties.
* </p>
*
* @author rbygrave
*
*/
public class JsonReadOptions {
protected JsonValueAdapter valueAdapter;
protected Map<String, JsonReadBeanVisitor<?>> visitorMap;
/**
* Default constructor.
*/
public JsonReadOptions() {
this.visitorMap = new LinkedHashMap<String, JsonReadBeanVisitor<?>>();
}
/**
* Return the JsonValueAdapter.
*/
public JsonValueAdapter getValueAdapter() {
return valueAdapter;
}
/**
* Return the map of JsonReadBeanVisitor's.
*/
public Map<String, JsonReadBeanVisitor<?>> getVisitorMap() {
return visitorMap;
}
/**
* Set a JsonValueAdapter for custom DateTime and Date formatting.
*/
public JsonReadOptions setValueAdapter(JsonValueAdapter valueAdapter) {
this.valueAdapter = valueAdapter;
return this;
}
/**
* Register a JsonReadBeanVisitor for the root level.
*/
public JsonReadOptions addRootVisitor(JsonReadBeanVisitor<?> visitor) {
return addVisitor(null, visitor);
}
/**
* Register a JsonReadBeanVisitor for a given path.
*/
public JsonReadOptions addVisitor(String path, JsonReadBeanVisitor<?> visitor) {
visitorMap.put(path, visitor);
return this;
}
}
@@ -1,61 +0,0 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebean.text.json;
import java.sql.Timestamp;
/**
* Allows you to customise the Date and Timestamp formats.
* <p>
* There is not a standard JSON format for Date or Timestamp types. By default
* Ebean uses ISO8601 "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and "yyyy-MM-dd".
* </p>
* <p>
* Note that Ebean will convert Joda types to either of the Date or Timestamp
* types and back for you.
* </p>
*
* @see JsonReadOptions
*
* @author rbygrave
*/
public interface JsonValueAdapter {
/**
* Convert the Date to json string.
*/
public String jsonFromDate(java.sql.Date date);
/**
* Convert the DateTime to json string.
*/
public String jsonFromTimestamp(java.sql.Timestamp date);
/**
* Parse the JSON string into a Date.
*/
public java.sql.Date jsonToDate(String jsonDate);
/**
* Parse the JSON DateTime into a Timestamp.
*/
public Timestamp jsonToTimestamp(String jsonDateTime);
}
@@ -1,33 +0,0 @@
package com.avaje.ebean.text.json;
/**
* Allows for customising the JSON write processing.
* <p>
* You can use this to add raw JSON content via {@link JsonWriter}.
* </p>
* <p>
* You register a JsonWriteBeanVisitor with {@link JsonWriteOptions}.
* </p>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean
*
* @see JsonWriteOptions
*/
public interface JsonWriteBeanVisitor<T> {
/**
* Visit the bean that has just been writing it's content to JSON. You can
* write your own additional JSON content to the JsonWriter if you wish.
*
* @param bean
* the bean that has been writing it's content
* @param jsonWriter
* the JsonWriter which you can append custom json content to if you
* wish.
*/
public void visit(T bean, JsonWriter jsonWriter);
}
@@ -1,78 +1,23 @@
package com.avaje.ebean.text.json;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.text.PathProperties;
/**
* Provides options for customising the JSON write process.
* <p>
* You can optionally provide a custom JsonValueAdapter to handle specific
* formatting for Date and DateTime types.
* </p>
* <p>
* You can optionally register JsonWriteBeanVisitors to customise the processing
* of the beans as they are processed and <strong>add raw JSON
* elements</strong>.
* </p>
* <p>
* You can explicitly state which properties to include in the JSON output for
* the root level and each path.
* </p>
*
* <pre class="code">
* // find some customers ...
*
* List&lt;Customer&gt; list = Ebean.find(Customer.class).select(&quot;id, name, status, shippingAddress&quot;)
* .fetch(&quot;billingAddress&quot;,
* &quot;line1, city&quot;).fetch(&quot;billingAddress.country&quot;, &quot;*&quot;).fetch(&quot;contacts&quot;, &quot;firstName,email&quot;)
* .order().desc(&quot;id&quot;)
* .findList();
*
* JsonContext json = Ebean.createJsonContext();
*
* JsonWriteOptions writeOptions = new JsonWriteOptions();
* writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor&lt;Customer&gt;() {
*
* public void visit(Customer bean, JsonWriter ctx) {
* System.out.println(&quot;write visit customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummyCust&quot;, &quot;34&quot;);
* ctx.appendKeyValue(&quot;smallCustObject&quot;, &quot;{\&quot;a\&quot;:34,\&quot;b\&quot;:\&quot;asdasdasd\&quot;}&quot;);
* }
* });
*
* writeOptions.setPathProperties(&quot;contacts&quot;, &quot;firstName,id&quot;);
* writeOptions.setPathVisitor(&quot;contacts&quot;, new JsonWriteBeanVisitor&lt;Contact&gt;() {
*
* public void visit(Contact bean, JsonWriter ctx) {
* System.out.println(&quot;write additional custom json on customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummy&quot;, &quot; 3400&quot; + bean.getId() + &quot;&quot;);
* ctx.appendKeyValue(&quot;smallObject&quot;, &quot;{\&quot;contactA\&quot;:34,\&quot;contactB\&quot;:\&quot;banana\&quot;}&quot;);
* }
*
* });
*
* // output as a JSON string with pretty formatting
* String s = json.toJsonString(list, true, writeOptions);
* String s = json.toJson(list, true, writeOptions);
*
* </pre>
*
* @see JsonContext#toList(Class, String, JsonReadOptions)
*
* @author rbygrave
*
*/
public class JsonWriteOptions {
protected String callback;
protected JsonValueAdapter valueAdapter;
protected Map<String, JsonWriteBeanVisitor<?>> visitorMap;
protected PathProperties pathProperties;
/**
@@ -88,138 +33,6 @@ public class JsonWriteOptions {
return o;
}
/**
* This creates and returns a copy of these options.
* <p>
* Note that it assumes that the JsonWriteBeanVisitor (if defined) are
* immutable and any JsonWriteBeanVisitor instances are shared between the
* original and the copy.
* </p>
*/
public JsonWriteOptions copy() {
JsonWriteOptions copy = new JsonWriteOptions();
copy.callback = callback;
copy.valueAdapter = valueAdapter;
copy.pathProperties = pathProperties;
if (visitorMap != null) {
copy.visitorMap = new HashMap<String, JsonWriteBeanVisitor<?>>(visitorMap);
}
return copy;
}
/**
* Return a JSONP callback function.
*/
public String getCallback() {
return callback;
}
/**
* Set a JSONP callback function.
*/
public JsonWriteOptions setCallback(String callback) {
this.callback = callback;
return this;
}
/**
* Return the JsonValueAdapter.
*/
public JsonValueAdapter getValueAdapter() {
return valueAdapter;
}
/**
* Set a JsonValueAdapter for custom DateTime and Date formatting.
*/
public JsonWriteOptions setValueAdapter(JsonValueAdapter valueAdapter) {
this.valueAdapter = valueAdapter;
return this;
}
/**
* Register a JsonWriteBeanVisitor for the root level.
*/
public JsonWriteOptions setRootPathVisitor(JsonWriteBeanVisitor<?> visitor) {
return setPathVisitor(null, visitor);
}
/**
* Register a JsonWriteBeanVisitor for the given path.
*/
public JsonWriteOptions setPathVisitor(String path, JsonWriteBeanVisitor<?> visitor) {
if (visitorMap == null) {
visitorMap = new HashMap<String, JsonWriteBeanVisitor<?>>();
}
visitorMap.put(path, visitor);
return this;
}
/**
* Set the properties to include in the JSON output for the given path.
*
* @param propertiesToInclude
* The set of properties to output
*/
public JsonWriteOptions setPathProperties(String path, Set<String> propertiesToInclude) {
if (pathProperties == null) {
pathProperties = new PathProperties();
}
pathProperties.put(path, propertiesToInclude);
return this;
}
/**
* Set the properties to include in the JSON output for the given path.
*
* @param propertiesToInclude
* Comma delimited list of properties to output
*/
public JsonWriteOptions setPathProperties(String path, String propertiesToInclude) {
return setPathProperties(path, parseProps(propertiesToInclude));
}
/**
* Set the properties to include in the JSON output for the root level.
*
* @param propertiesToInclude
* Comma delimited list of properties to output
*/
public JsonWriteOptions setRootPathProperties(String propertiesToInclude) {
return setPathProperties(null, parseProps(propertiesToInclude));
}
/**
* Set the properties to include in the JSON output for the root level.
*
* @param propertiesToInclude
* The set of properties to output
*/
public JsonWriteOptions setRootPathProperties(Set<String> propertiesToInclude) {
return setPathProperties(null, propertiesToInclude);
}
private Set<String> parseProps(String propertiesToInclude) {
LinkedHashSet<String> props = new LinkedHashSet<String>();
String[] split = propertiesToInclude.split(",");
for (int i = 0; i < split.length; i++) {
String s = split[i].trim();
if (s.length() > 0) {
props.add(s);
}
}
return props;
}
/**
* Return the Map of registered JsonWriteBeanVisitor's by path.
*/
public Map<String, JsonWriteBeanVisitor<?>> getVisitorMap() {
return visitorMap;
}
/**
* Set the Map of properties to include by path.
*/
@@ -1,28 +0,0 @@
package com.avaje.ebean.text.json;
/**
* The JSON Writer made available to JsonWriteBeanVisitor's so that you can
* append your own JSON content into the output.
*
* @see JsonWriteBeanVisitor
* @see JsonWriteOptions#setRootPathVisitor(JsonWriteBeanVisitor)
* @see JsonWriteOptions#setPathVisitor(String, JsonWriteBeanVisitor)
*
* @author rbygrave
*/
public interface JsonWriter {
/**
* Use this to append some custom content into the JSON output.
*
* @param key
* the json key
*
* @param rawJsonValue
* raw json value
*/
public void appendRawValue(String key, String rawJsonValue);
public void appendQuoteEscapeValue(String key, String rawJsonValue);
}
@@ -19,31 +19,10 @@
* .order().desc(&quot;id&quot;)
* .findList();
*
* JsonContext json = Ebean.createJsonContext();
*
* JsonWriteOptions writeOptions = new JsonWriteOptions();
* writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor&lt;Customer&gt;() {
*
* public void visit(Customer bean, JsonWriter ctx) {
* System.out.println(&quot;write visit customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummyCust&quot;, &quot;34&quot;);
* ctx.appendKeyValue(&quot;smallCustObject&quot;, &quot;{\&quot;a\&quot;:34,\&quot;b\&quot;:\&quot;asdasdasd\&quot;}&quot;);
* }
* });
*
* writeOptions.setPathProperties(&quot;contacts&quot;, &quot;firstName,id&quot;);
* writeOptions.setPathVisitor(&quot;contacts&quot;, new JsonWriteBeanVisitor&lt;Contact&gt;() {
*
* public void visit(Contact bean, JsonWriter ctx) {
* System.out.println(&quot;write additional custom json on customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummy&quot;, &quot; 3400&quot; + bean.getId() + &quot;&quot;);
* ctx.appendKeyValue(&quot;smallObject&quot;, &quot;{\&quot;contactA\&quot;:34,\&quot;contactB\&quot;:\&quot;banana\&quot;}&quot;);
* }
*
* });
*
* // output as a JSON string with pretty formatting
* String s = json.toJsonString(list, true, writeOptions);
* JsonContext json = Ebean.json();
*
* // output as a JSON string
* String jsonOutput = json.toJson(list);
*
* </pre>
*/
@@ -31,6 +31,12 @@ public class BindParams implements Serializable {
*/
private String preparedSql;
/**
* Bind hash and count used to detect when the bind values have changed such
* that the generated SQL (with named parameters) needs to be recalculated.
*/
private int[] bindHash;
public BindParams() {
}
@@ -51,20 +57,33 @@ public class BindParams implements Serializable {
* </p>
*/
public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
int[] vals = calcQueryPlanHash();
builder.add(vals[0]).bind(vals[1]);
}
/**
* Calculate and return a query plan bind hash with total bind count.
*/
public int[] calcQueryPlanHash() {
int tempBindCount;
int bc = 0;
int hc = 31;
for (Param param : positionedParameters) {
hc = hc * 31 + param.queryBindCount();
tempBindCount = param.queryBindCount();
bc += tempBindCount;
hc = hc * 31 + tempBindCount;
}
for (Map.Entry<String, Param> entry : namedParameters.entrySet()) {
tempBindCount = entry.getValue().queryBindCount();
bc += tempBindCount;
hc = hc * 31 + entry.getKey().hashCode();
hc = hc * 31 + entry.getValue().queryBindCount();
hc = hc * 31 + tempBindCount;
}
int bindCount = positionedParameters.size() + namedParameters.size();
builder.add(hc).bind(bindCount);
return new int[]{hc, bc};
}
/**
* Return a deep copy of the BindParams.
*/
@@ -110,7 +129,7 @@ public class BindParams implements Serializable {
* parameters ordered.
*/
public boolean requiresNamedParamsPrepare() {
return !namedParameters.isEmpty() && positionedParameters.isEmpty();
return !namedParameters.isEmpty();
}
/**
@@ -150,7 +169,7 @@ public class BindParams implements Serializable {
}
private Param getParam(String name) {
Param p = (Param) namedParameters.get(name);
Param p = namedParameters.get(name);
if (p == null) {
p = new Param();
namedParameters.put(name, p);
@@ -165,7 +184,7 @@ public class BindParams implements Serializable {
positionedParameters.add(new Param());
}
}
return (Param) positionedParameters.get(position - 1);
return positionedParameters.get(position - 1);
}
/**
@@ -253,7 +272,29 @@ public class BindParams implements Serializable {
return preparedSql;
}
/**
/**
* Return true if the bind hash and count has not changed.
*/
public boolean isSameBindHash() {
if (bindHash == null) {
bindHash = calcQueryPlanHash();
return false;
}
int[] oldPlan = bindHash;
bindHash = calcQueryPlanHash();
return bindHash[0] == oldPlan[0] && bindHash[1] == oldPlan[1];
}
/**
* Create a new positioned parameters orderedList.
*/
public OrderedList createOrderedList() {
positionedParameters.clear();
return new OrderedList(positionedParameters);
}
/**
* The bind parameters in the correct binding order.
* <p>
* This is the result of converting sql with named parameters
@@ -327,8 +368,6 @@ public class BindParams implements Serializable {
private Object outValue;
private int textLocation;
/**
* Construct a Parameter.
*/
@@ -368,17 +407,8 @@ public class BindParams implements Serializable {
}
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o == this) {
return true;
}
if (o instanceof Param) {
return hashCode() == o.hashCode();
}
return false;
}
return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode());
}
/**
* Return true if this is an In parameter that needs to be bound before
@@ -463,21 +493,6 @@ public class BindParams implements Serializable {
this.outValue = out;
}
/**
* Return the location this parameter was found in the sql text.
*/
public int getTextLocation() {
return textLocation;
}
/**
* Set the location in the sql text this parameter was located. This is
* used to control order for named parameters.
*/
public void setTextLocation(int textLocation) {
this.textLocation = textLocation;
}
/**
* If true do not include this value in a transaction log.
*/
@@ -11,6 +11,8 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface LoadBeanBuffer {
public int getBatchSize();
public List<EntityBeanIntercept> getBatch();
public BeanDescriptor<?> getBeanDescriptor();
@@ -59,4 +59,7 @@ public class LoadBeanRequest extends LoadRequest {
return lazyLoadProperty;
}
public int getBatchSize() {
return getLoadContext().getBatchSize();
}
}
@@ -11,16 +11,16 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
*/
public interface LoadContext {
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
/**
* Execute any secondary (+query) queries if there are any defined.
* @param parentRequest the originating query request
*/
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
/**
* Register any secondary queries (+query or +lazy) with their
@@ -13,6 +13,8 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
*/
public interface LoadManyBuffer {
public int getBatchSize();
public List<BeanCollection<?>> getBatch();
public BeanPropertyAssocMany<?> getBeanProperty();
@@ -18,12 +18,11 @@ public class LoadManyRequest extends LoadRequest {
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, int batchSize, boolean lazy,boolean onlyIds, boolean loadCache) {
this(loadContext, null, batchSize, lazy, onlyIds, loadCache);
public LoadManyRequest(LoadManyBuffer loadContext, boolean lazy, boolean onlyIds, boolean loadCache) {
this(loadContext, null, lazy, onlyIds, loadCache);
}
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
boolean onlyIds, boolean loadCache) {
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, boolean lazy, boolean onlyIds, boolean loadCache) {
super(parentRequest, lazy);
this.loadContext = loadContext;
@@ -69,4 +68,10 @@ public class LoadManyRequest extends LoadRequest {
return loadCache;
}
/**
* Return the batch size used for this load context.
*/
public int getBatchSize() {
return loadContext.getBatchSize();
}
}
@@ -13,9 +13,6 @@ public interface LoadSecondaryQuery {
/**
* Execute the secondary query with a given batch size.
*
* @param parentRequest
* the originating query request
*/
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, int requestedBatchSize, boolean all);
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest);
}
@@ -2,10 +2,7 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.TxScope;
import com.avaje.ebean.*;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanLoader;
import com.avaje.ebean.bean.CallStack;
@@ -63,6 +60,11 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
*/
public CallStack createCallStack();
/**
* Return the PersistenceContextScope to use defined at query or server level.
*/
public PersistenceContextScope getPersistenceContextScope(SpiQuery<?> query);
/**
* Return the DDL generator.
*/
@@ -44,9 +44,14 @@ public interface SpiExpressionRequest {
* Return the ordered list of bind values for all expressions in this request.
*/
public ArrayList<Object> getBindValues();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
/**
* Append a DB Like clause.
*/
public void appendLike();
}
@@ -5,6 +5,7 @@ import java.util.List;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
@@ -26,7 +27,7 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
*/
public interface SpiQuery<T> extends Query<T> {
public enum Mode {
public enum Mode {
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
Mode(boolean loadContextBean) {
this.loadContextBean = loadContextBean;
@@ -79,7 +80,20 @@ public interface SpiQuery<T> extends Query<T> {
*/
SUBQUERY
}
/**
* Return the PersistenceContextScope that this query should use.
* <p>
* This can be null and in that case use the default scope.
* </p>
*/
public PersistenceContextScope getPersistenceContextScope();
/**
* Return the default lazy load batch size.
*/
public int getLazyLoadBatchSize();
/**
* Return true if select all properties was used to ensure the property
* invoking a lazy load was included in the query.
@@ -5,4 +5,6 @@ import com.avaje.ebean.SqlUpdate;
public interface SpiSqlUpdate extends SqlUpdate {
public BindParams getBindParams();
public void setGeneratedSql(String sql);
}
@@ -19,179 +19,179 @@ public interface SpiTransaction extends Transaction {
* End the transaction when had query only use.
*/
public void endQueryOnly();
/**
* Return the string prefix with the transactin id and label used in logging.
*/
public String getLogPrefix();
/**
* Return true if generated SQL and Bind values should be logged to the
* transaction log.
*/
public boolean isLogSql();
/**
* Return true if summary level events should be logged to the transaction
* log.
*/
public boolean isLogSummary();
/**
* Return true if generated SQL and Bind values should be logged to the
* transaction log.
*/
public boolean isLogSql();
/**
* Log a message to the SQL logger.
*/
public void logSql(String msg);
/**
* Return true if summary level events should be logged to the transaction
* log.
*/
public boolean isLogSummary();
/**
* Log a message to the SUMMARY logger.
*/
public void logSummary(String msg);
/**
* Log a message to the SQL logger.
*/
public void logSql(String msg);
/**
* Register a "Derived Relationship" (that requires an additional update).
*/
public void registerDerivedRelationship(DerivedRelationshipData assocBean);
/**
* Log a message to the SUMMARY logger.
*/
public void logSummary(String msg);
/**
* Return the list of "Derived Relationships" that must be maintained after
* insert.
*/
public List<DerivedRelationshipData> getDerivedRelationship(Object bean);
/**
* Register a "Derived Relationship" (that requires an additional update).
*/
public void registerDerivedRelationship(DerivedRelationshipData assocBean);
/**
* Add a deleting bean to the registered list.
* <p>
* This is to handle bi-directional relationships where both sides Cascade.
* </p>
*/
public void registerDeleteBean(Integer hash);
/**
* Return the list of "Derived Relationships" that must be maintained after
* insert.
*/
public List<DerivedRelationshipData> getDerivedRelationship(Object bean);
/**
* Unregister the hash of the bean.
*/
public void unregisterDeleteBean(Integer hash);
/**
* Add a deleting bean to the registered list.
* <p>
* This is to handle bi-directional relationships where both sides Cascade.
* </p>
*/
public void registerDeleteBean(Integer hash);
/**
* Return true if this is a bean that has already been saved/deleted.
*/
public boolean isRegisteredDeleteBean(Integer hash);
/**
* Unregister the hash of the bean.
*/
public void unregisterDeleteBean(Integer hash);
/**
* Unregister the persisted bean.
*/
public void unregisterBean(Object bean);
/**
* Return true if this is a bean that has already been saved/deleted.
*/
public boolean isRegisteredDeleteBean(Integer hash);
/**
* Return true if this is a bean that has already been persisted in the
* current recursive save request. The goal is to stop recursively saving
* the bean when cascade persist is on both sides of a relationship).
* <p>
* This will register the bean if it is not already.
* </p>
*/
public boolean isRegisteredBean(Object bean);
/**
* Unregister the persisted bean.
*/
public void unregisterBean(Object bean);
/**
* Returns a String used to identify the transaction. This id is used for
* Transaction logging.
*/
public String getId();
/**
* Return true if this is a bean that has already been persisted in the
* current recursive save request. The goal is to stop recursively saving
* the bean when cascade persist is on both sides of a relationship).
* <p>
* This will register the bean if it is not already.
* </p>
*/
public boolean isRegisteredBean(Object bean);
/**
* Return the batchSize specifically set for this transaction or 0.
* <p>
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
public int getBatchSize();
/**
* Returns a String used to identify the transaction. This id is used for
* Transaction logging.
*/
public String getId();
/**
* Modify and return the current 'depth' of the transaction.
* <p>
* As we cascade save or delete we traverse the object graph tree. Going up
* to Assoc Ones the depth decreases and going down to Assoc Manys the depth
* increases.
* </p>
* <p>
* The depth is used for ordering batching statements. The lowest depth get
* executed first during save.
* </p>
*/
public int depth(int diff);
/**
* Return the batchSize specifically set for this transaction or 0.
* <p>
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
public int getBatchSize();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
*/
public boolean isExplicit();
/**
* Modify and return the current 'depth' of the transaction.
* <p>
* As we cascade save or delete we traverse the object graph tree. Going up
* to Assoc Ones the depth decreases and going down to Assoc Manys the depth
* increases.
* </p>
* <p>
* The depth is used for ordering batching statements. The lowest depth get
* executed first during save.
* </p>
*/
public int depth(int diff);
/**
* Get the object that holds the event details.
* <p>
* This information is used maintain the table state, cache and text
* indexes. On commit the Table modifications this generates is broadcast
* around the cluster (if you have a cluster).
* </p>
*/
public TransactionEvent getEvent();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
*/
public boolean isExplicit();
/**
* Whether persistCascade is on for save and delete.
*/
public boolean isPersistCascade();
/**
* Get the object that holds the event details.
* <p>
* This information is used maintain the table state, cache and text
* indexes. On commit the Table modifications this generates is broadcast
* around the cluster (if you have a cluster).
* </p>
*/
public TransactionEvent getEvent();
/**
* Return true if this request should be batched. Conversely returns false
* if this request should be executed immediately.
*/
public boolean isBatchThisRequest();
/**
* Whether persistCascade is on for save and delete.
*/
public boolean isPersistCascade();
/**
* Return the queue used to batch up persist requests.
*/
public BatchControl getBatchControl();
/**
* Return true if this request should be batched. Conversely returns false
* if this request should be executed immediately.
*/
public boolean isBatchThisRequest();
/**
* Set the queue used to batch up persist requests. There should only be one
* PersistQueue set per transaction.
*/
public void setBatchControl(BatchControl control);
/**
* Return the queue used to batch up persist requests.
*/
public BatchControl getBatchControl();
/**
* Return the persistence context associated with this transaction.
* <p>
* You may wish to hold onto this and set it against another transaction
* later. This is along the lines of 'extended persistence context'
* behaviour.
* </p>
*/
public PersistenceContext getPersistenceContext();
/**
* Set the queue used to batch up persist requests. There should only be one
* PersistQueue set per transaction.
*/
public void setBatchControl(BatchControl control);
/**
* Set the persistence context to this transaction.
* <p>
* This could be considered similar to 'EJB3 Extended Persistence Context'.
* In that you can get the PersistenceContext from a transaction, hold onto
* it, and then set it back later to a second transaction. In general there
* is one PersistenceContext per Transaction. The getPersistenceContext()
* and setPersistenceContext() enable a developer to reuse a single
* PersistenceContext with multiple transactions.
* </p>
*/
public void setPersistenceContext(PersistenceContext context);
/**
* Return the persistence context associated with this transaction.
* <p>
* You may wish to hold onto this and set it against another transaction
* later. This is along the lines of 'extended persistence context'
* behaviour.
* </p>
*/
public PersistenceContext getPersistenceContext();
/**
* Return the underlying Connection for internal use.
* <p>
* If the connection is made public from Transaction and the user code calls
* that method we can no longer trust the query only status of a
* Transaction.
* </p>
*/
public Connection getInternalConnection();
/**
* Set the persistence context to this transaction.
* <p>
* This could be considered similar to 'EJB3 Extended Persistence Context'.
* In that you can get the PersistenceContext from a transaction, hold onto
* it, and then set it back later to a second transaction. In general there
* is one PersistenceContext per Transaction. The getPersistenceContext()
* and setPersistenceContext() enable a developer to reuse a single
* PersistenceContext with multiple transactions.
* </p>
*/
public void setPersistenceContext(PersistenceContext context);
/**
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
*/
/**
* Return the underlying Connection for internal use.
* <p>
* If the connection is made public from Transaction and the user code calls
* that method we can no longer trust the query only status of a
* Transaction.
* </p>
*/
public Connection getInternalConnection();
/**
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
*/
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName);
}
@@ -1,7 +1,6 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import com.avaje.ebean.annotation.CacheTuning;
@@ -89,9 +88,7 @@ public class DefaultCacheHolder {
}
public void clearAll() {
Iterator<ServerCache> it = concMap.values().iterator();
while (it.hasNext()) {
ServerCache serverCache = it.next();
for (ServerCache serverCache : concMap.values()) {
serverCache.clear();
}
}
@@ -111,9 +111,7 @@ public class DefaultServerCache implements ServerCache {
int hc = reset ? removedHitCount.getAndSet(0) : removedHitCount.get();
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
for (CacheEntry cacheEntry : map.values()) {
hc += cacheEntry.getHitCount(reset);
}
@@ -268,10 +266,6 @@ public class DefaultServerCache implements ServerCache {
return map.size();
}
private Iterator<CacheEntry> cacheEntries() {
return map.values().iterator();
}
/**
* The task used to periodically trim the cache.
*/
@@ -296,7 +290,7 @@ public class DefaultServerCache implements ServerCache {
long idleExpire = System.currentTimeMillis() - (maxIdleSecs*1000);
long ttlExpire = System.currentTimeMillis() - (maxSecsToLive*1000);
Iterator<CacheEntry> it = cacheEntries();
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
if (maxIdleSecs > 0 && idleExpire > cacheEntry.getLastAccessTime()) {
@@ -40,10 +40,13 @@ public class DefaultServerCacheManager implements ServerCacheManager {
cacheFactory.init(server);
this.ebeanServer = (SpiEbeanServer)server;
}
public void setCaching(Class<?> beanType, boolean useCache) {
ebeanServer.getBeanDescriptor(beanType).getCacheOptions().setUseCache(useCache);
}
/**
* Set bean caching on or off for a given bean type.
*/
public void setCaching(Class<?> beanType, boolean useCache) {
ebeanServer.getBeanDescriptor(beanType).setUseCache(useCache);
}
/**
* Clear both the bean cache and the query cache for a
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.core;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.persistence.Embeddable;
@@ -11,6 +10,7 @@ import javax.persistence.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
@@ -56,7 +56,7 @@ public class BootupClasses implements ClassPathSearchMatcher {
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
private List<BeanPersistController> persistControllerInstances = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<?>> persistListenerInstances = new ArrayList<BeanPersistListener<?>>();
private List<BeanPersistListener> persistListenerInstances = new ArrayList<BeanPersistListener>();
private List<BeanQueryAdapter> queryAdapterInstances = new ArrayList<BeanQueryAdapter>();
private List<TransactionEventListener> transactionEventListenerInstances = new ArrayList<TransactionEventListener>();
@@ -65,7 +65,9 @@ public class BootupClasses implements ClassPathSearchMatcher {
public BootupClasses(List<Class<?>> list) {
if (list != null) {
process(list.iterator());
for (Class<?> cls : list) {
isMatch(cls);
}
}
}
@@ -83,13 +85,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
}
private void process(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> cls = it.next();
isMatch(cls);
}
}
/**
* Create a copy of this object so that classes can be added to it.
*/
@@ -150,9 +145,9 @@ public class BootupClasses implements ClassPathSearchMatcher {
}
}
public void addPersistListeners(List<BeanPersistListener<?>> listenerInstances) {
public void addPersistListeners(List<BeanPersistListener> listenerInstances) {
if (listenerInstances != null) {
for (BeanPersistListener<?> l : listenerInstances) {
for (BeanPersistListener l : listenerInstances) {
this.persistListenerInstances.add(l);
// don't automatically instantiate
this.beanListenerList.remove(l.getClass());
@@ -186,12 +181,12 @@ public class BootupClasses implements ClassPathSearchMatcher {
return queryAdapterInstances;
}
public List<BeanPersistListener<?>> getBeanPersistListeners() {
public List<BeanPersistListener> getBeanPersistListeners() {
// add class registered BeanPersistController to the
// already created instances
for (Class<?> cls : beanListenerList) {
try {
BeanPersistListener<?> newInstance = (BeanPersistListener<?>) cls.newInstance();
BeanPersistListener newInstance = (BeanPersistListener) cls.newInstance();
persistListenerInstances.add(newInstance);
} catch (Exception e) {
String msg = "Error creating BeanPersistController " + cls;
@@ -297,13 +292,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
return beanListenerList;
}
public void add(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> clazz = it.next();
isMatch(clazz);
}
}
public boolean isMatch(Class<?> cls) {
if (isEmbeddable(cls)) {
@@ -8,8 +8,6 @@ import com.avaje.ebeaninternal.server.lib.DaemonThreadPool;
/**
* The default implementation of the BackgroundExecutor.
*
* @author rbygrave
*/
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
@@ -20,7 +18,7 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
/**
* Construct the default implementation of BackgroundExecutor.
*
* @param mainPoolSize
* @param corePoolSize
* the core size of the thread pool.
* @param keepAliveSecs
* the time in seconds idle threads are keep alive
@@ -24,6 +24,7 @@ import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
@@ -39,7 +40,7 @@ public class DefaultBeanLoader {
this.server = server;
}
/**
/**
* Return a batch size that might be less than the requestedBatchSize.
* <p>
* This means we can have large and variable requestedBatchSizes.
@@ -50,7 +51,7 @@ public class DefaultBeanLoader {
* </p>
*/
private int getBatchSize(int batchSize) {
if (batchSize == 1) {
// there is only one bean/collection to load
return 1;
@@ -73,10 +74,10 @@ public class DefaultBeanLoader {
}
return batchSize;
}
public void refreshMany(EntityBean parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
public void refreshMany(EntityBean parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
public void loadMany(LoadManyRequest loadRequest) {
@@ -112,6 +113,15 @@ public class DefaultBeanLoader {
if (orderBy != null) {
query.orderBy(orderBy);
}
String extraWhere = many.getExtraWhere();
if (extraWhere != null) {
// replace special ${ta} placeholder with the base table alias
// which is always t0 and add the extra where clause
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
query.where().raw(ew);
}
query.setLazyLoadForParents(idList, many);
many.addWhereParentIdIn(query, idList);
@@ -120,6 +130,11 @@ public class DefaultBeanLoader {
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, loadRequest.getDescription());
if (loadRequest.isLazy()) {
// cascade the batch size (if set) for further lazy loading
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
}
// potentially changes the joins and selected properties
ctx.configureQuery(query);
@@ -143,7 +158,7 @@ public class DefaultBeanLoader {
desc.cacheManyPropPut(many, bc, parentId);
}
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
@@ -153,18 +168,17 @@ public class DefaultBeanLoader {
EntityBean parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
//ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
loadManyInternal(parentBean, propertyName, null, false, null, onlyIds);
}
public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, null, false);
}
public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, null, false);
}
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh,
ObjectGraphNode node, boolean onlyIds) {
EntityBeanIntercept ebi = ((EntityBean) parentBean)._ebean_getIntercept();
EntityBeanIntercept ebi = parentBean._ebean_getIntercept();
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
@@ -172,7 +186,7 @@ public class DefaultBeanLoader {
BeanCollection<?> beanCollection = null;
ExpressionList<?> filterMany = null;
Object currentValue = many.getValue(parentBean);
if (currentValue instanceof BeanCollection<?>) {
beanCollection = (BeanCollection<?>) currentValue;
@@ -189,7 +203,7 @@ public class DefaultBeanLoader {
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi != null && ebi.isReadOnly()) {
if (ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) {
@@ -201,7 +215,7 @@ public class DefaultBeanLoader {
if (refresh) {
// populate a new collection
Object emptyCollection = many.createEmpty(false);
BeanCollection<?> emptyCollection = many.createEmpty(parentBean);
many.setValue(parentBean, emptyCollection);
query.setLoadDescription("+refresh", null);
} else {
@@ -231,10 +245,8 @@ public class DefaultBeanLoader {
query.setLazyLoadManyPath(many.getName());
query.setPersistenceContext(pc);
if (ebi != null) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
server.findUnique(query, t);
@@ -305,6 +317,11 @@ public class DefaultBeanLoader {
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, loadRequest.getDescription());
if (loadRequest.isLazy()) {
// cascade the batch size (if set) for further lazy loading
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
}
ctx.configureQuery(query, loadRequest.getLazyLoadProperty());
// make sure the query doesn't use the cache
@@ -319,7 +336,7 @@ public class DefaultBeanLoader {
if (loadRequest.isLoadCache()) {
for (int i = 0; i < list.size(); i++) {
desc.cacheBeanPutData((EntityBean)list.get(i));
desc.cacheBeanPutData((EntityBean) list.get(i));
}
}
@@ -328,48 +345,50 @@ public class DefaultBeanLoader {
// necessary but allow processing to continue until it is accessed by client code
ebis[i].checkLazyLoadFailure();
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
public void refresh(EntityBean bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
}
public void refresh(EntityBean bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
}
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) {
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
EntityBeanIntercept ebi = bean._ebean_getIntercept();
PersistenceContext pc = ebi.getPersistenceContext();
if (Mode.REFRESH_BEAN == mode) {
// need a new PersistenceContext for REFRESH
pc = null;
}
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
if (EntityType.EMBEDDED == desc.getEntityType()) {
// lazy loading on an embedded bean property
EntityBean embeddedOwner = (EntityBean)ebi.getEmbeddedOwner();
EntityBean embeddedOwner = (EntityBean) ebi.getEmbeddedOwner();
int ownerIndex = ebi.getEmbeddedOwnerIndex();
refreshBeanInternal(embeddedOwner, mode, ownerIndex);
}
Object id = desc.getId(bean);
if (pc == null) {
// a reference with no existing persistenceContext
pc = new DefaultPersistenceContext();
pc.put(id, bean);
if (ebi != null) {
ebi.setPersistenceContext(pc);
}
ebi.setPersistenceContext(pc);
}
if (embeddedOwnerIndex == -1) {
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
// lazy loading and the bean cache is active
if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) {
if (desc.cacheBeanLoad(bean, ebi, id)) {
return;
}
}
@@ -379,15 +398,13 @@ public class DefaultBeanLoader {
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
if (ebi != null) {
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
if (embeddedOwnerIndex > -1) {
String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex);
query.select("id,"+embeddedBeanPropertyName);
query.select("id," + embeddedBeanPropertyName);
}
// don't collect autoFetch usage profiling information
// as we just copy the data out of these fetched beans
// and put the data into the original bean
@@ -406,11 +423,20 @@ public class DefaultBeanLoader {
query.setReadOnly(true);
}
if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) {
// explicitly state to load all properties on REFRESH.
// Lobs default to fetch lazy so this forces lobs to be
// included in a 'refresh' query
query.select("*");
}
Object dbBean = query.findUnique();
if (dbBean == null) {
String msg = "Bean not found during lazy load or refresh." + " id[" + id + "] type[" + desc.getBeanType() + "]";
throw new EntityNotFoundException(msg);
}
desc.resetManyProperties(dbBean);
}
}
@@ -17,36 +17,10 @@ import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.PersistenceException;
import com.avaje.ebean.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.AdminAutofetch;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.BeanState;
import com.avaje.ebean.CallableSql;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.Filter;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.SqlFutureList;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.TxCallable;
import com.avaje.ebean.TxIsolation;
import com.avaje.ebean.TxRunnable;
import com.avaje.ebean.TxScope;
import com.avaje.ebean.TxType;
import com.avaje.ebean.Update;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
@@ -65,7 +39,6 @@ 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;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
import com.avaje.ebeaninternal.api.LoadManyRequest;
import com.avaje.ebeaninternal.api.ScopeTrans;
@@ -164,7 +137,6 @@ public final class DefaultServer implements SpiEbeanServer {
private final CQueryEngine cqueryEngine;
//@Deprecated
private DdlGenerator ddlGenerator;
private final ExpressionFactory expressionFactory;
@@ -184,6 +156,11 @@ public final class DefaultServer implements SpiEbeanServer {
*/
private String mbeanName;
/**
* The default PersistenceContextScope used if it is not explicitly set on a query.
*/
private final PersistenceContextScope defaultPersistenceContextScope;
/**
* The MBeanServer Ebean is registered with.
*/
@@ -242,6 +219,7 @@ public final class DefaultServer implements SpiEbeanServer {
this.cqueryEngine = config.getCQueryEngine();
this.expressionFactory = config.getExpressionFactory();
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
this.defaultPersistenceContextScope = serverConfig.getPersistenceContextScope();
this.beanDescriptorManager = config.getBeanDescriptorManager();
beanDescriptorManager.setEbeanServer(this);
@@ -274,17 +252,21 @@ public final class DefaultServer implements SpiEbeanServer {
List<SpiEbeanPlugin> spiPlugins = new ArrayList<SpiEbeanPlugin>();
final Iterator<SpiEbeanPlugin> plugins = ServiceLoader.load(SpiEbeanPlugin.class).iterator();
while (plugins.hasNext()) {
SpiEbeanPlugin plugin = plugins.next();
for (SpiEbeanPlugin plugin : ServiceLoader.load(SpiEbeanPlugin.class)) {
spiPlugins.add(plugin);
plugin.setup(this, this.getDatabasePlatform(), config.getServerConfig());
if (plugin instanceof DdlGenerator) {
// backwards compatible
ddlGenerator = (DdlGenerator)plugin;
}
ddlGenerator = (DdlGenerator) plugin;
}
}
if (ddlGenerator == null) {
// ServiceLoader not finding ddlGenerator (typically OSGi)
ddlGenerator = new DdlGenerator();
spiPlugins.add(ddlGenerator);
ddlGenerator.setup(this, this.getDatabasePlatform(), config.getServerConfig());
}
ebeanPlugins = Collections.unmodifiableList(spiPlugins);
@@ -671,6 +653,15 @@ public final class DefaultServer implements SpiEbeanServer {
return (T) ref;
}
@Override
public void register(TransactionCallback transactionCallback) {
Transaction transaction = currentTransaction();
if (transaction == null) {
throw new PersistenceException("Not currently active transaction when trying to register transactionCallback");
}
transaction.register(transactionCallback);
}
/**
* Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use
* this when you want a thread to have a second independent transaction.
@@ -1154,8 +1145,8 @@ public final class DefaultServer implements SpiEbeanServer {
t = getCurrentServerTransaction();
}
PersistenceContext context = null;
if (t != null) {
// first look in the persistence context
if (t != null && useTransactionPersistenceContext(query)) {
// first look in the transaction scoped persistence context
context = t.getPersistenceContext();
if (context != null) {
WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId());
@@ -1178,7 +1169,22 @@ public final class DefaultServer implements SpiEbeanServer {
// Hit the L2 bean cache
return beanDescriptor.cacheBeanGet(query, context);
}
/**
* Return true if transactions PersistenceContext should be used.
*/
private <T> boolean useTransactionPersistenceContext(SpiQuery<T> query) {
return PersistenceContextScope.TRANSACTION.equals(getPersistenceContextScope(query));
}
/**
* Return the PersistenceContextScope to use defined at query or server level.
*/
public PersistenceContextScope getPersistenceContextScope(SpiQuery<?> query) {
PersistenceContextScope scope = query.getPersistenceContextScope();
return (scope != null) ? scope : defaultPersistenceContextScope;
}
@SuppressWarnings("unchecked")
private <T> T findId(Query<T> query, Transaction t) {
@@ -1407,12 +1413,27 @@ public final class DefaultServer implements SpiEbeanServer {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
try {
request.initTransIfRequired();
request.findVisit(visitor);
} finally {
// do nothing - findVisit garuntee's cleanup of the transaction if required
}
request.initTransIfRequired();
request.findVisit(visitor);
// no try finally - findVisit guarantee's cleanup of the transaction if required
}
public <T> void findEach(Query<T> query, QueryEachConsumer<T> consumer, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
request.initTransIfRequired();
request.findEach(consumer);
// no try finally - findVisit guarantee's cleanup of the transaction if required
}
public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
request.initTransIfRequired();
request.findEachWhile(consumer);
// no try finally - findVisit guarantee's cleanup of the transaction if required
}
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
@@ -1536,7 +1557,7 @@ public final class DefaultServer implements SpiEbeanServer {
@Override
public void markAsDirty(Object bean) {
if (bean instanceof EntityBean == false) {
if (!(bean instanceof EntityBean)) {
throw new IllegalArgumentException("This bean is not an EntityBean?");
}
// mark the bean as dirty (so that an update will not get skipped)
@@ -1644,7 +1665,7 @@ public final class DefaultServer implements SpiEbeanServer {
if (bean == null) {
throw new IllegalArgumentException(Message.msg("bean.isnull"));
}
if (bean instanceof EntityBean == false) {
if (!(bean instanceof EntityBean)) {
throw new IllegalArgumentException("Was expecting an EntityBean but got a "+bean.getClass());
}
return (EntityBean)bean;
@@ -1953,14 +1974,7 @@ public final class DefaultServer implements SpiEbeanServer {
public boolean isSupportedType(java.lang.reflect.Type genericType) {
TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType);
if (typeInfo == null) {
return false;
}
Class<?> beanType = typeInfo.getBeanType();
if (JsonElement.class.isAssignableFrom(beanType)) {
return true;
}
return getBeanDescriptor(typeInfo.getBeanType()) != null;
return typeInfo != null && getBeanDescriptor(typeInfo.getBeanType()) != null;
}
public Object getBeanId(Object bean) {
@@ -2079,11 +2093,18 @@ public final class DefaultServer implements SpiEbeanServer {
return new CallStack(finalTrace);
}
public JsonContext createJsonContext() {
@Override
public JsonContext json() {
// immutable thread safe so return shared instance
return jsonContext;
}
@Override
public JsonContext createJsonContext() {
return json();
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.core;
import java.sql.Connection;
@@ -93,6 +93,11 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
*/
private final String sql;
/**
* The actual sql with named parameters converted.
*/
private String generatedSql;
/**
* Some descriptive text that can be put into the transaction log.
*/
@@ -170,7 +175,16 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
return this;
}
public String getSql() {
public String getGeneratedSql() {
return generatedSql;
}
@Override
public void setGeneratedSql(String generatedSql) {
this.generatedSql = generatedSql;
}
public String getSql() {
return sql;
}
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -9,8 +11,6 @@ import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonValueAdapter;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
@@ -22,6 +22,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -30,7 +31,7 @@ import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
@@ -38,6 +39,7 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
import com.fasterxml.jackson.core.JsonFactory;
/**
* Used to extend the ServerConfig with additional objects used to configure and
@@ -87,10 +89,13 @@ public class InternalConfiguration {
private final XmlConfig xmlConfig;
private final JsonFactory jsonFactory;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
this.jsonFactory = serverConfig.getJsonFactory();
this.xmlConfig = xmlConfig;
this.pstmtBatch = pstmtBatch;
this.clusterManager = clusterManager;
@@ -113,8 +118,7 @@ public class InternalConfiguration {
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
serverConfig, beanDescriptorManager, this.getBootupClasses());
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
@@ -131,19 +135,38 @@ public class InternalConfiguration {
}
}
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
private TransactionManager createTransactionManager() {
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
// We know the DataSourcePool is using autoCommit
return true;
}
return false;
}
public JsonContext createJsonContext(SpiEbeanServer server) {
String s = serverConfig.getProperty("json.pretty", "false");
boolean dfltPretty = "true".equalsIgnoreCase(s);
s = serverConfig.getProperty("json.jsonValueAdapter", null);
JsonValueAdapter va = new DefaultJsonValueAdapter();
if (s != null) {
va = (JsonValueAdapter) ClassUtil.newInstance(s, this.getClass());
}
return new DJsonContext(server, va, dfltPretty);
return new DJsonContext(server, jsonFactory);
}
public XmlConfig getXmlConfig() {
@@ -0,0 +1,266 @@
package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
<<<<<<< HEAD
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
=======
>>>>>>> json-refactor
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
/**
* Used to extend the ServerConfig with additional objects used to configure and
* construct an EbeanServer.
*/
public class InternalConfiguration {
private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class);
private final ServerConfig serverConfig;
private final BootupClasses bootupClasses;
private final DeployInherit deployInherit;
private final ResourceManager resourceManager;
private final DeployOrmXml deployOrmXml;
private final TypeManager typeManager;
private final Binder binder;
private final DeployCreateProperties deployCreateProperties;
private final DeployUtil deployUtil;
private final BeanDescriptorManager beanDescriptorManager;
private final TransactionManager transactionManager;
private final TransactionScopeManager transactionScopeManager;
private final CQueryEngine cQueryEngine;
private final ClusterManager clusterManager;
private final ServerCacheManager cacheManager;
private final ExpressionFactory expressionFactory;
private final SpiBackgroundExecutor backgroundExecutor;
private final PstmtBatch pstmtBatch;
private final XmlConfig xmlConfig;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
this.xmlConfig = xmlConfig;
this.pstmtBatch = pstmtBatch;
this.clusterManager = clusterManager;
this.backgroundExecutor = backgroundExecutor;
this.cacheManager = cacheManager;
this.serverConfig = serverConfig;
this.bootupClasses = bootupClasses;
this.expressionFactory = new DefaultExpressionFactory();
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
this.binder = new Binder(typeManager);
this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig);
this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource());
this.deployInherit = new DeployInherit(bootupClasses);
this.deployCreateProperties = new DeployCreateProperties(typeManager);
this.deployUtil = new DeployUtil(typeManager, serverConfig);
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
externalTransactionManager = new JtaTransactionManager();
}
if (externalTransactionManager != null) {
externalTransactionManager.setTransactionManager(transactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
} else {
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
}
}
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
private TransactionManager createTransactionManager() {
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
// We know the DataSourcePool is using autoCommit
return true;
}
return false;
}
public JsonContext createJsonContext(SpiEbeanServer server) {
return new DJsonContext(server);
}
public XmlConfig getXmlConfig() {
return xmlConfig;
}
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
}
public RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue());
}
public OrmQueryEngine createOrmQueryEngine() {
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
}
public Persister createPersister(SpiEbeanServer server) {
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
}
public PstmtBatch getPstmtBatch() {
return pstmtBatch;
}
public ServerCacheManager getCacheManager() {
return cacheManager;
}
public BootupClasses getBootupClasses() {
return bootupClasses;
}
public DatabasePlatform getDatabasePlatform() {
return serverConfig.getDatabasePlatform();
}
public ServerConfig getServerConfig() {
return serverConfig;
}
public ExpressionFactory getExpressionFactory() {
return expressionFactory;
}
public TypeManager getTypeManager() {
return typeManager;
}
public Binder getBinder() {
return binder;
}
public BeanDescriptorManager getBeanDescriptorManager() {
return beanDescriptorManager;
}
public DeployInherit getDeployInherit() {
return deployInherit;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
public DeployOrmXml getDeployOrmXml() {
return deployOrmXml;
}
public DeployCreateProperties getDeployCreateProperties() {
return deployCreateProperties;
}
public DeployUtil getDeployUtil() {
return deployUtil;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public TransactionScopeManager getTransactionScopeManager() {
return transactionScopeManager;
}
public CQueryEngine getCQueryEngine() {
return cQueryEngine;
}
public ClusterManager getClusterManager() {
return clusterManager;
}
public SpiBackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
}
@@ -6,9 +6,7 @@ import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.*;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanFinder;
@@ -55,7 +53,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private HashQuery cacheKey;
private HashQueryPlan queryPlanHash;
/**
* Create the InternalQueryRequest.
*/
@@ -71,8 +69,17 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
this.readOnly = query.isReadOnly();
}
public void executeSecondaryQueries(int defaultQueryBatch) {
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
/**
* Return the database platform like clause.
*/
@Override
public String getDBLikeClause() {
return ebeanServer.getDatabasePlatform().getLikeClause();
}
public void executeSecondaryQueries() {
loadContext.executeSecondaryQueries(this);
}
/**
@@ -176,18 +183,21 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
persistenceContext = new DefaultPersistenceContext();
loadContext.setPersistenceContext(persistenceContext);
}
/**
* Get the TransactionContext either explicitly set on the query or
* transaction scoped.
*/
private PersistenceContext getPersistenceContext(SpiQuery<?> query, SpiTransaction t) {
// check if there is already a persistence context set which is the case
// when lazy loading or query joins are executed
PersistenceContext ctx = query.getPersistenceContext();
if (ctx == null) {
ctx = t.getPersistenceContext();
}
return ctx;
if (ctx != null) return ctx;
// determine the scope (from the query and then server)
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
}
/**
@@ -225,6 +235,30 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return idList.getIdList();
}
public void findEach(QueryEachConsumer<T> consumer) {
QueryIterator<T> it = queryEngine.findIterate(this);
try {
while (it.hasNext()) {
consumer.accept(it.next());
}
} finally {
it.close();
}
}
public void findEachWhile(QueryEachWhileConsumer<T> consumer) {
QueryIterator<T> it = queryEngine.findIterate(this);
try {
while (it.hasNext()) {
if (!consumer.accept(it.next())) {
break;
}
}
} finally {
it.close();
}
}
public void findVisit(QueryResultVisitor<T> visitor) {
QueryIterator<T> it = queryEngine.findIterate(this);
try {
@@ -370,4 +404,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return query.isLogSecondaryQuery();
}
/**
* Return the batch size for lazy loading on this bean query request.
*/
public int getLazyLoadBatchSize() {
int batchSize = query.getLazyLoadBatchSize();
return (batchSize > 0) ? batchSize : ebeanServer.getLazyLoadBatchSize();
}
}
@@ -38,7 +38,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private final BeanDescriptor<T> beanDescriptor;
private final BeanPersistListener<T> beanPersistListener;
private final BeanPersistListener beanPersistListener;
/**
* For per post insert update delete control.
@@ -91,8 +91,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private List<BeanPropertyAssocMany<?>> updatedManys;
/**
* Need to get and store the updated properties because the persist listener is notified
* later on a different thread and the bean has been reset at that point.
*/
private Set<String> updatedProperties;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type) {
PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) {
super(server, t, persistExecute);
this.entityBean = (EntityBean) bean;
@@ -103,12 +109,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
if (PersistRequest.Type.DETERMINE != type) {
this.type = type;
} else {
// determine mode during cascade save (supporting stateless update)
this.type = beanDescriptor.isInsertMode(intercept) ? Type.INSERT : Type.UPDATE;
this.type = type;
if (saveRecurse) {
this.persistCascade = t.isPersistCascade();
}
@@ -191,7 +194,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return beanPersistListener.inserted(bean);
case UPDATE:
return beanPersistListener.updated(bean, intercept.getDirtyPropertyNames());
return beanPersistListener.updated(bean, updatedProperties);
case DELETE:
return beanPersistListener.deleted(bean);
@@ -375,6 +378,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return -1;
case UPDATE:
if (beanPersistListener != null) {
// store the updated properties for sending later
updatedProperties = getUpdatedProperties();
}
persistExecute.executeUpdateBean(this);
return -1;
@@ -4,6 +4,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.QueryEachConsumer;
import com.avaje.ebean.QueryEachWhileConsumer;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.bean.BeanCollection;
@@ -65,7 +67,17 @@ public interface SpiOrmQueryRequest<T> {
*/
public void findVisit(QueryResultVisitor<T> visitor);
/**
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findEach(QueryEachConsumer<T> consumer);
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Execute the find returning a QueryIterator.
*/
public QueryIterator<T> findIterate();
@@ -95,4 +107,9 @@ public interface SpiOrmQueryRequest<T> {
*/
public BeanCollection<T> getFromQueryCache();
/**
* Return the Database platform like clause.
*/
public String getDBLikeClause();
}
@@ -0,0 +1,87 @@
package com.avaje.ebeaninternal.server.ddl;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
/**
* A visitor that creates indexes for columns annotated with ColumnIndex
*
* @author rvbiljouw
* @see com.avaje.ebean.annotation.Index
*/
public class CreateIndexVisitor extends AbstractBeanVisitor {
private final IndexPropertyVisitor iv;
public CreateIndexVisitor(DdlGenContext ctx) {
this.iv = new IndexPropertyVisitor(ctx);
}
@Override
public void visitBegin() {
}
@Override
public boolean visitBean(BeanDescriptor<?> descriptor) {
return descriptor.isInheritanceRoot();
}
@Override
public PropertyVisitor visitProperty(BeanProperty p) {
return iv;
}
@Override
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
visitInheritanceProperties(descriptor, iv);
}
@Override
public void visitEnd() {
}
private static final class IndexPropertyVisitor extends BaseTablePropertyVisitor {
private final DdlGenContext ctx;
public IndexPropertyVisitor(DdlGenContext ctx) {
this.ctx = ctx;
}
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
}
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
}
@Override
public void visitScalar(BeanProperty p) {
String baseTable = p.getBeanDescriptor().getBaseTable();
if (p.isIndexed()) {
String indexName = p.getIndexName();
if (indexName.length() == 0) {
indexName = ctx.getDdlSyntax().getIndexName(baseTable, p.getDbColumn(), ctx.incrementIxCount());
}
ctx.write("create index ")
.write(indexName)
.write(" on ")
.write(baseTable)
.write("(")
.write(p.getDbColumn())
.write(");")
.writeNewLine();
}
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
}
}
}
@@ -98,17 +98,17 @@ public class DdlGenerator implements SpiEbeanPlugin {
}
}
protected void writeDrop(String dropFile) {
protected void writeDrop(String dropFile) {
try {
String c = generateDropDdl();
writeFile(dropFile, c);
try {
String c = generateDropDdl();
writeFile(dropFile, c);
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
}
}
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
}
}
protected void writeCreate(String createFile) {
@@ -149,6 +149,9 @@ public class DdlGenerator implements SpiEbeanPlugin {
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
VisitorUtil.visit(server, fkeys);
CreateIndexVisitor indexes = new CreateIndexVisitor(ctx);
VisitorUtil.visit(server, indexes);
ctx.flush();
createContent = ctx.getContent();
return createContent;
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.Iterator;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
@@ -9,7 +7,9 @@ import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import java.io.IOException;
/**
* Helper functions for performing tasks on Lists Sets or Maps.
@@ -29,15 +29,15 @@ public interface BeanCollectionHelp<T> {
*/
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey);
/**
* Create an empty collection of the correct type without a parent bean.
*/
public BeanCollection<T> createEmptyNoParent();
/**
* Create an empty collection of the correct type.
*/
public Object createEmpty(boolean vanilla);
/**
* Create an iterator for reading the entries.
*/
public Iterator<?> getIterator(Object collection);
public BeanCollection<T> createEmpty(EntityBean bean);
/**
* Add a bean to the List Set or Map.
@@ -47,7 +47,7 @@ public interface BeanCollectionHelp<T> {
/**
* Create a lazy loading proxy for a List Set or Map.
*/
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName);
public BeanCollection<T> createReference(EntityBean parentBean);
/**
* Refresh the List Set or Map.
@@ -59,9 +59,9 @@ public interface BeanCollectionHelp<T> {
*/
public void refresh(BeanCollection<?> bc, EntityBean parentBean);
/**
* Write the collection out as json.
*/
public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude);
/**
* Write the collection out as json.
*/
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException;
}
@@ -9,6 +9,10 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
*/
public class BeanCollectionHelpFactory {
static final BeanListHelp LIST_HELP = new BeanListHelp();
static final BeanSetHelp SET_HELP = new BeanSetHelp();
/**
* Create the helper based on the many property.
*/
@@ -33,10 +37,10 @@ public class BeanCollectionHelpFactory {
SpiQuery.Type manyType = request.getQuery().getType();
if (manyType.equals(SpiQuery.Type.LIST)){
return new BeanListHelp<T>();
return LIST_HELP;
} else if (manyType.equals(SpiQuery.Type.SET)) {
return new BeanSetHelp<T>();
return SET_HELP;
} else {
BeanDescriptor<T> target = request.getBeanDescriptor();
@@ -1,11 +1,12 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -34,8 +35,6 @@ 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;
@@ -62,15 +61,13 @@ 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.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.DataBind;
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 com.fasterxml.jackson.core.JsonParser;
/**
* Describes Beans including their deployment information.
@@ -137,11 +134,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
private final CompoundUniqueContraint[] compoundUniqueConstraints;
/**
* Extra deployment attributes.
*/
private final Map<String, String> extraAttrMap;
/**
* The base database table.
*/
@@ -178,7 +170,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Listens for post commit insert update and delete events.
*/
private volatile BeanPersistListener<T> persistListener;
private volatile BeanPersistListener persistListener;
private volatile BeanQueryAdapter queryAdapter;
@@ -195,12 +187,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Inheritance information. Server side only.
*/
private final InheritInfo inheritInfo;
protected final InheritInfo inheritInfo;
/**
* Derived list of properties that make up the unique id.
*/
private final BeanProperty idProperty;
protected final BeanProperty idProperty;
private final int idPropertyIndex;
/**
@@ -228,17 +220,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
private final BeanPropertyAssocOne<?> unidirectional;
/**
* A hashcode of all the many property names. This is used to efficiently
* create sets of loaded property names (for partial objects).
*/
private final int namesOfManyPropsHash;
/**
* The set of names of the many properties.
*/
private final Set<String> namesOfManyProps;
/**
* list of properties that are Lists/Sets/Maps (Derived).
*/
@@ -327,7 +308,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
private final boolean cacheSharableBeans;
private final BeanDescriptorCacheHelp<T> cacheHelp;
private final BeanDescriptorJsonHelp<T> jsonHelp;
private final String defaultSelectClause;
private final Set<String> defaultSelectClauseSet;
@@ -378,8 +360,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.dependantTables = deploy.getDependantTables();
this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints();
this.extraAttrMap = deploy.getExtraAttributeMap();
this.baseTable = InternString.intern(deploy.getBaseTable());
this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
@@ -413,16 +393,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.propertiesManyDelete = listHelper.getManyDelete();
this.propertiesManyToMany = listHelper.getManyToMany();
this.namesOfManyProps = deriveManyPropNames();
this.namesOfManyPropsHash = namesOfManyProps.hashCode();
this.derivedTableJoins = listHelper.getTableJoin();
boolean noRelationships = propertiesOne.length + propertiesMany.length == 0;
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
this.cacheHelp = new BeanDescriptorCacheHelp<T>(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
this.jsonHelp = new BeanDescriptorJsonHelp<T>(this);
// Check if there are no cascade save associated beans ( subject to change
// in initialiseOther()). Note that if we are in an inheritance hierarchy
@@ -520,48 +497,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
}
/**
* Determine the concurrency mode based on the existence of a non-null version
* property value.
*/
public ConcurrencyMode determineConcurrencyMode(EntityBean bean) {
if (versionProperty == null) {
return ConcurrencyMode.NONE;
}
Object v = versionProperty.getValue(bean);
return (v == null) ? ConcurrencyMode.NONE : ConcurrencyMode.VERSION;
}
/**
* Return the Set of embedded beans that have changed.
*/
public Set<String> getDirtyEmbeddedProperties(EntityBean bean) {
HashSet<String> dirtyProperties = null;
for (int i = 0; i < propertiesEmbedded.length; i++) {
Object embValue = propertiesEmbedded[i].getValue(bean);
if (embValue instanceof EntityBean) {
if (((EntityBean) embValue)._ebean_getIntercept().isDirty()) {
// this embedded is dirty so should be included in an update
if (dirtyProperties == null) {
dirtyProperties = new HashSet<String>();
}
dirtyProperties.add(propertiesEmbedded[i].getName());
}
} else {
// must assume it is dirty
if (dirtyProperties == null) {
dirtyProperties = new HashSet<String>();
}
dirtyProperties.add(propertiesEmbedded[i].getName());
}
}
return dirtyProperties;
}
/**
* Return the EbeanServer instance that owns this BeanDescriptor.
*/
@@ -603,9 +538,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (isEmbedded()) {
// initialise all the properties
Iterator<BeanProperty> it = propertiesAll();
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertiesAll()) {
prop.initialise();
}
} else {
@@ -623,9 +556,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (!isEmbedded()) {
// initialise all the non-id properties
Iterator<BeanProperty> it = propertiesAll();
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise();
}
@@ -673,10 +604,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
cacheHelp.initialise();
}
protected boolean hasInheritance() {
return inheritInfo != null;
}
public SqlUpdate deleteById(Object id, List<Object> idList) {
if (id != null) {
return deleteById(id);
@@ -798,6 +725,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return inheritInfo == null || inheritInfo.isRoot();
}
/**
* Set the bean caching on or off.
*/
public void setUseCache(boolean useCache) {
cacheHelp.setUseCache(useCache);
}
/**
* Return true if there is currently query caching for this type of bean.
*/
@@ -844,8 +778,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
cacheHelp.queryCachePut(id, query);
}
/**
* Try to load the beanCollection from cache return true if successful.
*/
@@ -1028,9 +960,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* Reset the statistics on all the query plans.
*/
public void clearQueryStatistics() {
Iterator<CQueryPlan> it = queryPlanCache.values().iterator();
while (it.hasNext()) {
CQueryPlan queryPlan = (CQueryPlan) it.next();
for (CQueryPlan queryPlan : queryPlanCache.values()) {
queryPlan.resetStatistics();
}
}
@@ -1038,21 +968,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Execute the postLoad if a BeanPersistController exists for this bean.
*/
@SuppressWarnings("unchecked")
public void postLoad(Object bean, Set<String> includedProperties) {
BeanPersistController c = persistController;
if (c != null) {
c.postLoad((T) bean, includedProperties);
c.postLoad(bean, includedProperties);
}
}
/**
* Return the query plans for this BeanDescriptor.
*/
public Iterator<CQueryPlan> queryPlans() {
return queryPlanCache.values().iterator();
}
public CQueryPlan getQueryPlan(HashQueryPlan key) {
return queryPlanCache.get(key);
}
@@ -1075,13 +997,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
updatePlanCache.put(key, plan);
}
/**
* Return the TypeManager.
*/
public TypeManager getTypeManager() {
return typeManager;
}
/**
* Return true if updates should only include changed properties. Otherwise
* all loaded properties are included in the update.
@@ -1209,7 +1124,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
public T createReference(Boolean readOnly, Object id) {
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
CachedBeanData d = (CachedBeanData) cacheHelp.beanCacheGetData(id);
CachedBeanData d = cacheHelp.beanCacheGetData(id);
if (d != null) {
Object shareableBean = d.getSharableBean();
if (shareableBean != null) {
@@ -1223,8 +1138,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoaderByServerName(ebeanServer.getName());
ebi.setBeanLoader(ebeanServer);
// Note: not creating proxies for many's...
ebi.setReference(idPropertyIndex);
@@ -1236,13 +1150,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
}
/**
* Return the BeanProperty for the given deployment name.
*/
public BeanProperty getBeanPropertyFromDbColumn(String dbColumn) {
return propMapByDbColumn.get(dbColumn);
}
/**
* Return the bean property traversing the object graph and taking into
* account inheritance.
@@ -1371,14 +1278,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return (idProperty == null) ? null : idProperty.getValue(bean);
}
/**
* Return false if the id is a simple scalar and false if it is embedded or
* concatenated.
*/
public boolean isComplexId() {
return idBinder.isComplexId();
}
/**
* Return the default order by that may need to be added if a many property is
* included in the query.
@@ -1409,7 +1308,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* Get a BeanProperty by its name.
*/
public BeanProperty getBeanProperty(String propName) {
return (BeanProperty) propMap.get(propName);
return propMap.get(propName);
}
public void sort(List<T> list, String sortByClause) {
@@ -1588,11 +1487,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return prop;
}
protected Object getBeanPropertyWithInheritance(EntityBean bean, String propName) {
/**
* Reset the many properties to empty state ready for reloading.
*/
public void resetManyProperties(Object dbBean) {
BeanDescriptor<?> desc = getBeanDescriptor(bean.getClass());
BeanProperty beanProperty = desc.findBeanProperty(propName);
return beanProperty.getValue(bean);
EntityBean bean = (EntityBean)dbBean;
for (int i = 0; i < propertiesMany.length; i++) {
propertiesMany[i].resetMany(bean);
}
}
/**
@@ -1635,17 +1538,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return EntityType.EMBEDDED.equals(entityType);
}
public boolean isBaseTableType() {
return EntityType.ORM.equals(entityType);
}
/**
* Return the concurrency mode used for beans of this type.
*/
public ConcurrencyMode getConcurrencyMode() {
return concurrencyMode;
}
/**
* Return the tables this bean is dependent on. This implies that if any of
* these tables are modified then cached beans may be invalidated.
@@ -1664,7 +1556,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Return the beanListener.
*/
public BeanPersistListener<T> getPersistListener() {
public BeanPersistListener getPersistListener() {
return persistListener;
}
@@ -1686,16 +1578,16 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* De-register the BeanPersistListener.
*/
@SuppressWarnings("unchecked")
public void deregister(BeanPersistListener<?> listener) {
public void deregister(BeanPersistListener listener) {
// volatile read...
BeanPersistListener<T> currListener = persistListener;
BeanPersistListener currListener = persistListener;
if (currListener == null) {
// nothing to deregister
} else {
BeanPersistListener<T> deregListener = (BeanPersistListener<T>) listener;
if (currListener instanceof ChainedBeanPersistListener<?>) {
BeanPersistListener deregListener = listener;
if (currListener instanceof ChainedBeanPersistListener) {
// remove it from the existing chain
persistListener = ((ChainedBeanPersistListener<T>) currListener).deregister(deregListener);
persistListener = ((ChainedBeanPersistListener) currListener).deregister(deregListener);
} else if (currListener.equals(deregListener)) {
persistListener = null;
}
@@ -1708,9 +1600,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
public void deregister(BeanPersistController controller) {
// volatile read...
BeanPersistController c = persistController;
if (c == null) {
// nothing to deregister
} else {
if (c != null) {
if (c instanceof ChainedBeanPersistController) {
// remove it from the existing chain
persistController = ((ChainedBeanPersistController) c).deregister(controller);
@@ -1724,23 +1614,20 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* Register the new BeanPersistController.
*/
@SuppressWarnings("unchecked")
public void register(BeanPersistListener<?> newPersistListener) {
public void register(BeanPersistListener newPersistListener) {
if (!PersistListenerManager.isRegisterFor(beanType, newPersistListener)) {
// skip
} else {
BeanPersistListener<T> newListener = (BeanPersistListener<T>) newPersistListener;
if (newPersistListener.isRegisterFor(beanType)) {
// volatile read...
BeanPersistListener<T> currListener = persistListener;
BeanPersistListener currListener = persistListener;
if (currListener == null) {
persistListener = newListener;
persistListener = newPersistListener;
} else {
if (currListener instanceof ChainedBeanPersistListener<?>) {
if (currListener instanceof ChainedBeanPersistListener) {
// add it to the existing chain
persistListener = ((ChainedBeanPersistListener<T>) currListener).register(newListener);
persistListener = ((ChainedBeanPersistListener) currListener).register(newPersistListener);
} else {
// build new chain of the 2
persistListener = new ChainedBeanPersistListener<T>(currListener, newListener);
persistListener = new ChainedBeanPersistListener(currListener, newPersistListener);
}
}
}
@@ -1751,9 +1638,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
*/
public void register(BeanPersistController newController) {
if (!newController.isRegisterFor(beanType)) {
// skip
} else {
if (newController.isRegisterFor(beanType)) {
// volatile read...
BeanPersistController c = persistController;
if (c == null) {
@@ -1797,13 +1682,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return baseTable;
}
/**
* Get a named extra attribute.
*/
public String getExtraAttribute(String key) {
return (String) extraAttrMap.get(key);
}
/**
* Return the identity generation type.
*/
@@ -1854,10 +1732,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
/**
* Return an Iterator of all BeanProperty. This includes transient properties.
* Return a collection of all BeanProperty. This includes transient properties.
*/
public Iterator<BeanProperty> propertiesAll() {
return propMap.values().iterator();
public Collection<BeanProperty> propertiesAll() {
return propMap.values();
}
/**
@@ -1886,21 +1764,31 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return idProperty;
}
public boolean isInsertMode(EntityBeanIntercept ebi) {
/**
* Return true if this bean should be inserted rather than updated.
*
* @param ebi
* The entity bean intercept
* @param insertMode
* true if the 'root request' was an insert rather than an update
*/
public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) {
if (ebi.isLoaded()) {
// must be an update as the bean is loaded
return false;
}
// determine based on Id property
if (idProperty.isEmbedded()) {
// not using Id generator so just base on isLoaded()
return !ebi.isLoaded();
}
//if (idGenerator == null) {
// return !ebi.isLoaded();
//} else {
return !hasIdProperty(ebi);
//}
if (!hasIdProperty(ebi)) {
// No Id property means it must be an insert
return true;
}
// same as the 'root request'
return insertMode;
}
public boolean isReference(EntityBeanIntercept ebi) {
@@ -1912,17 +1800,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
public boolean hasIdProperty(EntityBeanIntercept ebi) {
if (idPropertyIndex > -1) {
return ebi.isLoadedProperty(idPropertyIndex);
}
return false;
return idPropertyIndex > -1 && ebi.isLoadedProperty(idPropertyIndex);
}
public boolean hasVersionProperty(EntityBeanIntercept ebi) {
if (versionPropertyIndex > -1) {
return ebi.isLoadedProperty(versionPropertyIndex);
}
return false;
return versionPropertyIndex > -1 && ebi.isLoadedProperty(versionPropertyIndex);
}
/**
@@ -1985,16 +1867,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return propertiesOneImportedDelete;
}
/**
* Returns OneToOnes that are on the exported side of a OneToOne.
* <p>
* These associations do not own the relationship.
* </p>
*/
public BeanPropertyAssocOne<?>[] propertiesOneExported() {
return propertiesOneExported;
}
/**
* Exported assoc ones with cascade save.
*/
@@ -2009,32 +1881,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return propertiesOneExportedDelete;
}
private Set<String> deriveManyPropNames() {
LinkedHashSet<String> names = new LinkedHashSet<String>();
for (int i = 0; i < propertiesMany.length; i++) {
names.add(propertiesMany[i].getName());
}
return Collections.unmodifiableSet(names);
}
/**
* Return a hash of the names of the many properties on this bean type. This
* is used for efficient building of included properties sets for partial
* objects.
*/
public int getNamesOfManyPropsHash() {
return namesOfManyPropsHash;
}
/**
* Returns the set of many property names for this bean type.
*/
public Set<String> getNamesOfManyProps() {
return namesOfManyProps;
}
/**
* All Non Assoc Many's for this descriptor.
*/
@@ -2081,20 +1927,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return versionProperty;
}
/**
* Return true if this is an Update (rather than insert) given that the bean
* is involved in a stateless update.
*/
public boolean isStatelessUpdate(EntityBean bean) {
if (versionProperty == null) {
Object versionValue = getId(bean);
return !DmlUtil.isNullOrZero(versionValue);
} else {
Object versionValue = versionProperty.getValue(bean);
return !DmlUtil.isNullOrZero(versionValue);
}
}
/**
* Scalar properties without the unique id or secondary table properties.
*/
@@ -2119,167 +1951,23 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return propertiesLocal;
}
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
if (bean != null) {
ctx.appendObjectBegin();
WriteBeanState prevState = ctx.pushBeanState(bean);
if (inheritInfo != null) {
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
String discValue = localInheritInfo.getDiscriminatorStringValue();
String discColumn = localInheritInfo.getDiscriminatorColumn();
ctx.appendDiscriminator(discColumn, discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
localDescriptor.jsonWriteProperties(ctx, bean);
} else {
jsonWriteProperties(ctx, bean);
}
ctx.pushPreviousState(prevState);
ctx.appendObjectEnd();
}
}
@SuppressWarnings("unchecked")
private void jsonWriteProperties(WriteJsonContext ctx, EntityBean bean) {
JsonWriteBeanVisitor<T> beanVisitor = (JsonWriteBeanVisitor<T>) ctx.getBeanVisitor();
Set<String> props = ctx.getIncludeProperties();
boolean explicitAllProps;
if (props == null) {
explicitAllProps = false;
} else {
explicitAllProps = props.contains("*");
if (explicitAllProps || props.isEmpty()) {
props = null;
}
}
if (idProperty != null) {
Object idValue = idProperty.getValue(bean);
if (idValue != null) {
if (props == null || props.contains(idProperty.getName())) {
idProperty.jsonWrite(ctx, bean);
}
}
}
if (!explicitAllProps && props == null) {
// just render the loaded properties
props = ((EntityBean)bean)._ebean_getIntercept().getLoadedPropertyNames();
}
if (props != null) {
// render only the appropriate properties (when not all properties)
for (String prop : props) {
BeanProperty p = getBeanProperty(prop);
if (p != null && !p.isId()) {
p.jsonWrite(ctx, bean);
}
}
} else {
if (explicitAllProps || !isReference(bean._ebean_getIntercept())) {
// render all the properties and invoke lazy loading if required
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);
}
}
}
if (beanVisitor != null) {
beanVisitor.visit((T) bean, ctx);
}
}
@SuppressWarnings("unchecked")
public T jsonReadBean(ReadJsonContext ctx, String path) {
ReadBeanState beanState = jsonRead(ctx, path);
if (beanState == null) {
return null;
} else {
return (T) beanState.getBean();
}
}
public ReadBeanState jsonRead(ReadJsonContext ctx, String path) {
if (!ctx.readObjectBegin()) {
// the object is null
return null;
}
if (inheritInfo == null) {
return jsonReadObject(ctx, path);
} else {
// check for the discriminator value to determine the correct sub type
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
if (!ctx.readKeyNext()) {
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
throw new TextException(msg);
}
String propName = ctx.getTokenKey();
String discValue;
if (propName.equalsIgnoreCase(discColumn)) {
discValue = ctx.readScalarValue();
if (!ctx.readValueNext()) {
// Expected to read a comma to setup for reading the real properties of the bean
String msg = "Error reading inheritance discriminator [" + discColumn + "]. Expected more json name values?";
throw new TextException(msg);
}
} else {
// Assume that the we are just reading using this bean type
// Push the token key back so that it is re-read as it is one
// of the real properties of the bean itself
ctx.pushTokenKey();
discValue = inheritInfo.getDiscriminatorStringValue();
}
// determine the sub type for this particular json object
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
return localDescriptor.jsonReadObject(ctx, path);
}
}
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, null);
}
private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) {
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, key);
}
EntityBean bean = createEntityBean();
ctx.pushBean(bean, path, this);
do {
if (!ctx.readKeyNext()) {
break;
} else {
// we read a property key ...
String propName = ctx.getTokenKey();
BeanProperty p = getBeanProperty(propName);
if (p != null) {
p.jsonRead(ctx, bean);
ctx.setProperty(propName);
} else {
// unknown property key ...
ctx.readUnmappedJson(propName);
}
if (!ctx.readValueNext()) {
break;
}
}
} while (true);
return ctx.popBeanState();
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWriteProperties(writeJson, bean);
}
public T jsonRead(JsonParser parser, String path) throws IOException {
return jsonHelp.jsonRead(parser, path);
}
protected T jsonReadObject(JsonParser parser, String path) throws IOException {
return jsonHelp.jsonReadObject(parser, path);
}
}
@@ -105,7 +105,16 @@ public final class BeanDescriptorCacheHelp<T> {
}
}
}
public void setUseCache(boolean useCache) {
if (useCache) {
getBeanCache();
} else {
beanCacheClear();
beanCache = null;
}
}
/**
* Return true if there is currently query caching for this type of bean.
*/
@@ -429,18 +438,21 @@ public final class BeanDescriptorCacheHelp<T> {
EntityBean bean = desc.createEntityBean();
desc.convertSetId(id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
bean._ebean_getIntercept().setReadOnly(true);
ebi.setReadOnly(true);
}
beanLoadData(bean, data);
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit", cacheName, id);
}
return (T) bean;
}
/**
* Remove a bean from the cache given its Id.
*/
@@ -0,0 +1,131 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
public class BeanDescriptorJsonHelp<T> {
private final BeanDescriptor<T> desc;
private final InheritInfo inheritInfo;
public BeanDescriptorJsonHelp(BeanDescriptor<T> desc) {
this.desc = desc;
this.inheritInfo = desc.inheritInfo;
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
writeJson.writeStartObject(key);
if (inheritInfo == null) {
jsonWriteProperties(writeJson, bean);
} else {
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
String discValue = localInheritInfo.getDiscriminatorStringValue();
String discColumn = localInheritInfo.getDiscriminatorColumn();
writeJson.gen().writeStringField(discColumn, discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
localDescriptor.jsonWriteProperties(writeJson, bean);
}
writeJson.writeEndObject();
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
WriteBean writeBean = writeJson.createWriteBean(desc, bean);
writeBean.write(writeJson);
}
@SuppressWarnings("unchecked")
public T jsonRead(JsonParser parser, String path) throws IOException {
JsonToken token = parser.nextToken();
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
return null;
}
if (JsonToken.START_OBJECT != token) {
throw new JsonParseException("Unexpected token "+token+" - expecting start_object", parser.getCurrentLocation());
}
if (desc.inheritInfo == null) {
return jsonReadObject(parser, path);
}
// check for the discriminator value to determine the correct sub type
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
token = parser.nextToken();
if (token != JsonToken.FIELD_NAME) {
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
throw new JsonParseException(msg, parser.getCurrentLocation());
}
String propName = parser.getCurrentName();
if (!propName.equalsIgnoreCase(discColumn)) {
// just try to assume this is the correct bean type in the inheritance
BeanProperty property = desc.getBeanProperty(propName);
if (property != null) {
EntityBean bean = desc.createEntityBean();
property.jsonRead(parser, bean);
return jsonReadProperties(parser, bean);
}
String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?";
throw new JsonParseException(msg, parser.getCurrentLocation());
}
String discValue = parser.nextTextValue();
// determine the sub type for this particular json object
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
return (T) localDescriptor.jsonReadObject(parser, path);
}
protected T jsonReadObject(JsonParser parser, String path) throws IOException {
EntityBean bean = desc.createEntityBean();
return jsonReadProperties(parser, bean);
}
@SuppressWarnings("unchecked")
protected T jsonReadProperties(JsonParser parser, EntityBean bean) throws IOException {
do {
JsonToken event = parser.nextToken();
if (JsonToken.FIELD_NAME == event) {
String key = parser.getCurrentName();
BeanProperty p = desc.getBeanProperty(key);
if (p != null) {
p.jsonRead(parser, bean);
} else {
// Object rawValue = EJson.parse(parser);
// unknown property key ...
// ctx.readUnmappedJson(propName);
}
} else if (JsonToken.END_OBJECT == event) {
break;
} else {
throw new RuntimeException("Unexpected token " + event + " - expecting key or end_object at: " + parser.getCurrentLocation());
}
} while (true);
return (T)bean;
}
}
@@ -1,17 +1,20 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import org.slf4j.Logger;
@@ -59,12 +62,10 @@ import com.avaje.ebeaninternal.server.deploy.parse.ReadAnnotations;
import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectProperties;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory;
import com.avaje.ebeaninternal.server.type.TypeManager;
/**
@@ -85,7 +86,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private final DeployInherit deplyInherit;
private final BeanReflectFactory reflectFactory;
private final BeanPropertyInfoFactory reflectFactory;
private final DeployUtil deployUtil;
@@ -510,9 +511,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityBeanTable() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
@@ -526,18 +525,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentAssociations() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
readDeployAssociations(info);
}
}
private void readInheritedIdGenerators() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
@@ -613,10 +608,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* @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());
}
@@ -626,7 +621,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
if (assoc != null){
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -755,9 +749,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
String searchName = name.substring(0, p).toLowerCase();
// search for this in the possible matches
Iterator<String> it = matchSet.iterator();
while (it.hasNext()) {
String possibleMappedBy = it.next();
for (String possibleMappedBy : matchSet) {
String possibleLower = possibleMappedBy.toLowerCase();
if (possibleLower.indexOf(searchName) > -1) {
// we have a match..
@@ -1019,6 +1011,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
desc.checkInheritanceMapping();
createProperties.createProperties(desc);
@@ -1153,12 +1146,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void setScalarType(DeployBeanDescriptor<?> deployDesc) {
Iterator<DeployBeanProperty> it = deployDesc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop instanceof DeployBeanPropertyAssoc<?>) {
} else {
for (DeployBeanProperty prop : deployDesc.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssoc<?> == false) {
deployUtil.setScalarType(prop);
}
}
@@ -1285,9 +1274,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
private BeanReflectFactory createReflectionFactory() {
private BeanPropertyInfoFactory createReflectionFactory() {
return new EnhanceBeanReflectFactory();
return new EnhanceBeanPropertyInfoFactory();
}
/**
@@ -1306,36 +1295,45 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
Class<?> beanType = desc.getBeanType();
BeanReflectProperties reflectProps = new BeanReflectProperties(beanType);
BeanPropertiesReader reflectProps = new BeanPropertiesReader(beanType);
BeanReflect beanReflect = reflectFactory.create(beanType);
BeanPropertyInfo beanReflect = reflectFactory.create(beanType);
desc.setBeanReflect(beanReflect);
desc.setProperties(reflectProps.getProperties());
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : desc.propertiesAll()) {
String propName = prop.getName();
Integer pos = reflectProps.getPropertyIndex(propName);
if (pos == null) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
BeanReflectGetter getter = beanReflect.getGetter(propName, pos.intValue());
BeanReflectSetter setter = beanReflect.getSetter(propName, pos.intValue());
prop.setGetter(getter);
prop.setSetter(setter);
prop.setPropertyIndex(pos.intValue());
if (getter == null) {
String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?";
throw new RuntimeException(m);
if (isPersistentField(prop)) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
} else {
int propertyIndex = pos.intValue();
prop.setPropertyIndex(propertyIndex);
prop.setGetter(beanReflect.getGetter(propName, propertyIndex));
prop.setSetter(beanReflect.getSetter(propName, propertyIndex));
}
}
}
/**
* Return true if this is a persistent field (not transient or static).
*/
private boolean isPersistentField(DeployBeanProperty prop) {
Field field = prop.getField();
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
return false;
}
if (field.isAnnotationPresent(Transient.class)) {
return false;
}
return true;
}
/**
* DevNote: It is assumed that Embedded can contain version properties. It is
* also assumed that Embedded beans do NOT themselves contain Embedded beans
@@ -1407,6 +1405,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* enhanced or all dynamically subclassed).
*/
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
// we got to the top of the inheritance
@@ -1417,14 +1416,40 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return;
}
if (!EntityBean.class.isAssignableFrom(superclass)) {
if (isMappedSuperWithNoProperties(superclass)) {
// ok to stop and treat just the same as Object.class
return;
}
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
}
// recursively continue up the inheritance hierarchy
checkInheritedClasses(superclass);
}
/**
* Return true if this is a MappedSuperclass bean with no persistent properties.
* If so it is ok for it not to be enhanced.
*/
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
if (annotation == null) {
return false;
}
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) {
// ignore this field
} else if (field.isAnnotationPresent(Transient.class)) {
// ignore this field
} else {
return false;
}
}
return true;
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -12,7 +13,7 @@ import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.common.BeanList;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
/**
* Helper object for dealing with Lists.
@@ -21,29 +22,36 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
private final BeanPropertyAssocMany<T> many;
private final BeanDescriptor<T> targetDescriptor;
private final String propertyName;
private BeanCollectionLoader loader;
public BeanListHelp(BeanPropertyAssocMany<T> many) {
this.many = many;
this.targetDescriptor = many.getTargetDescriptor();
this.propertyName = many.getName();
}
public BeanListHelp() {
this.many = null;
this.targetDescriptor = null;
this.propertyName = null;
}
@Override
public void setLoader(BeanCollectionLoader loader) {
this.loader = loader;
}
/**
* Internal add bypassing any modify listening.
*/
@Override
public void add(BeanCollection<?> collection, EntityBean bean) {
collection.internalAdd(bean);
}
@Override
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
if (bc instanceof BeanList<?>) {
@@ -53,57 +61,42 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
bl.setActualList(new ArrayList<Object>());
}
return bl;
} else if (bc instanceof List<?>) {
return new VanillaAdd((List<?>) bc);
} else {
throw new RuntimeException("Unhandled type " + bc);
}
}
@SuppressWarnings("unchecked")
static class VanillaAdd implements BeanCollectionAdd {
@SuppressWarnings("rawtypes")
private final List list;
private VanillaAdd(List<?> list) {
this.list = list;
}
public void addBean(EntityBean bean) {
list.add(bean);
}
@Override
public BeanCollection<T> createEmptyNoParent() {
return new BeanList<T>();
}
public Iterator<?> getIterator(Object collection) {
return ((List<?>) collection).iterator();
}
public Object createEmpty(boolean vanilla) {
if (vanilla) {
return new ArrayList<T>();
}
BeanList<T> beanList = new BeanList<T>();
@Override
public BeanCollection<T> createEmpty(EntityBean parentBean) {
BeanList<T> beanList = new BeanList<T>(loader, parentBean, propertyName);
if (many != null) {
beanList.setModifyListening(many.getModifyListenMode());
}
return beanList;
}
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName) {
@Override
public BeanCollection<T> createReference(EntityBean parentBean) {
BeanList<T> beanList = new BeanList<T>(loader, parentBean, propertyName);
beanList.setModifyListening(many.getModifyListenMode());
return beanList;
}
@Override
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean) {
BeanList<?> newBeanList = (BeanList<?>) server.findList(query, t);
refresh(newBeanList, parentBean);
}
@Override
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
BeanList<?> newBeanList = (BeanList<?>) bc;
@@ -128,7 +121,8 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
}
}
public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) {
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
List<?> list;
if (collection instanceof BeanCollection<?>) {
@@ -147,15 +141,11 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
list = (List<?>) collection;
}
ctx.beginAssocMany(name);
ctx.writeStartArray(name);
for (int j = 0; j < list.size(); j++) {
if (j > 0) {
ctx.appendComma();
}
Object detailBean = list.get(j);
targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean);
targetDescriptor.jsonWrite(ctx, (EntityBean) list.get(j));
}
ctx.endAssocMany();
ctx.writeEndArray();
}
}
@@ -1,10 +1,5 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
@@ -13,181 +8,177 @@ import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.common.BeanMap;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Helper specifically for dealing with Maps.
*/
public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
private final BeanPropertyAssocMany<T> many;
private final BeanDescriptor<T> targetDescriptor;
private final BeanProperty beanProperty;
private BeanCollectionLoader loader;
//private final String mapKey;
/**
* When created for a given query that will return a map.
*/
public BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
this(null, targetDescriptor, mapKey);
}
private final BeanPropertyAssocMany<T> many;
private final BeanDescriptor<T> targetDescriptor;
private final String propertyName;
private final BeanProperty beanProperty;
private BeanCollectionLoader loader;
public BeanMapHelp(BeanPropertyAssocMany<T> many){
this(many, many.getTargetDescriptor(), many.getMapKey());
}
/**
* When help is attached to a specific many property.
*/
private BeanMapHelp(BeanPropertyAssocMany<T> many, BeanDescriptor<T> targetDescriptor, String mapKey){
this.many = many;
this.targetDescriptor = targetDescriptor;
//this.mapKey = mapKey;
this.beanProperty = targetDescriptor.getBeanProperty(mapKey);
}
/**
* Return an iterator of the values.
*/
public Iterator<?> getIterator(Object collection) {
return ((Map<?,?>) collection).values().iterator();
/**
* When created for a given query that will return a map.
*/
public BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
this.targetDescriptor = targetDescriptor;
this.beanProperty = targetDescriptor.getBeanProperty(mapKey);
this.many = null;
this.propertyName = null;
}
/**
* When help is attached to a specific many property.
*/ public BeanMapHelp(BeanPropertyAssocMany<T> many) {
this.many = many;
this.targetDescriptor = many.getTargetDescriptor();
this.propertyName = many.getName();
this.beanProperty = targetDescriptor.getBeanProperty(many.getMapKey());
}
@Override
public void setLoader(BeanCollectionLoader loader) {
this.loader = loader;
}
@Override
@SuppressWarnings("unchecked")
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
if (mapKey == null) {
mapKey = many.getMapKey();
}
public void setLoader(BeanCollectionLoader loader){
this.loader = loader;
}
BeanProperty beanProp = targetDescriptor.getBeanProperty(mapKey);
@SuppressWarnings("unchecked")
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
if(mapKey == null){
mapKey = many.getMapKey();
}
BeanProperty beanProp = targetDescriptor.getBeanProperty(mapKey);
if (bc instanceof BeanMap<?,?>){
BeanMap<Object, Object> bm = (BeanMap<Object, Object>)bc;
Map<Object, Object> actualMap = bm.getActualMap();
if (actualMap == null){
actualMap = new LinkedHashMap<Object, Object>();
bm.setActualMap(actualMap);
}
return new Adder(beanProp, actualMap);
} else if (bc instanceof Map<?,?>) {
return new Adder(beanProp, (Map<Object, Object>)bc);
} else {
throw new RuntimeException("Unhandled type "+bc);
}
}
if (bc instanceof BeanMap<?, ?>) {
BeanMap<Object, Object> bm = (BeanMap<Object, Object>) bc;
Map<Object, Object> actualMap = bm.getActualMap();
if (actualMap == null) {
actualMap = new LinkedHashMap<Object, Object>();
bm.setActualMap(actualMap);
}
return new Adder(beanProp, actualMap);
static class Adder implements BeanCollectionAdd {
private final BeanProperty beanProperty;
private final Map<Object, Object> map;
Adder(BeanProperty beanProperty, Map<Object, Object> map) {
this.beanProperty = beanProperty;
this.map = map;
}
public void addBean(EntityBean bean) {
Object keyValue = beanProperty.getValue(bean);
map.put(keyValue, bean);
}
}
} else {
throw new RuntimeException("Unhandled type " + bc);
}
}
@SuppressWarnings("rawtypes")
public Object createEmpty(boolean vanilla) {
if (vanilla) {
return new LinkedHashMap();
}
BeanMap beanMap = new BeanMap();
if (many != null) {
beanMap.setModifyListening(many.getModifyListenMode());
}
return beanMap;
}
public void add(BeanCollection<?> collection, EntityBean bean) {
static class Adder implements BeanCollectionAdd {
Object keyValue = beanProperty.getValueIntercept(bean);
private final BeanProperty beanProperty;
((BeanMap<?,?>) collection).internalPut(keyValue, bean);
}
private final Map<Object, Object> map;
@SuppressWarnings({ "unchecked", "rawtypes" })
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName) {
Adder(BeanProperty beanProperty, Map<Object, Object> map) {
this.beanProperty = beanProperty;
this.map = map;
}
BeanMap beanMap = new BeanMap(loader, parentBean, propertyName);
public void addBean(EntityBean bean) {
Object keyValue = beanProperty.getValue(bean);
map.put(keyValue, bean);
}
}
@Override
public BeanCollection<T> createEmptyNoParent() {
return new BeanMap();
}
@Override
@SuppressWarnings("rawtypes")
public BeanCollection<T> createEmpty(EntityBean ownerBean) {
BeanMap beanMap = new BeanMap(loader, ownerBean, propertyName);
if (many != null) {
beanMap.setModifyListening(many.getModifyListenMode());
}
return beanMap;
}
}
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean) {
BeanMap<?, ?> newBeanMap = (BeanMap<?, ?>) server.findMap(query, t);
refresh(newBeanMap, parentBean);
}
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
@Override
public void add(BeanCollection<?> collection, EntityBean bean) {
BeanMap<?, ?> newBeanMap = (BeanMap<?, ?>) bc;
Map<?, ?> current = (Map<?, ?>) many.getValue(parentBean);
Object keyValue = beanProperty.getValueIntercept(bean);
((BeanMap<?, ?>) collection).internalPut(keyValue, bean);
}
newBeanMap.setModifyListening(many.getModifyListenMode());
if (current == null) {
// the currentMap is null? Not really expecting this...
many.setValue(parentBean, newBeanMap);
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public BeanCollection<T> createReference(EntityBean parentBean) {
} else if (current instanceof BeanMap<?,?>) {
// normally this case, replace just the underlying list
BeanMap<?, ?> currentBeanMap = (BeanMap<?, ?>) current;
currentBeanMap.setActualMap(newBeanMap.getActualMap());
currentBeanMap.setModifyListening(many.getModifyListenMode());
BeanMap beanMap = new BeanMap(loader, parentBean, propertyName);
if (many != null) {
beanMap.setModifyListening(many.getModifyListenMode());
}
return beanMap;
}
} else {
// replace the entire set
many.setValue(parentBean, newBeanMap);
}
}
@Override
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean) {
BeanMap<?, ?> newBeanMap = (BeanMap<?, ?>) server.findMap(query, t);
refresh(newBeanMap, parentBean);
}
public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude) {
Map<?,?> map;
if (collection instanceof BeanCollection<?>){
BeanMap<?,?> bc = (BeanMap<?,?>)collection;
if (!bc.isPopulated()){
if (explicitInclude){
// invoke lazy loading as collection
// is explicitly included in the output
bc.size();
} else {
return;
}
}
map = bc.getActualMap();
@Override
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
BeanMap<?, ?> newBeanMap = (BeanMap<?, ?>) bc;
Map<?, ?> current = (Map<?, ?>) many.getValue(parentBean);
newBeanMap.setModifyListening(many.getModifyListenMode());
if (current == null) {
// the currentMap is null? Not really expecting this...
many.setValue(parentBean, newBeanMap);
} else if (current instanceof BeanMap<?, ?>) {
// normally this case, replace just the underlying list
BeanMap<?, ?> currentBeanMap = (BeanMap<?, ?>) current;
currentBeanMap.setActualMap(newBeanMap.getActualMap());
currentBeanMap.setModifyListening(many.getModifyListenMode());
} else {
// replace the entire set
many.setValue(parentBean, newBeanMap);
}
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
Map<?, ?> map;
if (collection instanceof BeanCollection<?>) {
BeanMap<?, ?> bc = (BeanMap<?, ?>) collection;
if (!bc.isPopulated()) {
if (explicitInclude) {
// invoke lazy loading as collection
// is explicitly included in the output
bc.size();
} else {
map = (Map<?,?>)collection;
return;
}
int count = 0;
ctx.beginAssocMany(name);
Iterator<?> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<?, ?> entry = (Entry<?, ?>)it.next();
if (count++ > 0){
ctx.appendComma();
}
//FIXME: json write map key ...
Object detailBean = entry.getValue();
targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean);
}
ctx.endAssocMany();
}
map = bc.getActualMap();
} else {
map = (Map<?, ?>) collection;
}
ctx.writeStartArray(name);
for (Entry<?, ?> entry : map.entrySet()) {
//FIXME: json write map key ...
targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue());
}
ctx.writeEndArray();
}
}
@@ -18,7 +18,6 @@ import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.text.StringFormatter;
import com.avaje.ebean.text.StringParser;
import com.avaje.ebean.text.TextException;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
@@ -28,12 +27,13 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter;
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
/**
* Description of a property of a bean. Includes its deployment information such
@@ -41,29 +41,6 @@ import com.avaje.ebeaninternal.server.type.ScalarType;
*/
public class BeanProperty implements ElPropertyValue {
/**
* Advanced bean deployment. To exclude this property from update where
* clause.
*/
public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE";
/**
* Advanced bean deployment. To exclude this property from delete where
* clause.
*/
public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE";
/**
* Advanced bean deployment. To exclude this property from insert.
*/
public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT";
/**
* Advanced bean deployment. To exclude this property from update set
* clause.
*/
public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE";
/**
* Flag to mark this at part of the unique id.
*/
@@ -219,9 +196,9 @@ public class BeanProperty implements ElPropertyValue {
*/
final GeneratedProperty generatedProperty;
final BeanReflectGetter getter;
final BeanPropertyGetter getter;
final BeanReflectSetter setter;
final BeanPropertySetter setter;
final BeanDescriptor<?> descriptor;
@@ -262,6 +239,9 @@ public class BeanProperty implements ElPropertyValue {
final boolean jsonDeserialize;
final boolean indexed;
final String indexName;
public BeanProperty(DeployBeanProperty deploy) {
this(null, null, deploy);
}
@@ -271,7 +251,8 @@ public class BeanProperty implements ElPropertyValue {
this.descriptor = descriptor;
this.name = InternString.intern(deploy.getName());
this.propertyIndex = deploy.getPropertyIndex();
this.indexed = deploy.isIndexed();
this.indexName = deploy.getIndexName();
this.unidirectionalShadow = deploy.isUndirectionalShadow();
this.discriminator = deploy.isDiscriminator();
this.localEncrypted = deploy.isLocalEncrypted();
@@ -362,6 +343,8 @@ public class BeanProperty implements ElPropertyValue {
this.name = InternString.intern(source.getName());
this.propertyIndex = source.propertyIndex;
this.indexed = source.isIndexed();
this.indexName = source.getIndexName();
this.dbColumn = InternString.intern(override.getDbColumn());
this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin());
this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect());
@@ -446,7 +429,7 @@ public class BeanProperty implements ElPropertyValue {
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain,
boolean propertyDeploy) {
boolean propertyDeploy) {
throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName());
}
@@ -475,22 +458,14 @@ public class BeanProperty implements ElPropertyValue {
* Return true if this property maps to the inheritance discriminator column.
*/
public boolean isDiscriminator() {
return discriminator;
return discriminator;
}
/**
* Return true if the underlying type is mutable.
*/
public boolean isMutableScalarType() {
if (scalarType == null) {
return false;
}
return scalarType.isMutable();
}
public void copyProperty(EntityBean sourceBean, EntityBean destBean) {
Object value = getValue(sourceBean);
setValue(destBean, value);
return scalarType != null && scalarType.isMutable();
}
/**
@@ -500,10 +475,6 @@ public class BeanProperty implements ElPropertyValue {
return descriptor.getEncryptKey(this);
}
public String getDecryptProperty() {
return dbEncryptFunction.getDecryptSql(this.getName());
}
public String getDecryptProperty(String propertyName) {
return dbEncryptFunction.getDecryptSql(propertyName);
}
@@ -569,24 +540,6 @@ public class BeanProperty implements ElPropertyValue {
return owningType.isAssignableFrom(type);
}
public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
try {
Object value = scalarType.read(ctx.getDataReader());
if (value == null || bean == null) {
// not setting the value...
} else {
if (owningType.equals(type)) {
setValue(bean, value);
}
}
return value;
} catch (Exception e) {
String msg = "Error readSet on " + descriptor + "." + name;
throw new PersistenceException(msg, e);
}
}
public void loadIgnore(DbReadContext ctx) {
scalarType.loadIgnore(ctx.getDataReader());
}
@@ -610,16 +563,13 @@ public class BeanProperty implements ElPropertyValue {
public Object readSet(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
try {
Object value = scalarType.read(ctx.getDataReader());
if (bean == null || (type != null && !owningType.isAssignableFrom(type))) {
// not setting the value...
} else {
setValue(bean, value);
}
return value;
Object value = scalarType.read(ctx.getDataReader());
if (bean != null && ((type == null || owningType.isAssignableFrom(type)))) {
setValue(bean, value);
}
return value;
} catch (Exception e) {
String msg = "Error readSet on " + descriptor + "." + name;
throw new PersistenceException(msg, e);
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
}
@@ -647,24 +597,17 @@ public class BeanProperty implements ElPropertyValue {
return scalarType.readData(dataInput);
}
public boolean isCascadeValidate() {
return cascadeValidate;
}
/**
* Checks to see if a bean is a reference (will be lazy loaded) or a
* BeanCollection that has not yet been populated.
* <p>
* For base types this returns true.
* </p>
*/
public boolean isValueLoaded(Object value) {
return true;
}
public BeanProperty getBeanProperty() {
return this;
}
public boolean isIndexed() {
return indexed;
}
public String getIndexName() {
return indexName;
}
/**
* Return the getter method.
@@ -723,16 +666,21 @@ public class BeanProperty implements ElPropertyValue {
}
}
private static Object[] NO_ARGS = new Object[0];
public Object getCacheDataValue(EntityBean bean) {
return getValue(bean);
}
public Object getCacheDataValue(EntityBean bean){
return getValue(bean);
public void setCacheDataValue(EntityBean bean, Object cacheData) {
setValue(bean, cacheData);
}
public void setCacheDataValue(EntityBean bean, Object cacheData){
setValue(bean, cacheData);
/**
* Get the property value from a compound value type.
*/
public Object getValueObject(Object bean) {
throw new RuntimeException("Expected to be called only on BeanPropertyCompoundScalar");
}
/**
* Return the value of the property method.
*/
@@ -745,19 +693,6 @@ public class BeanProperty implements ElPropertyValue {
throw new RuntimeException(msg, ex);
}
}
/**
* Explicitly use reflection to get value.
*/
public Object getValueViaReflection(Object bean) {
try {
return readMethod.invoke(bean, NO_ARGS);
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error.";
throw new RuntimeException(msg, ex);
}
}
public Object getValueIntercept(EntityBean bean) {
try {
@@ -812,17 +747,9 @@ public class BeanProperty implements ElPropertyValue {
return name;
}
/**
* This is a full ElGetValue.
*/
public boolean isDeployOnly() {
return false;
}
@Override
public boolean containsFormulaWithJoin() {
return formula && sqlFormulaJoin != null;
return formula && sqlFormulaJoin != null;
}
public boolean containsManySince(String sinceProperty) {
@@ -880,16 +807,16 @@ public class BeanProperty implements ElPropertyValue {
/**
* Return true if the mutable value is considered dirty.
* This is only used for 'mutable' scalar types like hstore etc.
* This is only used for 'mutable' scalar types like hstore etc.
*/
public boolean isDirtyValue(Object value) {
return scalarType.isDirty(value);
return scalarType.isDirty(value);
}
/**
* Return the scalarType.
*/
public ScalarType<?> getScalarType() {
public ScalarType<Object> getScalarType() {
return scalarType;
}
@@ -906,11 +833,11 @@ public class BeanProperty implements ElPropertyValue {
}
public int getJdbcType() {
return scalarType == null ? 0 : scalarType.getJdbcType();
return scalarType == null ? 0 : scalarType.getJdbcType();
}
public Object parseDateTime(long systemTimeMillis) {
return scalarType.parseDateTime(systemTimeMillis);
public Object parseDateTime(long systemTimeMillis) {
return scalarType.convertFromMillis(systemTimeMillis);
}
/**
@@ -972,10 +899,10 @@ public class BeanProperty implements ElPropertyValue {
* Return true if this is the natural key property.
*/
public boolean isNaturalKey() {
return naturalKey;
return naturalKey;
}
/**
/**
* Return true if this property is mandatory.
*/
public boolean isNullable() {
@@ -1008,7 +935,7 @@ public class BeanProperty implements ElPropertyValue {
* Return true if this property is loadable from a resultSet.
*/
public boolean isLoadProperty() {
return !isTransient || formula;
return !isTransient || formula;
}
/**
@@ -1018,10 +945,6 @@ public class BeanProperty implements ElPropertyValue {
return version;
}
public String getDeployProperty() {
return dbColumn;
}
/**
* The database column name this is mapped to.
*/
@@ -1051,10 +974,10 @@ public class BeanProperty implements ElPropertyValue {
* Lob's usually default to fetch lazy.
*/
public boolean isFetchEager() {
return fetchEager;
return fetchEager;
}
/**
/**
* Return true if this is mapped to a Clob Blob LongVarchar or
* LongVarbinary.
*/
@@ -1064,17 +987,17 @@ public class BeanProperty implements ElPropertyValue {
private boolean isLobType(int type) {
switch (type) {
case Types.CLOB:
return true;
case Types.BLOB:
return true;
case Types.LONGVARBINARY:
return true;
case Types.LONGVARCHAR:
return true;
case Types.CLOB:
return true;
case Types.BLOB:
return true;
case Types.LONGVARBINARY:
return true;
case Types.LONGVARCHAR:
return true;
default:
return false;
default:
return false;
}
}
@@ -1155,13 +1078,6 @@ public class BeanProperty implements ElPropertyValue {
return embedded;
}
/**
* Return an extra attribute set on this property.
*/
public String getExtraAttribute(String key) {
return extraAttributeMap.get(key);
}
/**
* Return the default value.
*/
@@ -1173,35 +1089,30 @@ public class BeanProperty implements ElPropertyValue {
return name;
}
@SuppressWarnings("unchecked")
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
if(!jsonSerialize){
return;
}
Object value = getValueIntercept(bean);
if (value == null) {
ctx.appendNull(name);
} else {
ctx.appendNameValue(name, scalarType, value);
}
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
Object value = getValueIntercept(bean);
if (value == null) {
writeJson.writeNull(name);
} else {
scalarType.jsonWrite(writeJson.gen(), name, value);
}
}
public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException {
if (!jsonDeserialize) {
return;
}
public void jsonRead(ReadJsonContext ctx, EntityBean bean) {
if(!jsonDeserialize){
return;
}
String jsonValue;
try {
jsonValue = ctx.readScalarValue();
} catch (TextException e){
throw new TextException("Error reading property "+getFullBeanName(), e);
}
Object objValue;
if (jsonValue == null) {
objValue = null;
} else {
objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter());
}
setValue(bean, objValue);
JsonToken event = ctx.nextToken();
if (JsonToken.VALUE_NULL == event) {
setValue(bean, null);
} else {
// expect to read non-null json value
Object objValue = scalarType.jsonRead(ctx, event);
setValue(bean, objValue);
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.EntityBean;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class BeanPropertyAssocManyJsonHelp {
private final BeanPropertyAssocMany<?> many;
public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
this.many = many;
}
public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException {
if (!this.many.jsonDeserialize) {
return;
}
JsonToken event = parser.nextToken();
if (JsonToken.VALUE_NULL == event) {
return;
}
if (JsonToken.START_ARRAY != event) {
throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation());
}
BeanCollection<?> collection = many.createEmpty(parentBean);
BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null);
do {
EntityBean detailBean = (EntityBean) many.targetDescriptor.jsonRead(parser, many.name);
if (detailBean == null) {
// read the entire array
break;
}
add.addBean(detailBean);
if (parentBean != null && many.childMasterProperty != null) {
// bind detail bean back to master via mappedBy property
many.childMasterProperty.setValue(detailBean, parentBean);
}
} while (true);
many.setValue(parentBean, collection);
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -24,8 +25,8 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.fasterxml.jackson.core.JsonParser;
/**
* Property mapped to a joined bean.
@@ -264,14 +265,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
@Override
public boolean isValueLoaded(Object value) {
if (value instanceof EntityBean) {
return ((EntityBean) value)._ebean_getIntercept().isLoaded();
}
return true;
}
/**
* Return meta data for the deployment of the embedded bean specific to this
* property.
@@ -402,13 +395,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return getTargetDescriptor().createEntityBean();
}
/**
* Return an empty reference object.
*/
public Object createEmptyReference() {
return targetDescriptor.createEntityBean();
}
@Override
public Object elGetReference(EntityBean bean) {
Object value = getValueIntercept(bean);
@@ -472,7 +458,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]);
return list.toArray(new ExportedProperty[list.size()]);
}
/**
@@ -830,38 +816,40 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
@Override
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
Object value = getValueIntercept(bean);
if (value == null){
ctx.beginAssocOneIsNull(name);
} else {
if (ctx.isParentBean(value)){
// bi-directional and already rendered parent
} else {
// Hmmm, not writing complex non-entity bean
if (value instanceof EntityBean) {
ctx.pushParentBean(bean);
ctx.beginAssocOne(name);
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
refDesc.jsonWrite(ctx, (EntityBean)value);
ctx.endAssocOne();
ctx.popParentBean();
}
}
}
@Override
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
@Override
public void jsonRead(ReadJsonContext ctx, EntityBean bean){
if (targetDescriptor != null) {
T assocBean = targetDescriptor.jsonReadBean(ctx, name);
setValue(bean, assocBean);
Object value = getValueIntercept(bean);
if (value == null) {
writeJson.writeNull(name);
} else {
if (writeJson.isParentBean(value)) {
// bi-directional and already rendered parent
} else {
// Hmmm, not writing complex non-entity bean
if (value instanceof EntityBean) {
writeJson.beginAssocOne(name, bean);
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
refDesc.jsonWrite(writeJson, (EntityBean) value, name);
writeJson.endAssocOne();
}
}
}
}
@Override
public void jsonRead(JsonParser parser, EntityBean bean) throws IOException {
if (jsonDeserialize && targetDescriptor != null) {
T assocBean = targetDescriptor.jsonRead(parser, name);
setValue(bean, assocBean);
}
}
public boolean isReference(Object detailBean) {
EntityBean eb = (EntityBean)detailBean;
@@ -1,20 +1,23 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.text.json.EJson;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.fasterxml.jackson.core.JsonParser;
/**
* Property mapped to an Immutable Compound Value Object.
@@ -86,19 +89,6 @@ public class BeanPropertyCompound extends BeanProperty {
}
}
/**
* Get the underlying compound type.
*/
@SuppressWarnings("unchecked")
public Object getValueUnderlying(EntityBean bean) {
Object value = getValue(bean);
if (typeConverter != null){
value = typeConverter.unwrapValue(value);
}
return value;
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
if (chain == null) {
@@ -177,15 +167,32 @@ public class BeanPropertyCompound extends BeanProperty {
return bean;
}
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
Object valueObject = getValueIntercept(bean);
compoundType.jsonWrite(ctx, valueObject, name);
public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
Object value = getValueIntercept(bean);
if (value == null) {
ctx.writeNull(name);
} else {
compoundType.jsonWrite(ctx, value, name);
}
}
public void jsonRead(ReadJsonContext ctx, EntityBean bean){
Object objValue = compoundType.jsonRead(ctx);
public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException {
if (!jsonDeserialize) {
return;
}
Object value = EJson.parse(ctx);
if (value == null) {
setValue(bean, null);
} else {
@SuppressWarnings("unchecked")
Map<String,Object> map = (Map<String,Object>)value;
Object objValue = compoundType.jsonConvert(map);
setValue(bean, objValue);
}
}
}
@@ -7,7 +7,7 @@ import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
/**
@@ -22,7 +22,7 @@ import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
*/
public class BeanPropertyCompoundRoot {
private final BeanReflectSetter setter;
private final BeanPropertySetter setter;
/**
* The method used to write the property.

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