Compare commits

...
Author SHA1 Message Date
Rob Bygrave ad79f175da [maven-release-plugin] prepare release avaje-ebeanorm-4.0.3 2014-05-27 22:28:09 +12:00
Rob Bygrave a4b145bc2d Updated Model - javadoc and cleanup 2014-05-27 22:19:41 +12:00
Rob Bygrave 5491b27528 Fix for #131 - Memory issue (Hard refs) when using findIterate 2014-05-27 20:35:53 +12:00
Rob Bygrave 9211c7e6ab Update Model - insert,update,delete to other server, db(), db(server)
and javadoc tweaks
2014-05-26 01:18:17 +12:00
Rob Bygrave 5bcb6cd116 Cleanup of Model and Finder 2014-05-25 02:33:08 +12:00
Rob Bygrave 534f2c8b07 Initial cleanup of Model and Finder 2014-05-25 02:22:36 +12:00
Rob Bygrave 7ad390934e Add protected method on Ebean to specifically support mocking out the
default server.
2014-05-25 01:59:34 +12:00
Rob Bygrave 2a651c38e3 Initial add of Model and Finder 2014-05-25 00:27:32 +12:00
James RoperandRob Bygrave ddd30f9ea0 Backport to JDK 6
Replaced all uses of ju.Objects.hashCode and ju.Objects.equals with a
copy of their implementations inlined into the code.

Also changed source/target for compiler plugin.

To test, I compiled my own avaje launchagent against 6, and changed to
use that in the pom, compiled/tested the whole project using JDK 7,
then ran mvn surefire:test using JDK 6 - running surefire:test ensures
that mvn doesn't try to recompile everything against 6, since that's not
possible because of some of the delegate classes having delegate methods
to JDK 7 jdbc classes.
2014-05-24 22:51:05 +12:00
Rob Bygrave 01d41b7eea Update README.md 2014-05-19 00:26:21 +12:00
Rob Bygrave 4cdea33073 [maven-release-plugin] prepare for next development iteration 2014-05-18 23:19:06 +12:00
Rob Bygrave 33d7e10ad8 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.2 2014-05-18 23:18:42 +12:00
Rob Bygrave 2201f9220e Change version in pom for 4.0.2 release 2014-05-18 23:17:17 +12:00
Rob Bygrave b8743a5ef2 Fix for #85 - Invalid query when ORDER BY with DISTINCT 2014-05-18 23:00:28 +12:00
Rob Bygrave b587aa8172 No effective change - touch up test assert 2014-05-18 22:04:37 +12:00
Rob Bygrave c37fa83675 No effective change - update test with better assert 2014-05-18 21:51:14 +12:00
Rob Bygrave 08602afcff Fix for #37 - disjunction expression should not produce inner join - left outer join instead 2014-05-18 21:17:19 +12:00
Rob Bygrave 5b98d82f58 No effective change - reformat 2014-05-17 23:53:23 +12:00
Rob Bygrave 22cd5b0e82 Fix for #128 storing UUID as binary 2014-05-17 21:52:51 +12:00
Rob Bygrave 93855478dd Fix for #128 storing UUID as binary 2014-05-17 21:52:18 +12:00
Rob Bygrave 59b86d5b19 Fix for #128 - UUID stored as binary(16) rather than varchar(40) 2014-05-17 21:51:18 +12:00
Rob Bygrave 27afb9ee2e Merge pull request #41 from corux/master
Unit test for BUG 402 / #37
2014-05-17 01:31:21 +12:00
Rob Bygrave a49cd42a0c Fix for #119 - Add support for @PrePersist, @PostPersist, @PostLoad 2014-05-17 01:24:32 +12:00
Rob Bygrave 8c8442ec58 Fix for #112 - ENH (411) : RawSql to support discriminator column mapping. 2014-05-17 00:11:44 +12:00
Rob Bygrave 41f3173adf Add PagedList was part of #96 2014-05-16 01:41:56 +12:00
Rob Bygrave 1a4192b40f Add another embedded bean l2 cache test 2014-05-15 23:57:20 +12:00
Rob Bygrave fffda7c789 Test and additional fix for #56 - Using findIterate with MySQL streams
the entire result set at once
2014-05-15 22:08:23 +12:00
Rob Bygrave b7f6596bcb Test case for #109 - Ebean magically adding +1 hour for a datetime that is inside the hour of a daylight saving change. This issue does not reproduce. 2014-05-15 00:18:03 +12:00
Rob Bygrave 1ea49dd206 Test for #110 - BeanSet of sub type with inheritance has null listen mode 2014-05-15 00:07:25 +12:00
Rob Bygrave 32e4f114b4 No effective change - cleanup of imports on test code 2014-05-14 23:47:46 +12:00
Rob Bygrave fcbedda597 Fix for #102 - When using @Transient along with @Formula I'm not seeing any values in the field. 2014-05-14 23:35:15 +12:00
Rob Bygrave 2055349d76 Fix for #103 DDL creation doesn't handle nullable attribute of @JoinColumn 2014-05-14 23:21:31 +12:00
Rob Bygrave 44ee64cde5 Fix for #57 - Embedded Entities with Autofetch Exception, plus some additional tests 2014-05-14 23:13:14 +12:00
Rob Bygrave 9f916998c9 Test and Fix for #127 Exception using findIds() with inheritance
SINGLE_TABLE
2014-05-14 21:10:52 +12:00
Rob Bygrave 2ddef37632 Merge pull request #58 from mbell697/autofetch-embedded
Failing test case for issue #57
2014-05-01 00:21:59 +12:00
Rob Bygrave 538ebdc899 Fix for #122 Allow the global row limit for queries to be configurable ( CQuery.GLOBAL_ROW_LIMIT) 2014-05-01 00:12:48 +12:00
Rob Bygrave d6e0d661fb no effective change - javadoc update 2014-05-01 00:11:59 +12:00
Rob Bygrave 289deb397a Embedded beans support in l2 cache - initial work 2014-04-30 01:49:20 +12:00
Rob Bygrave 68b4aab837 Add test for old (427) : Binding enum on find throws No ScalarType registered 2014-04-30 00:08:23 +12:00
Rob Bygrave 563293f7a9 Test case and fix for #113 (395) : On save @OneToOne does not cascade parent IDs to child node 2014-04-30 00:01:54 +12:00
Rob Bygrave 5049694fd3 Add test for old 408 bug - does not reproduce, not sure when fixed at this point 2014-04-29 23:19:03 +12:00
Rob Bygrave a9c8d1960c Add test - self referencing relationships: ResourceFile 2014-04-29 22:25:14 +12:00
Rob Bygrave 066b409a65 No effective change -Add test: Truck, Car, Wheel plus test code cleanup 2014-04-29 22:00:07 +12:00
Rob Bygrave 54ad2e0ad0 remove the Object[] transient from the test 2014-04-29 21:21:44 +12:00
Rob Bygrave 77f7bc2a7e Fix for #111 - Ebean doesn't honor @Transient for non-primitive array properties 2014-04-29 21:20:42 +12:00
Rob Bygrave f773d74597 Test for #105 - did not reproduce - Ebean is trying to delete new
objects added to the list of an entity and then removed without being
persisted before
2014-04-29 20:42:51 +12:00
Rob Bygrave 90b23c4985 Bump version of agent and mavenenhancer 2014-04-29 20:36:00 +12:00
Rob Bygrave 60b4ad410b [maven-release-plugin] prepare for next development iteration 2014-04-27 01:59:10 +12:00
Rob Bygrave 56215aafa3 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.1-RC1 2014-04-27 01:58:45 +12:00
Rob Bygrave 5ba66e4d78 #96 - marking findPagingList() as deprecated 2014-04-27 01:52:44 +12:00
Rob Bygrave 0eb7139426 Fix for #95 - Deprecating - Query findFutureList() and SqlQuery findFutureList() 2014-04-27 01:44:26 +12:00
Rob Bygrave d16d92db54 Test case and Fix for #67 - Bug - @AttributeOverrides annotation for attribute definition in entity does not work for embedded primary keys 2014-04-27 00:57:29 +12:00
Rob Bygrave dde59db411 Test case for #75 - Persistence Context Cache not cleared when updating an entity in a running transaction. Actually the test case does not fail. 2014-04-27 00:05:00 +12:00
Rob Bygrave 1a13fc9742 Fixes for #94 - Additional API - added update(Collection beans); and insert(Collection beans); and #93 - ebean.properties defaultDeleteMissingChildren moved to updatesDeleteMissingChildren 2014-04-26 23:43:08 +12:00
Rob Bygrave e1ec0bcdfe Test and Fix for #39 - TestOnCascadeDeleteChildrenWithCompositeKeys from hei1233212000, with fix included 2014-04-25 22:34:03 +12:00
Rob Bygrave 27803afe64 Test and Fix for #39 - TestOnCascadeDeleteChildrenWithCompositeKeys from hei1233212000, with fix included 2014-04-25 22:32:33 +12:00
Rob Bygrave c011dab3e2 Change to use released 4.1.0 version of agent and maven enhancer 2014-04-25 19:58:41 +12:00
Rob Bygrave fb7abb44e0 Add a test for enhancement equals() and hashCode() 2014-04-25 19:23:56 +12:00
Rob Bygrave 14034ffaa0 Change to use enhancement for creating new entity bean instances 2014-04-25 15:54:07 +12:00
Rob Bygrave 75bcbc06e1 #53 - Add test for protected constructor 2014-04-25 15:19:50 +12:00
Rob Bygrave bc75736780 merge of develop-v4 2014-04-25 14:36:06 +12:00
Rob Bygrave ed0fe7fd57 Caching changes and refactoring, includes Fix for Wobu - TestCacheCollectionIds test case 2014-04-25 03:06:22 +12:00
wbuchnerandRob Bygrave 696dabc658 Add Wobu TestCacheDelete test case for cached beans 2014-04-24 02:42:28 +12:00
Wolfgang BuchnerandRob Bygrave 89f7d2bace Test and fix for TestStatelessUpdate.testStatelessRecursiveUpdateWithVersionField() 2014-04-24 02:34:01 +12:00
Rob Bygrave 4c4e084e43 WIP update/refactoring on caching 2014-04-24 01:57:52 +12:00
Wolfgang BuchnerandRob Bygrave 2dccb818a2 unit test for: update ManyToMany collection cache on updating cached beans 2014-04-23 21:19:06 +12:00
Rob Bygrave a7df7da984 Fix #23 - Covers: unit tests for: stateless updates which fails when executing without... 2014-04-23 21:16:30 +12:00
Wolfgang BuchnerandRob Bygrave b012720a5e unit test for: ensuring many relations won't be deleted on a partial stateless update when using BeanPersistAdapter 2014-04-23 03:29:10 +12:00
Rob Bygrave 1451ec0084 Fix for TestStatelessUpdate.testWithoutChangesAndVersionColumnAndIgnoreNullValues 2014-04-23 03:27:39 +12:00
Wolfgang BuchnerandRob Bygrave 0d2101ca7c unit tests for: stateless updates which fails when executing without changes 2014-04-23 03:05:16 +12:00
Wolfgang BuchnerandRob Bygrave a22496a57d added intelliJ files to gitignore 2014-04-23 03:00:00 +12:00
Rob Bygrave 654b10154e Fix for #83 - Bug: Stateless update cascading to OneToOne or ManyToOne incorrectly tries to INSERT rather than UPDATE 2014-04-23 02:31:08 +12:00
Rob Bygrave f4357c2767 Add test for reference on bean with constructor setting properties 2014-04-23 02:01:27 +12:00
Rob Bygrave 0d1c9e2883 Fix for #91 Stateless Update API Change, #90 Removed BeanState.setReference(), #89 Remove support for multiple @Id properties 2014-04-23 01:51:27 +12:00
Rob Bygrave 5e2d592c2a v4 refactor for diff() support 2014-04-21 12:34:51 +12:00
Rob Bygrave 2344f57f84 #88 - v4 - Breaking API Change - BeanPersistRequest.getOldValues() replaced with ... Map<String,ValuePair> getUpdatedValues() 2014-04-20 22:52:02 +12:00
Rob Bygrave fc628d336d Fix for #87 - v4 - Remove Query setBackgroundFetchAfter() feature 2014-04-20 19:30:40 +12:00
Rob Bygrave 18d9c53ce3 Fix for #86 - v4 - Remove deprecated QueryListener. You must convert to findIterate() or findVisit() 2014-04-20 17:18:13 +12:00
Rob Bygrave 811d298355 WIP - rename EntityBeanIntercept lazy loading property methods 2014-04-20 17:01:36 +12:00
Rob Bygrave f7a37537db WIP update api 2014-04-20 16:57:14 +12:00
Rob Bygrave 0c9d95619b [maven-release-plugin] prepare for next development iteration 2014-04-03 23:19:03 +13:00
Rob Bygrave 96e4b4640f Merge in v4 code changes 2014-04-03 03:47:55 +13:00
Rob Bygrave a3c8ed88ca Update pom to 4.0.1-RC1-SNAPSHOT 2014-04-03 02:19:16 +13:00
Mark Bell d8391ba90c Test embedded fetch also as this throws the same exception 2013-11-11 13:23:53 -05:00
Mark Bell c4d41ecb00 Failing test case for auto fetch + embedded exception 2013-11-11 13:13:46 -05:00
Tobias Wallura 39f5284a76 unit test for issue #37 2013-07-10 10:24:03 +02:00
Robin Bygrave 4ec62a5772 initial rework for new enhancement 2013-06-19 20:36:24 +12:00
Robin Bygrave eb1eafe98c WIP - Initial enhancement API change 2013-05-01 14:46:55 +12:00
353 changed files with 12777 additions and 8795 deletions
+7
View File
@@ -7,3 +7,10 @@ target/
logs/
log/
/mydb.db
# Intellij project files
*.iml
*.ipr
*.iws
.idea/
+2 -4
View File
@@ -1,14 +1,12 @@
avaje-ebeanorm
==============
Main EbeanORM artifact
Release - 4.0.2 - May 19th: https://github.com/ebean-orm/avaje-ebeanorm/wiki/4.0.2-Release
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.3.2</version>
<version>4.0.2</version>
</dependency>
+20 -4
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.3.2</version>
<version>4.0.3</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -94,7 +94,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>3.2.2</version>
<version>4.1.2</version>
<scope>test</scope>
</dependency>
@@ -136,9 +136,16 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-integration</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
@@ -167,10 +174,19 @@
<!-- Enhance the meta beans -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>3.3.2</version>
<version>4.1.2</version>
<executions>
<execution>
<id>main</id>
+7 -11
View File
@@ -1,6 +1,7 @@
package com.avaje.ebean;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.Set;
/**
@@ -46,6 +47,11 @@ public interface BeanState {
*/
public Set<String> getChangedProps();
/**
* Return a map of the updated properties and their new and old values.
*/
public Map<String,ValuePair> getDirtyValues();
/**
* Return true if the bean is readOnly.
* <p>
@@ -69,16 +75,6 @@ public interface BeanState {
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Advanced - Used to programmatically build a reference object.
* <p>
* You can create a new EntityBean (
* {@link EbeanServer#createEntityBean(Class)}, set its Id property and then
* call this setReference() method.
* </p>
*/
public void setReference();
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
@@ -90,5 +86,5 @@ public interface BeanState {
* the properties that where loaded or null for a fully loaded entity
* bean.
*/
public void setLoaded(Set<String> loadedProperties);
public void setLoaded();
}
+62 -58
View File
@@ -5,20 +5,20 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This Ebean object is effectively a singleton that holds a map of registered
@@ -214,14 +214,13 @@ public final class Ebean {
* Register a server so we can get it by its name.
*/
private void register(EbeanServer server, boolean isPrimaryServer) {
registerWithName(server.getName(), server, isPrimaryServer);
}
private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) {
synchronized (monitor) {
concMap.put(server.getName(), server);
EbeanServer existingServer = syncMap.put(server.getName(), server);
if (existingServer != null) {
String msg = "Existing EbeanServer [" + server.getName() + "] is being replaced?";
logger.warn(msg);
}
concMap.put(name, server);
syncMap.put(name, server);
if (isPrimaryServer) {
primaryServer = server;
}
@@ -286,6 +285,15 @@ public final class Ebean {
serverMgr.register(server, isPrimaryServer);
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default server.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) {
EbeanServer originalPrimaryServer = serverMgr.primaryServer;
serverMgr.registerWithName(name, server, isPrimaryServer);
return originalPrimaryServer;
}
/**
* Return the next identity value for a given bean type.
* <p>
@@ -454,69 +462,65 @@ public final class Ebean {
}
/**
* Force an update using the bean updating the non-null properties.
* Insert the bean. This is useful when you set the Id property on a bean and
* want to explicitly insert it.
*/
public static void insert(Object bean) {
serverMgr.getPrimaryServer().insert(bean);
}
/**
* Insert a collection of beans.
*/
public static void insert(Collection<?> beans) {
serverMgr.getPrimaryServer().insert(beans);
}
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link Ebean#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
* update().You can create a new instance and set some of its properties programmatically for via
* JSON/XML marshalling etc. This is described as a 'stateless update'.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
*
* <pre class="code">
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
*
* // generally you should set the version property
* // so that Optimistic Concurrency Checking is used.
* // If a version property is not set then no Optimistic
* // Concurrency Checking occurs for the update
* // c.setLastUpdate(lastUpdateTime);
*
* // by default the Non-null properties
* // are included in the update
* Ebean.update(c);
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName(&quot;ModifiedNameNoOCC&quot;);
* ebeanServer.update(customer);
*
* </pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public static void update(Object bean) {
public static void update(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().update(bean);
}
/**
* Force an update using the bean explicitly stating the properties to update.
* <p>
* If you don't specify explicit properties to use in the update then the
* non-null properties are included in the update.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
*
* @param bean
* The bean holding the values to be included in the update.
* @param updateProps
* the explicit set of properties to include in the update (can be
* null).
* Update the beans in the collection.
*/
public static void update(Object bean, Set<String> updateProps) {
serverMgr.getPrimaryServer().update(bean, updateProps);
public static void update(Collection<?> beans) throws OptimisticLockException {
serverMgr.getPrimaryServer().update(beans);
}
/**
@@ -529,8 +533,8 @@ public final class Ebean {
/**
* Save all the beans from a Collection.
*/
public static int save(Collection<?> c) throws OptimisticLockException {
return save(c.iterator());
public static int save(Collection<?> beans) throws OptimisticLockException {
return serverMgr.getPrimaryServer().save(beans);
}
/**
+119 -175
View File
@@ -85,9 +85,12 @@ import com.avaje.ebean.text.json.JsonContext;
public interface EbeanServer {
/**
* Shutdown the EbeanServer.
* Shutdown the EbeanServer programmatically.
* <p>
* If the under underlying DataSource is the EbeanORM implementation then you
* This method is not normally required. Ebean registers a shutdown hook and shuts down cleanly.
* </p>
* <p>
* If the under underlying DataSource is the Ebean implementation then you
* also have the option of shutting down the DataSource and deregistering the
* JDBC driver.
* </p>
@@ -410,12 +413,25 @@ public interface EbeanServer {
public <T> T find(Class<T> beanType, Object uid);
/**
* Get a reference Object (see {@link Ebean#getReference(Class, Object)}.
* Get a reference bean (see {@link Ebean#getReference(Class, Object)}.
* <p>
* This will not perform a query against the database.
* </p>
* <pre class="code">
* Product product = Ebean.getReference(Product.class, 1);
*
* @see Ebean#getReference(Class, Object)
* // 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>
*
* @param beanType
* the type of entity bean
* @param id
* the id value
*/
public <T> T getReference(Class<T> beanType, Object uid);
@@ -428,21 +444,21 @@ public interface EbeanServer {
/**
* Return the Id values of the query as a List.
*/
public <T> List<Object> findIds(Query<T> query, Transaction t);
public <T> List<Object> findIds(Query<T> query, Transaction transaction);
/**
* Return a QueryIterator for the query. This is similar to findVisit 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.
*/
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t);
public <T> QueryIterator<T> findIterate(Query<T> query, 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.
*/
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t);
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction transaction);
/**
* Execute a query returning a list of beans.
@@ -477,7 +493,7 @@ public interface EbeanServer {
* the transaction (can be null).
* @return a Future object for the row count query
*/
public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction t);
public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction transaction);
/**
* Execute find Id's query in a background thread.
@@ -493,7 +509,7 @@ public interface EbeanServer {
* the transaction (can be null).
* @return a Future object for the list of Id's
*/
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t);
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
/**
* Execute find list query in a background thread.
@@ -508,8 +524,9 @@ public interface EbeanServer {
* @param t
* the transaction (can be null).
* @return a Future object for the list result of the query
* @deprecated
*/
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t);
public <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
/**
* Execute find list SQL query in a background thread.
@@ -524,13 +541,37 @@ public interface EbeanServer {
* @param t
* the transaction (can be null).
* @return a Future object for the list result of the query
* @deprecated
*/
public SqlFutureList findFutureList(SqlQuery query, Transaction t);
public SqlFutureList findFutureList(SqlQuery query, Transaction transaction);
/**
* Find using a PagingList with explicit transaction and pageSize.
* Deprecated in favour of findPagedList().
* @deprecated
*/
public <T> PagingList<T> findPagingList(Query<T> query, Transaction transaction, int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public <T> PagingList<T> findPagingList(Query<T> query, Transaction t, int pageSize);
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize);
/**
* Execute the query returning a set of entity beans.
@@ -677,7 +718,7 @@ public interface EbeanServer {
/**
* Save all the beans in the collection.
*/
public int save(Collection<?> it) throws OptimisticLockException;
public int save(Collection<?> beans) throws OptimisticLockException;
/**
* Delete the bean.
@@ -704,7 +745,7 @@ public interface EbeanServer {
/**
* Delete the bean given its type and id with an explicit transaction.
*/
public int delete(Class<?> beanType, Object id, Transaction t);
public int delete(Class<?> beanType, Object id, Transaction transaction);
/**
* Delete several beans given their type and id values.
@@ -715,7 +756,7 @@ public interface EbeanServer {
* Delete several beans given their type and id values with an explicit
* transaction.
*/
public void delete(Class<?> beanType, Collection<?> ids, Transaction t);
public void delete(Class<?> beanType, Collection<?> ids, Transaction transaction);
/**
* Execute a SQL Update Delete or Insert statement using the current
@@ -784,205 +825,107 @@ public interface EbeanServer {
/**
* Insert or update a bean with an explicit transaction.
*/
public void save(Object bean, Transaction t) throws OptimisticLockException;
public void save(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Save all the beans in the iterator with an explicit transaction.
*/
public int save(Iterator<?> it, Transaction t) throws OptimisticLockException;
public int save(Iterator<?> it, Transaction transaction) throws OptimisticLockException;
/**
* Force an update using the bean.
* Save all the beans in the collection with an explicit transaction.
*/
public int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
* update().You can create a new instance and set some of its properties programmatically for via
* JSON/XML marshalling etc. This is described as a 'stateless update'.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
*
* <pre class="code">
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
*
* // generally you should set the version property
* // so that Optimistic Concurrency Checking is used.
* // If a version property is not set then no Optimistic
* // Concurrency Checking occurs for the update
* // c.setLastUpdate(lastUpdateTime);
*
* // by default the Non-null properties
* // are included in the update
* ebeanServer.update(c);
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName(&quot;ModifiedNameNoOCC&quot;);
* ebeanServer.update(customer);
*
* </pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public void update(Object bean);
public void update(Object bean) throws OptimisticLockException;
/**
* Force an update of the non-null properties of the bean with an explicit
* transaction.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
* Update a bean additionally specifying a transaction.
*/
public void update(Object bean, Transaction t);
public void update(Object bean, Transaction t) throws OptimisticLockException;
/**
* Force an update using the bean explicitly stating the properties to update.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
*
* <pre class="code">
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
*
* // generally you should set the version property
* // so that Optimistic Concurrency Checking is used.
* // If a version property is not set then no Optimistic
* // Concurrency Checking occurs for the update
* // c.setLastUpdate(lastUpdateTime);
*
* // by default the Non-null properties
* // are included in the update
* ebeanServer.update(c);
*
* </pre>
*/
public void update(Object bean, Set<String> updateProps);
/**
* Force an update of the specified properties of the bean with an explicit
* transaction.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
*/
public void update(Object bean, Set<String> updateProps, Transaction t);
/**
* Force an update additionally specifying whether to 'deleteMissingChildren'
* when the update cascades to a OneToMany or ManyToMany.
* <p>
* By default the deleteMissingChildren is true and it is assumed that when
* cascade saving a O2M or M2M relationship that the relationship is 'fully
* loaded' and any child beans that are no longer on the relationship will be
* deleted.
* </p>
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
*
* @param bean
* the bean to update
* @param updateProps
* optionally you can specify the properties to update (can be null).
* @param t
* optionally you can specify the transaction to use (can be null).
* @param transaction
* the transaction to use (can be null).
* @param deleteMissingChildren
* specify false if you do not want 'missing children' of a OneToMany
* or ManyToMany to be automatically deleted.
* @param updateNullProperties
* specify true if by default you want properties with null values to
* be included in the update and false if those properties should be
* treated as 'unloaded' and excluded from the update. This only
* takes effect if the updateProps is null.
*/
public void update(Object bean, Set<String> updateProps, Transaction t,
boolean deleteMissingChildren, boolean updateNullProperties);
public void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;
/**
* Force the bean to be saved with an explicit insert.
* <p>
* Typically you would use save() and let Ebean determine if the bean should
* be inserted or updated. This can be useful when you are transferring data
* between databases and want to explicitly insert a bean into a different
* database that it came from.
* </p>
* Update a collection of beans. If there is no current transaction one is created and used to
* update all the beans in the collection.
*/
public void update(Collection<?> beans) throws OptimisticLockException;
/**
* Update a collection of beans with an explicit transaction.
*/
public void update(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Insert the bean.
*/
public void insert(Object bean);
/**
* Force the bean to be saved with an explicit insert.
* <p>
* Typically you would use save() and let Ebean determine if the bean should
* be inserted or updated. This can be useful when you are transferring data
* between databases and want to explicitly insert a bean into a different
* database that it came from.
* </p>
* Insert the bean with a transaction.
*/
public void insert(Object bean, Transaction t);
/**
* Insert a collection of beans. If there is no current transaction one is created and used to
* insert all the beans in the collection.
*/
public void insert(Collection<?> beans);
/**
* Insert a collection of beans with an explicit transaction.
*/
public void insert(Collection<?> beans, Transaction t);
/**
* Delete the associations (from the intersection table) of a ManyToMany given
* the owner bean and the propertyName of the ManyToMany collection.
@@ -1158,4 +1101,5 @@ public interface EbeanServer {
*/
public JsonContext createJsonContext();
}
@@ -200,10 +200,34 @@ public interface ExpressionList<T> extends Serializable {
*
* @param pageSize
* the number of beans fetched per Page
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Add some filter predicate expressions to the many property.
*/
public ExpressionList<T> filterMany(String prop);
/**
@@ -244,14 +268,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMaxRows(int maxRows);
/**
* Set the number of rows after which the fetching should continue in a
* background thread.
*
* @see Query#setBackgroundFetchAfter(int)
*/
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the name of the property which values become the key of a map.
*
@@ -259,15 +275,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMapKey(String mapKey);
/**
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
* Set a QueryListener for bean by bean processing.
*
* @see Query#setListener(QueryListener)
* @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set to true to use the query for executing this query.
*
+543
View File
@@ -0,0 +1,543 @@
package com.avaje.ebean;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.MappedSuperclass;
/**
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
* deleting beans.
*
* <p>
* By having your entity beans extend this it provides a 'Active Record' style programming model for
* Ebean users.
*
* <p>
* Note that there is a avaje-ebeanorm-mocker project that enables you to use Mockito or similar
* tools to still mock out the underlying 'default EbeanServer' for testing purposes.
*
* <p>
* You may choose not use this Model mapped superclass if you don't like the 'Active Record' style
* or if you believe it 'pollutes' your entity beans.
*
* <p>
* If you choose to use the Model mapped superclass you will probably also chose to additionally add
* a {@link Finder} as a public static field to complete the active record pattern and provide a
* relatively nice clean way to write queries.
*/
@MappedSuperclass
public class Model {
/**
* Return the underlying 'default' EbeanServer.
*
* <p>
* This provides full access to the API such as explicit transaction demarcation etc.
*
* <p>
* TODO: Transaction example
*/
public EbeanServer db() {
return Ebean.getServer(null);
}
/**
* Return a named EbeanServer that is typically different to the default server.
*
* <p>
* If you are using multiple databases then each database has a name and maps to a single
* EbeanServer. You can use this method to get an EbeanServer for another database.
*
* @param server
* The name of the EbeanServer. If this is null then the default EbeanServer is returned.
*/
public EbeanServer db(String server) {
return Ebean.getServer(server);
}
/**
* Insert or update this entity depending on its state.
*
* <p>
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
* insert or an update based on that.
*/
public void save() {
db().save(this);
}
/**
* Update this entity.
*/
public void update() {
db().update(this);
}
/**
* Insert this entity.
*/
public void insert() {
db().insert(this);
}
/**
* Delete this entity.
*/
public void delete() {
db().delete(this);
}
/**
* Perform an update using this entity against the specified server.
*/
public void update(String server) {
db(server).update(this);
}
/**
* Perform an insert using this entity against the specified server.
*/
public void insert(String server) {
db(server).insert(this);
}
/**
* Perform a delete using this entity against the specified server.
*/
public void delete(String server) {
db(server).delete(this);
}
/**
* Refreshes this entity from the database.
*/
public void refresh() {
db().refresh(this);
}
/**
* Helper object for performing queries.
*
* <p>
* 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
* The Id type. This is most often a {@link Long} but is also often a {@link UUID} or
* {@link String}.
*
* @param T
* The bean type
*/
public static class Finder<I, T> {
private final Class<I> idType;
private final Class<T> type;
private final String serverName;
/**
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>.
*
* <p>
* Typically you use this constructor to have a static "find" field on each entity bean.
*/
public Finder(Class<I> idType, Class<T> type) {
this(null, idType, type);
}
/**
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>, using a
* specific EbeanServer.
*
* <p>
* Typically you don't need to use this method.
*/
public Finder(String serverName, Class<I> idType, Class<T> type) {
this.type = type;
this.idType = idType;
this.serverName = serverName;
}
/**
* Return the underlying 'default' EbeanServer.
*
* <p>
* This provides full access to the API such as explicit transaction demarcation etc.
*
*/
public EbeanServer db() {
return Ebean.getServer(serverName);
}
/**
* Return typically a different EbeanServer to the default.
* <p>
* This is equivilent to {@link Ebean#getServer(String)}
*
* @param server
* The name of the EbeanServer. If this is null then the default EbeanServer is
* returned.
*/
public EbeanServer db(String server) {
return Ebean.getServer(server);
}
/**
* Creates a Finder for the named EbeanServer.
*
* <p>
* Create and return a new Finder for a different server.
*/
public Finder<I, T> on(String server) {
return new Finder<I, T>(server, idType, type);
}
// Does not exist yet but I think should
// public void deleteById(I id) {
// return db().deleteById(type, id);
// }
/**
* Retrieves all entities of the given type.
*
* <p>
* This is the same as (synonym for) {@link #findList()}
*/
public List<T> all() {
return findList();
}
/**
* Retrieves an entity by ID.
*
* <p>
* Equivilent to {@link EbeanServer#find(Class, Object)}
*/
public T byId(I id) {
return db().find(type, id);
}
/**
* Creates an entity reference for this ID.
*
* <p>
* Equivilent to {@link EbeanServer#getReference(Class, Object)}
*/
public T ref(I id) {
return db().getReference(type, id);
}
/**
* Creates a filter for sorting and filtering lists of entities locally without going back to
* the database.
* <p>
* Equivilent to {@link EbeanServer#filter(Class)}
*/
public Filter<T> filter() {
return db().filter(type);
}
/**
* Creates a query.
* <p>
* Equivilent to {@link EbeanServer#find(Class)}
*/
public Query<T> query() {
return db().find(type);
}
/**
* Returns the next identity value.
*
* @see EbeanServer#nextId(Class)
*/
@SuppressWarnings("unchecked")
public I nextId() {
return (I) db().nextId(type);
}
/**
* Executes a query and returns the results as a list of IDs.
* <p>
* Equivilent to {@link Query#findIds()}
*/
public List<Object> findIds() {
return query().findIds();
}
/**
* Retrieves all entities of the given type.
* <p>
* The same as {@link #all()}
* <p>
* Equivilent to {@link Query#findList()}
*/
public List<T> findList() {
return query().findList();
}
/**
* Returns all the entities of the given type as a set.
* <p>
* Equivilent to {@link Query#findSet()}
*/
public Set<T> findSet() {
return query().findSet();
}
/**
* Retrieves all entities of the given type as a map of objects.
* <p>
* Equivilent to {@link Query#findMap()}
*/
public Map<?, T> findMap() {
return query().findMap();
}
/**
* 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)}
*/
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
return query().findMap(keyProperty, keyType);
}
/**
* 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)}
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return query().findPagedList(pageIndex, pageSize);
}
/**
* Executes a find row count query in a background thread.
* <p>
* Equivilent to {@link Query#findFutureRowCount()}
*/
public FutureRowCount<T> findFutureRowCount() {
return query().findFutureRowCount();
}
/**
* Returns the total number of entities for this type. *
* <p>
* Equivilent to {@link Query#findRowCount()}
*/
public int findRowCount() {
return query().findRowCount();
}
/**
* Returns the <code>ExpressionFactory</code> used by this query.
*/
public ExpressionFactory getExpressionFactory() {
return query().getExpressionFactory();
}
/**
* 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)}
*/
public Query<T> select(String fetchProperties) {
return query().select(fetchProperties);
}
/**
* Specifies a path to load including all its properties.
* <p>
* Equivilent to {@link Query#fetch(String)}
*/
public Query<T> fetch(String path) {
return query().fetch(path);
}
/**
* 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)}
*/
public Query<T> fetch(String path, FetchConfig joinConfig) {
return query().fetch(path, joinConfig);
}
/**
* 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)}
*/
public Query<T> fetch(String path, String fetchProperties) {
return query().fetch(path, fetchProperties);
}
/**
* 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)}
*/
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) {
return query().fetch(assocProperty, fetchProperties, fetchConfig);
}
/**
* Adds expressions to the <code>where</code> clause with the ability to chain on the
* <code>ExpressionList</code>.
* <p>
* Equivilent to {@link Query#where()}
*/
public ExpressionList<T> where() {
return query().where();
}
/**
* Returns the <code>order by</code> clause so that you can append an ascending or descending
* property to the <code>order by</code> clause.
* <p>
* This is exactly the same as {@link #orderBy}.
* <p>
* Equivilent to {@link Query#order()}
*/
public OrderBy<T> order() {
return query().order();
}
/**
* Sets the <code>order by</code> clause, replacing the existing <code>order by</code> clause if
* there is one.
* <p>
* This is exactly the same as {@link #orderBy(String)}.
*/
public Query<T> order(String orderByClause) {
return query().order(orderByClause);
}
/**
* Returns the <code>order by</code> clause so that you can append an ascending or descending
* property to the <code>order by</code> clause.
* <p>
* This is exactly the same as {@link #order}.
* <p>
* Equivilent to {@link Query#orderBy()}
*/
public OrderBy<T> orderBy() {
return query().orderBy();
}
/**
* Set the <code>order by</code> clause replacing the existing <code>order by</code> clause if
* there is one.
* <p>
* This is exactly the same as {@link #order(String)}.
*/
public Query<T> orderBy(String orderByClause) {
return query().orderBy(orderByClause);
}
/**
* Sets the first row to return for this query.
* <p>
* Equivilent to {@link Query#setFirstRow(int)}
*/
public Query<T> setFirstRow(int firstRow) {
return query().setFirstRow(firstRow);
}
/**
* Sets the maximum number of rows to return in the query.
* <p>
* Equivilent to {@link Query#setMaxRows(int)}
*/
public Query<T> setMaxRows(int maxRows) {
return query().setMaxRows(maxRows);
}
/**
* Sets the ID value to query.
*
* <p>
* 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)}
*/
public Query<T> setId(Object id) {
return query().setId(id);
}
/**
* Create and return a new query using the OQL.
* <p>
* Equivilent to {@link EbeanServer#createQuery(Class, String)}
*/
public Query<T> setQuery(String oql) {
return db().createQuery(type, oql);
}
/**
* Create and return a new query based on the <code>RawSql</code>.
* <p>
* Equivilent to {@link Query#setRawSql(RawSql)}
*/
public Query<T> setRawSql(RawSql rawSql) {
return query().setRawSql(rawSql);
}
/**
* Create a query with explicit 'Autofetch' use.
*/
public Query<T> setAutofetch(boolean autofetch) {
return query().setAutofetch(autofetch);
}
/**
* Create a query with the select with "for update" specified.
*
* <p>
* This will typically create row level database locks on the selected rows.
*/
public Query<T> setForUpdate(boolean forUpdate) {
return query().setForUpdate(forUpdate);
}
/**
* Create a query specifying whether the returned beans will be read-only.
*/
public Query<T> setReadOnly(boolean readOnly) {
return query().setReadOnly(readOnly);
}
/**
* Create a query specifying if the beans should be loaded into the L2 cache.
*/
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
return query().setLoadBeanCache(loadBeanCache);
}
/**
* Create a query specifying if the L2 bean cache should be used.
*/
public Query<T> setUseCache(boolean useBeanCache) {
return query().setUseCache(useBeanCache);
}
/**
* Create a query specifying if the L2 query cache should be used.
*/
public Query<T> setUseQueryCache(boolean useQueryCache) {
return query().setUseQueryCache(useQueryCache);
}
}
}
@@ -0,0 +1,80 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* Represents a page of results.
* <p>
* The benefit of using PagedList over just using the normal Query with
* {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps
* functionality that can call {@link Query#findFutureRowCount()} to determine total row count,
* total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
* limit the result set.
* </p>
*
* @param <T>
* the entity bean type
*
* @see Query#findPagedList(int, int)
*/
public interface PagedList<T> {
/**
* Initiate the loading of the total row count in the background.
*/
public void loadRowCount();
/**
* Return the Future row count. You might get this if you wish to cancel the total row count query
* or specify a timeout for that query.
*/
public Future<Integer> getFutureRowCount();
/**
* Return the list of entities for this page.
*/
public List<T> getList();
/**
* Return the total row count for all pages.
*/
public int getTotalRowCount();
/**
* Return the total number of pages based on the page size and total row count.
*/
public int getTotalPageCount();
/**
* Return the index position of this page. Zero based.
*/
public int getPageIndex();
/**
* Return true if there is a next page.
*/
public boolean hasNext();
/**
* Return true if there is a previous page.
*/
public boolean hasPrev();
/**
* Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
* last row and Z the total row count.
*
* @param to
* String to put between the first and last row
* @param of
* String to put between the last row and the total row count
*
* @return String of the format XtoYofZ.
*/
public String getDisplayXtoYofZ(String to, String of);
}
+25 -54
View File
@@ -589,34 +589,42 @@ public interface Query<T> extends Serializable {
/**
* Execute find list query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
* Deprecated with a view to simplifying internals.
* </p>
*
* @return a Future object for the list result of the query
* @deprecated
*/
public FutureList<T> findFutureList();
/**
* Return a PagingList for this query.
* <p>
* This can be used to break up a query into multiple queries to fetch the
* data a page at a time.
* </p>
* <p>
* This typically works by using a query per page and setting
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
* query. This usually would translate into SQL that uses limit offset, rownum
* or row_number function to limit the result set.
* </p>
*
* @param pageSize
* the number of beans fetched per Page
* This is being deprecated in favour of the simplier {@link Query#findPagedList(int, int)}.
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
* limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Set a named bind parameter. Named parameters have a colon to prefix the
* name.
@@ -663,36 +671,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setParameter(int position, Object value);
/**
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
* <p>
* Set a listener to process the query on a row by row basis.
* </p>
* <p>
* Use this when you want to process a large query and do not want to hold the
* entire query result in memory.
* </p>
* <p>
* It this case the rows are not loaded into the persistence context and
* instead are processed by the query listener.
* </p>
*
* <pre class="code">
* QueryListener&lt;Order&gt; listener = ...;
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
*
* // set the listener that will process each order one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list (emptyList) will be empty ...
* List&lt;Order&gt; emtyList = query.findList();
* </pre>
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set the Id value to query. This is used with findUnique().
* <p>
@@ -966,13 +944,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setMaxRows(int maxRows);
/**
* Set the rows after which fetching should continue in a background thread.
*
* @param backgroundFetchAfter
*/
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the property to use as keys for a map.
* <p>
@@ -1,43 +0,0 @@
package com.avaje.ebean;
/**
* Deprecated, please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
* <p>
* Provides a mechanism for processing a query one bean at a time.
* </p>
* <p>
* This is useful when the query will return a large number of results and you
* want to process the beans one at a time rather than whole all of the beans in
* memory at once.
* </p>
*
* <pre class="code">
* QueryListener&lt;Order&gt; listener = ...;
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
*
* // set the listener that will process each order one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list will be empty ... so don't bother assigning it
* query.findList();
* </pre>
*
* @param <T>
* the type of entity bean
* @deprecated Please migrate to using {@link #findIterate()} or
* {@link #findVisit(QueryResultVisitor)}
*/
public interface QueryListener<T> {
/**
* Process the bean that has just been read.
* <p>
* This bean will not be added to the List Set or Map and nor will it be put
* into the PersistenceContext. This is what makes this a good way to process
* a large result set (which could normally use a lot of memory).
* </p>
*/
public void process(T bean);
}
@@ -79,6 +79,7 @@ public interface SqlQuery extends Serializable {
* </p>
*
* @return a Future object for the list result of the query
* @deprecated
*/
public SqlFutureList findFutureList();
+26 -10
View File
@@ -5,30 +5,46 @@ package com.avaje.ebean;
*/
public class ValuePair {
final Object value1;
private final Object newValue;
final Object value2;
private final Object oldValue;
public ValuePair(Object value1, Object value2) {
this.value1 = value1;
this.value2 = value2;
public ValuePair(Object newValue, Object oldValue) {
this.newValue = newValue;
this.oldValue = oldValue;
}
/**
* Return the first value.
* Return the new value.
*/
public Object getNewValue() {
return newValue;
}
/**
* Return the old value.
*/
public Object getOldValue() {
return oldValue;
}
/**
* Return the new value.
*/
@Deprecated
public Object getValue1() {
return value1;
return newValue;
}
/**
* Return the second value.
* Return the old value.
*/
@Deprecated
public Object getValue2() {
return value2;
return oldValue;
}
public String toString() {
return value1 + "," + value2;
return newValue + "," + oldValue;
}
}
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
/**
* Use a version column.
*/
VERSION,
/**
* Use all the columns (except Lobs).
*/
ALL
VERSION
}
@@ -3,11 +3,8 @@ package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.Query;
/**
* Lazy loading capable Maps, Lists and Sets.
@@ -34,10 +31,16 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Return true if the collection is empty and untouched. Used to detect if a
* collection was 'cleared' deliberately or just un-initialised.
*/
public boolean isEmptyAndUntouched();
/**
* Return the bean that owns this collection.
*/
public Object getOwnerBean();
public EntityBean getOwnerBean();
/**
* Return the bean property name this collection represents.
@@ -75,30 +78,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public void setFilterMany(ExpressionList<?> filterMany);
/**
* Set when this collection is being loaded via a background thread.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void setBackgroundFetch(Future<Integer> future);
/**
* Wait for the fetch to complete with a given timeout.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void backgroundFetchWait(long wait, TimeUnit timeUnit);
/**
* Wait for the fetch to complete.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void backgroundFetchWait();
/**
* Set a listener to be notified when the BeanCollection is first touched.
*/
@@ -174,18 +153,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public void setHasMoreRows(boolean hasMoreRows);
/**
* Returns true if the fetch has finished. False if the fetch is continuing in
* a background thread.
*/
public boolean isFinishedFetch();
/**
* Set to true when a fetch has finished. Used when a fetch continues in the
* background.
*/
public void setFinishedFetch(boolean finishedFetch);
/**
* return true if there are real rows held. Return false is this is using
* Deferred fetch to lazy load the rows and the rows have not yet been
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
/**
* Add a loaded bean to the collection.
*/
public void addBean(Object bean);
public void addBean(EntityBean bean);
}
@@ -14,6 +14,10 @@ import java.io.Serializable;
*/
public interface EntityBean extends Serializable {
public String[] _ebean_getPropertyNames();
public String _ebean_getPropertyName(int pos);
/**
* Return the enhancement marker value.
* <p>
@@ -79,31 +83,19 @@ public interface EntityBean extends Serializable {
*/
public Object _ebean_createCopy();
/**
* Return the fields in their index order.
*/
public String[] _ebean_getFieldNames();
/**
* Set the value of a field of an entity bean of this type.
* <p>
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading and oldValues creation.
* </p>
*
* @param fieldIndex
* the index of the field
* @param entityBean
* the entityBean of this type to modify
* @param value
* the value to set
*/
public void _ebean_setField(int fieldIndex, Object entityBean, Object value);
public void _ebean_setField(int fieldIndex, Object value);
/**
* Set the field value with interception.
*/
public void _ebean_setFieldIntercept(int fieldIndex, Object entityBean, Object value);
public void _ebean_setFieldIntercept(int fieldIndex, Object value);
/**
* Return the value of a field from an entity bean of this type.
@@ -111,17 +103,12 @@ public interface EntityBean extends Serializable {
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading.
* </p>
*
* @param fieldIndex
* the index of the field
* @param entityBean
* the entityBean to get the value from
*/
public Object _ebean_getField(int fieldIndex, Object entityBean);
public Object _ebean_getField(int fieldIndex);
/**
* Return the field value with interception.
*/
public Object _ebean_getFieldIntercept(int fieldIndex, Object entityBean);
public Object _ebean_getFieldIntercept(int fieldIndex);
}
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,6 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Objects;
/**
* Identifies a unique node of an object graph.
@@ -70,7 +69,7 @@ public final class ObjectGraphNode implements Serializable {
public int hashCode() {
int hc = 31 * originQueryPoint.hashCode();
hc = 31 * hc + Objects.hashCode(path);
hc = 31 * hc + (path == null ? 0 : path.hashCode());
return hc;
}
@@ -83,7 +82,7 @@ public final class ObjectGraphNode implements Serializable {
}
ObjectGraphNode e = (ObjectGraphNode) obj;
return Objects.equals(e.path, path)
return ((e.path == path) || (e.path != null && e.path.equals(path)))
&& e.originQueryPoint.equals(originQueryPoint);
}
}
@@ -1,8 +1,6 @@
package com.avaje.ebean.common;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.persistence.PersistenceException;
@@ -12,7 +10,6 @@ import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
/**
* Base class for List Set and Map implementations of BeanCollection.
@@ -38,25 +35,16 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected transient BeanCollectionTouched beanCollectionTouched;
protected transient Future<Integer> fetchFuture;
/**
* The owning bean (used for lazy fetch).
*/
protected final Object ownerBean;
protected final EntityBean ownerBean;
/**
* The name of this property in the owning bean (used for lazy fetch).
*/
protected final String propertyName;
/**
* Can be false when a background thread is used to continue the fetch the
* rows. It will set this to true when it is finished. If no background thread
* is used then this should already be true.
*/
protected boolean finishedFetch = true;
/**
* Flag set to true if rows are limited by firstRow maxRows and more rows
* exist. For use by client to enable 'next' for paging.
@@ -70,6 +58,12 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected boolean modifyRemoveListening;
protected boolean modifyListening;
/**
* Flag used to tell if empty collections have been cleared etc or just
* uninitialised.
*/
protected boolean touched;
/**
* Constructor not non-lazy loading collection.
*/
@@ -81,19 +75,15 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
/**
* Used to create deferred fetch proxy.
*/
public AbstractBeanCollection(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
this.loader = loader;
this.ebeanServerName = loader.getName();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
if (ownerBean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
this.readOnly = ebi.isReadOnly();
}
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
}
public Object getOwnerBean() {
public EntityBean getOwnerBean() {
return ownerBean;
}
@@ -128,7 +118,14 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
checkEmptyLazyLoad();
}
protected void touched() {
/**
* Set touched. If setFlag is false then typically an isEmpty() call and still
* considering that to be untouched.
*/
protected void touched(boolean setFlag) {
if (setFlag) {
touched = true;
}
if (beanCollectionTouched != null) {
// only call this once
beanCollectionTouched.notifyTouched(this);
@@ -174,46 +171,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.hasMoreRows = hasMoreRows;
}
/**
* Returns true if the fetch has finished. False if the fetch is continuing in
* a background thread.
*/
public boolean isFinishedFetch() {
return finishedFetch;
}
/**
* Set to true when a fetch has finished. Used when a fetch continues in the
* background.
*/
public void setFinishedFetch(boolean finishedFetch) {
this.finishedFetch = finishedFetch;
}
public void setBackgroundFetch(Future<Integer> fetchFuture) {
this.fetchFuture = fetchFuture;
}
public void backgroundFetchWait(long wait, TimeUnit timeUnit) {
if (fetchFuture != null) {
try {
fetchFuture.get(wait, timeUnit);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
public void backgroundFetchWait() {
if (fetchFuture != null) {
try {
fetchFuture.get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
@@ -10,13 +10,15 @@ import java.util.ListIterator;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* List capable of lazy loading.
*/
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>,
BeanCollectionAdd {
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
private static final long serialVersionUID = 1L;
/**
* The underlying List implementation.
*/
@@ -40,12 +42,17 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
/**
* Used to create deferred fetch proxy.
*/
public BeanList(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
@Override
public boolean isEmptyAndUntouched() {
return !touched && (list == null || list.isEmpty());
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
list.add((E) bean);
}
@@ -75,16 +82,24 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
list = new ArrayList<E>();
}
}
touched();
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
}
touched();
touched(setTouched);
}
}
@@ -257,7 +272,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
public boolean isEmpty() {
init();
initAsUntouched();
return list.isEmpty();
}
@@ -8,12 +8,15 @@ import java.util.Map;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* Map capable of lazy loading.
*/
public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Map<K, E> {
private static final long serialVersionUID = 1L;
/**
* The underlying map implementation.
*/
@@ -33,9 +36,13 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
this(new LinkedHashMap<K, E>());
}
public BeanMap(BeanCollectionLoader ebeanServer, Object ownerBean, String propertyName) {
public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) {
super(ebeanServer, ownerBean, propertyName);
}
public boolean isEmptyAndUntouched() {
return !touched && (map == null || map.isEmpty());
}
@SuppressWarnings("unchecked")
public void internalPut(Object key, Object bean) {
@@ -83,16 +90,24 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
map = new LinkedHashMap<K, E>();
}
}
touched();
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (map == null) {
lazyLoadCollection(false);
}
touched();
touched(setTouched);
}
}
@@ -209,7 +224,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public boolean isEmpty() {
init();
initAsUntouched();
return map.isEmpty();
}
@@ -8,12 +8,15 @@ import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* Set capable of lazy loading.
*/
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
private static final long serialVersionUID = 1L;
/**
* The underlying Set implementation.
*/
@@ -33,12 +36,16 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
this(new LinkedHashSet<E>());
}
public BeanSet(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
public boolean isEmptyAndUntouched() {
return !touched && (set == null || set.isEmpty());
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
set.add((E) bean);
}
@@ -83,16 +90,24 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
set = new LinkedHashSet<E>();
}
}
touched();
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (set == null) {
lazyLoadCollection(true);
}
touched();
touched(setTouched);
}
}
@@ -217,7 +232,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public boolean isEmpty() {
init();
initAsUntouched();
return set.isEmpty();
}
@@ -66,7 +66,10 @@ import com.avaje.ebean.util.ClassUtil;
* @author rbygrave
*/
public class ServerConfig {
/** The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100 */
/**
* The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100
*/
private final static int DEFAULT_QUERY_BATCH_SIZE = 100;
/**
@@ -112,13 +115,19 @@ public class ServerConfig {
*/
private List<String> searchJars = new ArrayList<String>();
/** The autofetch config. */
/**
* Config controlling the autofetch behaviour.
*/
private AutofetchConfig autofetchConfig = new AutofetchConfig();
/** The database platform name. */
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
private String databasePlatformName;
/** The database platform. */
/**
* The database platform.
*/
private DatabasePlatform databasePlatform;
/**
@@ -130,10 +139,14 @@ public class ServerConfig {
private int persistBatchSize = 20;
/** The default batch size for lazy loading */
/**
* The default batch size for lazy loading
*/
private int lazyLoadBatchSize = 1;
/** The query batch size. */
/**
* The query batch size.
*/
private int queryBatchSize = -1;
private boolean ddlGenerate;
@@ -152,26 +165,51 @@ public class ServerConfig {
*/
private PstmtDelegate pstmtDelegate;
/** The data source. */
/**
* The data source (if programmatically provided).
*/
private DataSource dataSource;
/** The data source config. */
/**
* The data source config.
*/
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
/** The data source jndi name. */
/**
* The data source JNDI name if using a JNDI DataSource.
*/
private String dataSourceJndiName;
/** The database boolean true. */
/**
* The database boolean true value (typically either 1, T, or Y).
*/
private String databaseBooleanTrue;
/** The database boolean false. */
/**
* The database boolean false value (typically either 0, F or N).
*/
private String databaseBooleanFalse;
/** The naming convention. */
/**
* The naming convention.
*/
private NamingConvention namingConvention;
/** The update changes only. */
/**
* Behaviour of update to include on the change properties.
*/
private boolean updateChangesOnly = true;
/**
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
*/
private boolean updatesDeleteMissingChildren = true;
/**
* 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<?>>();
@@ -764,6 +802,21 @@ public class ServerConfig {
this.dbEncrypt = dbEncrypt;
}
/**
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public boolean isUuidStoreAsBinary() {
return uuidStoreAsBinary;
}
/**
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
this.uuidStoreAsBinary = uuidStoreAsBinary;
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -931,8 +984,23 @@ public class ServerConfig {
public void setUpdateChangesOnly(boolean updateChangesOnly) {
this.updateChangesOnly = updateChangesOnly;
}
/**
* Return true if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
*/
public boolean isUpdatesDeleteMissingChildren() {
return updatesDeleteMissingChildren;
}
/**
* Set if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
*/
public void setUpdatesDeleteMissingChildren(boolean updatesDeleteMissingChildren) {
this.updatesDeleteMissingChildren = updatesDeleteMissingChildren;
}
/**
* Return true if the ebeanServer should collection query statistics by ObjectGraphNode.
*/
@@ -1235,8 +1303,11 @@ public class ServerConfig {
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", true);
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
boolean batchMode = p.getBoolean("batch.mode", false);
persistBatching = p.getBoolean("persistBatching", batchMode);
@@ -1249,6 +1320,7 @@ public class ServerConfig {
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
@@ -5,7 +5,7 @@ import java.util.Set;
/**
* Used to enhance or override the default bean persistence mechanism.
* <p>
* Note that if want to totally change the finding, you need to use a BeanFinder
* Note that if want to totally change the finding, you need to use a BeanQueryAdapter
* rather than using postLoad().
* </p>
* <p>
@@ -2,6 +2,8 @@ package com.avaje.ebean.event;
import java.util.Set;
import com.avaje.ebean.config.ServerConfig;
/**
* Listens for committed bean events.
* <p>
@@ -31,8 +33,9 @@ import java.util.Set;
* </p>
* <p>
* A BeanPersistListener is either found automatically via class path search or
* can be added programmatically via ServerConfiguration.addEntity().
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}.
* </p>
* @see ServerConfig#add(BeanPersistListener)
*/
public interface BeanPersistListener<T> {
@@ -52,7 +55,7 @@ public interface BeanPersistListener<T> {
* @param bean
* The bean that was updated.
* @param updatedProperties
* the properties on the bean that where updated
* The properties that were modified by this update.
*/
public boolean updated(T bean, Set<String> updatedProperties);
@@ -1,9 +1,11 @@
package com.avaje.ebean.event;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.ValuePair;
/**
* Holds the information available for a bean persist (insert, update or
@@ -41,12 +43,8 @@ public interface BeanPersistRequest<T> {
public T getBean();
/**
* Returns a bean containing the original values prior to the bean being
* modified.
* <p>
* This is for updates only.
* </p>
* Returns a map of the properties that have changed and their new and old values.
*/
public T getOldValues();
public Map<String,ValuePair> getUpdatedValues();
}
@@ -32,7 +32,6 @@ import com.avaje.ebean.text.StringParser;
* csvReader.addDateTime(&quot;anniversary&quot;, &quot;dd-MMM-yyyy&quot;);
* csvReader.addProperty(&quot;billingAddress.line1&quot;);
* csvReader.addProperty(&quot;billingAddress.city&quot;);
* csvReader.addReference(&quot;billingAddress.country.code&quot;);
*
* csvReader.process(reader);
*
@@ -41,8 +40,6 @@ import com.avaje.ebean.text.StringParser;
* }
* </pre>
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*/
@@ -129,13 +126,6 @@ public interface CsvReader<T> {
*/
public void addProperty(String propertyName);
/**
* Define the next property to be a reference. This effectively means it
* represents a foreign key. For example, with an Address object a Country
* Code could be a reference.
*/
public void addReference(String propertyName);
/**
* Define the next property and use a custom StringParser to convert the
* string content into the appropriate type for the property.
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.util.Objects;
/**
* A hash for a query plan.
*/
@@ -26,7 +24,7 @@ public class HashQueryPlan {
public int hashCode() {
int hc = planHash;
hc = hc * 31 + bindCount;
hc = hc * 31 + Objects.hashCode(rawSql);
hc = hc * 31 + (rawSql == null ? 0 : rawSql.hashCode());
return hc;
}
@@ -41,6 +39,6 @@ public class HashQueryPlan {
HashQueryPlan e = (HashQueryPlan) obj;
return e.planHash == planHash
&& e.bindCount == bindCount
&& Objects.equals(e.rawSql, rawSql);
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
}
}
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.util.Objects;
/**
* Used to build HashQueryPlan instances.
*/
@@ -33,7 +31,7 @@ public class HashQueryPlanBuilder {
* Add an object to the hash calculation.
*/
public HashQueryPlanBuilder add(Object object) {
planHash = planHash * 31 + Objects.hashCode(object);
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
return this;
}
@@ -2,8 +2,8 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading ManyToOne and OneToOne relationships.
@@ -18,10 +18,12 @@ public class LoadBeanRequest extends LoadRequest {
private final boolean loadCache;
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
boolean loadCache) {
super(transaction, lazy);
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, boolean lazy, String lazyLoadProperty, boolean loadCache) {
this(LoadBuffer, null, lazy, lazyLoadProperty, loadCache);
}
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
super(parentRequest, lazy);
this.LoadBuffer = LoadBuffer;
this.batch = LoadBuffer.getBatch();
this.lazyLoadProperty = lazyLoadProperty;
@@ -2,69 +2,71 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
* Request for loading Associated Many Beans.
*/
public class LoadManyRequest extends LoadRequest {
private final List<BeanCollection<?>> batch;
private final List<BeanCollection<?>> batch;
private final LoadManyBuffer loadContext;
private final LoadManyBuffer loadContext;
private final boolean onlyIds;
private final boolean onlyIds;
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
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, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
boolean onlyIds, boolean loadCache) {
super(transaction, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
super(parentRequest, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such
* as clear() and removeAll() where it generally makes sense to
* only fetch the Id values as the other property information is
* not used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
* generally makes sense to only fetch the Id values as the other property information is not
* used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
}
@@ -1,22 +1,36 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
*/
public abstract class LoadRequest {
protected final boolean lazy;
protected final OrmQueryRequest<?> parentRequest;
protected final Transaction transaction;
protected final Transaction transaction;
public LoadRequest(Transaction transaction, boolean lazy) {
protected final boolean lazy;
this.transaction = transaction;
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
this.parentRequest = parentRequest;
this.transaction = parentRequest == null ? null : parentRequest.getTransaction();
this.lazy = lazy;
}
/**
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
* true. This is for testing purposes to confirm the secondary query executes etc.
*/
public void logSecondaryQuery(SpiQuery<?> query) {
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
parentRequest.getQuery().logSecondaryQuery(query);
}
}
/**
* Return true if this is a lazy load and false if it is a secondary query.
*/
@@ -1,13 +1,15 @@
package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.Set;
import java.util.Collection;
import java.util.TreeMap;
import java.util.TreeSet;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Holds the joins needs to support the many where predicates.
@@ -17,12 +19,32 @@ public class ManyWhereJoins implements Serializable {
private static final long serialVersionUID = -6490181101871795417L;
private final TreeSet<String> joins = new TreeSet<String>();
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
private StringBuilder formulaProperties = new StringBuilder();
private boolean formulaWithJoin;
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
private boolean requireOuterJoins;
/**
* Return the current 'mode' indicating if outer joins are currently required or not.
*/
public boolean isRequireOuterJoins() {
return requireOuterJoins;
}
/**
* Set the 'mode' to be that joins added are required to be outer joins.
* This is set during the evaluation of disjunction predicates.
*/
public void setRequireOuterJoins(boolean requireOuterJoins) {
this.requireOuterJoins = requireOuterJoins;
}
/**
* Add a many where join.
*/
@@ -34,10 +56,10 @@ public class ManyWhereJoins implements Serializable {
join = addManyToJoin(join, p.getName());
}
if (join != null){
joins.add(join);
addJoin(join);
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
joins.add(join+"."+secondaryTableJoinPrefix);
addJoin(join+"."+secondaryTableJoinPrefix);
}
addParentJoins(join);
}
@@ -58,11 +80,16 @@ public class ManyWhereJoins implements Serializable {
private void addParentJoins(String join) {
String[] split = SplitName.split(join);
if (split[0] != null){
joins.add(split[0]);
addJoin(split[0]);
addParentJoins(split[0]);
}
}
private void addJoin(String property) {
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
joins.put(property, new PropertyJoin(property, joinType));
}
/**
* Return true if there are no extra many where joins.
*/
@@ -73,10 +100,22 @@ public class ManyWhereJoins implements Serializable {
/**
* Return the set of many where joins.
*/
public Set<String> getJoins() {
return joins;
public Collection<PropertyJoin> getPropertyJoins() {
return joins.values();
}
/**
* Return the set of property names for the many where joins.
*/
public TreeSet<String> getPropertyNames() {
TreeSet<String> propertyNames = new TreeSet<String>();
for (PropertyJoin join : joins.values()) {
propertyNames.add(join.getProperty());
}
return propertyNames;
}
/**
* In findRowCount query found a formula property with a join clause so building a select clause
* specifically for the findRowCount query.
@@ -0,0 +1,39 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join required for a given property and whether than needs to be an outer join.
*/
public class PropertyJoin {
/**
* The property name.
*/
private final String property;
/**
* Set to true if the property needs to be an outer join.
*/
private final SqlJoinType joinType;
public PropertyJoin(String property, SqlJoinType joinType) {
this.property = property;
this.joinType = joinType;
}
/**
* Return the property that should be joined.
*/
public String getProperty() {
return property;
}
/**
* Return true if this join is required to be an outer join.
*/
public SqlJoinType getSqlJoinType() {
return joinType;
}
}
@@ -10,6 +10,7 @@ import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanLoader;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
@@ -30,18 +31,15 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
*/
public void shutdownManaged();
/**
* Return true if query origins should be collected.
*/
public boolean isCollectQueryOrigins();
/**
* Return true if DeleteMissingChildren defaults to true for stateless
* updates.
* Return the server configuration.
*/
public boolean isDefaultDeleteMissingChildren();
/**
* Return true if UpdateNullProperties defaults to true for stateless updates.
*/
public boolean isDefaultUpdateNullProperties();
public ServerConfig getServerConfig();
/**
* Return the DatabasePlatform for this server.
@@ -10,13 +10,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface SpiExpression extends Expression {
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent
* join clause.
* </p>
*/
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
/**
@@ -6,7 +6,6 @@ import java.util.List;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
@@ -261,10 +260,25 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Boolean isAutofetch();
// /**
// * Return explicit forUpdate setting or null.
// */
// public boolean isForUpdate();
/**
* Set to true if you want to capture executed secondary queries.
*/
public void setLogSecondaryQuery(boolean logSecondaryQuery);
/**
* Return true if executed secondary queries should be captured.
*/
public boolean isLogSecondaryQuery();
/**
* Return the list of secondary queries that were executed.
*/
public List<SpiQuery<?>> getLoggedSecondaryQueries();
/**
* Log an executed secondary query.
*/
public void logSecondaryQuery(SpiQuery<?> query);
/**
* If return null then no autoFetch profiling for this query. If a
@@ -509,12 +523,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public String getMapKey();
/**
* Return the number of rows after which fetching should occur in a
* background thread.
*/
public int getBackgroundFetchAfter();
/**
* Return the maximum number of rows to return in the query.
*/
@@ -545,19 +553,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Object getId();
/**
* Return the queryListener.
*/
public QueryListener<T> getListener();
/**
* Return true if this query should use its own transaction.
* <p>
* This is true for background fetching and when using QueryListener.
* </p>
*/
public boolean createOwnTransaction();
/**
* Set the generated sql for debug purposes.
*
@@ -1,9 +1,9 @@
package com.avaje.ebeaninternal.api;
import java.sql.SQLException;
import java.util.Set;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.persist.dml.DmlHandler;
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
@@ -21,20 +21,20 @@ import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
*/
public interface SpiUpdatePlan {
/**
* Return true if the set clause has no columns.
* <p>
* Can occur when the only columns updated have a updatable=false in their
* deployment.
* </p>
*/
public boolean isEmptySetClause();
/**
* Return true if the set clause has no columns.
* <p>
* Can occur when the only columns updated have a updatable=false in their
* deployment.
* </p>
*/
public boolean isEmptySetClause();
/**
* Bind given the request and bean. The bean could be the oldValues bean
* when binding a update or delete where clause with ALL concurrency mode.
*/
public void bindSet(DmlHandler bind, Object bean) throws SQLException;
public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
/**
* Return the time this plan was created.
@@ -66,10 +66,10 @@ public interface SpiUpdatePlan {
*/
public Bindable getSet();
/**
* Return the properties that where changed and should be included in the
* update statement.
*/
public Set<String> getProperties();
// /**
// * Return the properties that where changed and should be included in the
// * update statement.
// */
// public Set<String> getProperties();
}
@@ -86,9 +86,9 @@ public class StatisticsNodeUsage implements Serializable {
}
if ((modified || queryTuningAddVersion) && desc != null) {
BeanProperty[] versionProps = desc.propertiesVersion();
if (versionProps.length > 0) {
pathProps.addToPath(path, versionProps[0].getName());
BeanProperty versionProp = desc.getVersionProperty();
if (versionProp != null) {
pathProps.addToPath(path, versionProp.getName());
}
}
}
@@ -1,50 +1,102 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.Set;
import java.util.Arrays;
/**
* Data held in the bean cache for cached beans.
*/
public class CachedBeanData {
private final Object sharableBean;
private final Set<String> loadedProperties;
private final Object[] data;
private final int naturalKeyUpdate;
public CachedBeanData(Object sharableBean, Set<String> loadedProperties, Object[] data, int naturalKeyUpdate) {
this.sharableBean = sharableBean;
this.loadedProperties= loadedProperties;
this.data = data;
this.naturalKeyUpdate = naturalKeyUpdate;
}
public Object getSharableBean() {
return sharableBean;
}
private final long whenCreated;
private final Object sharableBean;
private final boolean[] loaded;
private final Object[] data;
private final boolean naturalKeyUpdate;
private final Object naturalKey;
private final Object oldNaturalKey;
public boolean isNaturalKeyUpdate() {
return naturalKeyUpdate > -1;
}
public Object getNaturalKey() {
return data[naturalKeyUpdate];
}
public CachedBeanData(Object sharableBean, boolean[] loaded, Object[] data, Object naturalKey, Object oldNaturalKey) {
this.whenCreated = System.currentTimeMillis();
this.sharableBean = sharableBean;
this.loaded = loaded;
this.data = data;
this.naturalKeyUpdate = naturalKey != null;
this.naturalKey = (naturalKey != null) ? naturalKey : oldNaturalKey;
this.oldNaturalKey = oldNaturalKey;
}
public boolean containsProperty(String propName) {
return loadedProperties == null || loadedProperties.contains(propName);
public String toString() {
return Arrays.toString(data);
}
/**
* Return a copy of the property data.
*/
public Object[] copyData() {
Object[] dest = new Object[data.length];
System.arraycopy(data, 0, dest, 0, data.length);
return dest;
}
/**
* Return a copy of the loaded status for the properties.
*/
public boolean[] copyLoaded() {
boolean[] dest = new boolean[data.length];
for (int i = 0; i < dest.length; i++) {
dest[i] = loaded[i];
}
public Object getData(int i){
return data[i];
}
public Set<String> getLoadedProperties() {
return loadedProperties;
}
public Object[] copyData() {
Object[] dest = new Object[data.length];
System.arraycopy(data, 0, dest, 0, data.length);
return dest;
}
return dest;
}
/**
* Return when the cached data was created.
*/
public long getWhenCreated() {
return whenCreated;
}
/**
* Return a sharable (immutable read only) bean.
*/
public Object getSharableBean() {
return sharableBean;
}
/**
* Return true if this data requires an update to the natural key cache.
*/
public boolean isNaturalKeyUpdate() {
return naturalKeyUpdate;
}
/**
* Return the new/current natural key value.
*/
public Object getNaturalKey() {
return naturalKey;
}
/**
* Return the old natural key (its entry should be removed).
*/
public Object getOldNaturalKey() {
return oldNaturalKey;
}
/**
* Return the data for the specific property.
*/
public Object getData(int i) {
return data[i];
}
/**
* Return true if the property is contained in this data.
*/
public boolean isLoaded(int i) {
return loaded[i];
}
}
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.HashSet;
import java.util.Set;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -10,94 +7,71 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
public class CachedBeanDataFromBean {
private final BeanDescriptor<?> desc;
private final Object bean;
private final EntityBeanIntercept ebi;
private final Set<String> loadedProps;
private final Set<String> extractProps;
public static CachedBeanData extract(BeanDescriptor<?> desc, Object bean){
if (bean instanceof EntityBean){
return new CachedBeanDataFromBean(desc, bean, ((EntityBean)bean)._ebean_getIntercept()).extract();
} else {
return new CachedBeanDataFromBean(desc, bean, null).extract();
}
}
public static CachedBeanData extract(BeanDescriptor<?> desc, Object bean, EntityBeanIntercept ebi){
return new CachedBeanDataFromBean(desc, bean, ebi).extract();
}
private CachedBeanDataFromBean(BeanDescriptor<?> desc, Object bean, EntityBeanIntercept ebi) {
this.desc = desc;
this.bean = bean;
this.ebi = ebi;
if (ebi != null){
this.loadedProps = ebi.getLoadedProps();
this.extractProps = (loadedProps == null) ? null : new HashSet<String>();
} else {
this.extractProps = new HashSet<String>();
this.loadedProps = null;
}
}
private CachedBeanData extract(){
public static CachedBeanData extract(BeanDescriptor<?> desc, EntityBean bean) {
BeanProperty[] props = desc.propertiesNonMany();
EntityBeanIntercept ebi = bean._ebean_getIntercept();
Object[] data = new Object[desc.getPropertyCount()];
boolean[] loaded = new boolean[desc.getPropertyCount()];
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
int propertyIndex = idProperty.getPropertyIndex();
if (ebi.isLoadedProperty(propertyIndex)) {
// extract the id property value
data[propertyIndex] = idProperty.getCacheDataValue(bean);
loaded[propertyIndex] = true;
}
}
BeanProperty[] props = desc.propertiesNonMany();
Object[] data = new Object[props.length];
int naturalKeyUpdate = -1;
for (int i = 0; i < props.length; i++) {
BeanProperty prop = props[i];
if (includeNonManyProperty(prop.getName())){
data[i] = prop.getCacheDataValue(bean);
if (prop.isNaturalKey()) {
naturalKeyUpdate = i;
}
if (ebi != null){
if (extractProps != null){
extractProps.add(prop.getName());
}
} else if (data[i] != null){
if (extractProps != null){
extractProps.add(prop.getName());
}
}
}
Object naturalKey = null;
// extract all the non-many properties
for (int i = 0; i < props.length; i++) {
BeanProperty prop = props[i];
if (ebi.isLoadedProperty(prop.getPropertyIndex())) {
int propertyIndex = prop.getPropertyIndex();
data[propertyIndex] = prop.getCacheDataValue(bean);
loaded[propertyIndex] = true;
if (prop.isNaturalKey()) {
naturalKey = prop.getValue(bean);
}
Object sharableBean = null;
if (desc.isCacheSharableBeans() && ebi != null && loadedProps == null){
if (ebi.isReadOnly()){
sharableBean = bean;
} else {
// create a readOnly sharable instance by copying the data
sharableBean = desc.createBean();
BeanProperty[] propertiesId = desc.propertiesId();
for (int i = 0; i < propertiesId.length; i++) {
Object v = propertiesId[i].getValue(bean);
propertiesId[i].setValue(sharableBean, v);
}
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
for (int i = 0; i < propertiesNonTransient.length; i++) {
Object v = propertiesNonTransient[i].getValue(bean);
propertiesNonTransient[i].setValue(sharableBean, v);
}
EntityBeanIntercept ebi = ((EntityBean)sharableBean)._ebean_intercept();
ebi.setReadOnly(true);
ebi.setLoaded();
}
}
return new CachedBeanData(sharableBean, extractProps, data, naturalKeyUpdate);
}
}
EntityBean sharableBean = createSharableBean(desc, bean, ebi);
return new CachedBeanData(sharableBean, loaded, data, naturalKey, null);
}
private static EntityBean createSharableBean(BeanDescriptor<?> desc, EntityBean bean, EntityBeanIntercept beanEbi) {
private boolean includeNonManyProperty(String name) {
return loadedProps == null || loadedProps.contains(name);
if (!desc.isCacheSharableBeans() || !beanEbi.isFullyLoadedBean()) {
return null;
}
if (beanEbi.isReadOnly()) {
return bean;
}
// create a readOnly sharable instance by copying the data
EntityBean sharableBean = desc.createEntityBean();
BeanProperty idProp = desc.getIdProperty();
if (idProp != null) {
Object v = idProp.getValue(bean);
idProp.setValue(sharableBean, v);
}
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
for (int i = 0; i < propertiesNonTransient.length; i++) {
Object v = propertiesNonTransient[i].getValue(bean);
propertiesNonTransient[i].setValue(sharableBean, v);
}
EntityBeanIntercept intercept = sharableBean._ebean_intercept();
intercept.setReadOnly(true);
intercept.setLoaded();
return sharableBean;
}
}
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.HashSet;
import java.util.Set;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -11,107 +8,45 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
public class CachedBeanDataToBean {
private final BeanDescriptor<?> desc;
private final Object bean;
private final EntityBeanIntercept ebi;
private final CachedBeanData cacheBeandata;
private final Set<String> cacheLoadedProperties;
private final Set<String> loadedProps;
private final Set<String> excludeProps;
private final Object oldValuesBean;
private final boolean readOnly;
public static void load(BeanDescriptor<?> desc, Object bean, CachedBeanData cacheBeandata) {
if (bean instanceof EntityBean){
load(desc, bean, ((EntityBean)bean)._ebean_getIntercept(), cacheBeandata);
} else {
load(desc, bean, null, cacheBeandata);
}
public static boolean load(BeanDescriptor<?> desc, EntityBean bean, CachedBeanData cacheBeanData) {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
// load the id property
loadProperty(bean, cacheBeanData, ebi, idProperty);
}
// load the non-many properties
BeanProperty[] props = desc.propertiesNonMany();
for (int i = 0; i < props.length; i++) {
loadProperty(bean, cacheBeanData, ebi, props[i]);
}
public static void load(BeanDescriptor<?> desc, Object bean, EntityBeanIntercept ebi, CachedBeanData cacheBeandata) {
new CachedBeanDataToBean(desc, bean, ebi, cacheBeandata).load();
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
for (int i = 0; i < manys.length; i++) {
manys[i].createReferenceIfNull(bean);
}
private CachedBeanDataToBean(BeanDescriptor<?> desc, Object bean, EntityBeanIntercept ebi, CachedBeanData cacheBeandata) {
this.desc = desc;
this.bean = bean;
this.ebi = ebi;
this.cacheBeandata = cacheBeandata;
this.cacheLoadedProperties = cacheBeandata.getLoadedProperties();
this.loadedProps = (cacheLoadedProperties == null) ? null : new HashSet<String>();
if (ebi != null){
this.excludeProps = ebi.getLoadedProps();
this.oldValuesBean = ebi.getOldValues();
this.readOnly = ebi.isReadOnly();
} else {
this.excludeProps = null;
this.oldValuesBean = null;
this.readOnly = false;
}
}
private boolean load(){
BeanProperty[] propertiesNonTransient = desc.propertiesNonMany();
for (int i = 0; i < propertiesNonTransient.length; i++) {
BeanProperty prop = propertiesNonTransient[i];
if (includeNonManyProperty(prop.getName())){
Object data = cacheBeandata.getData(i);
prop.setCacheDataValue(bean, data, oldValuesBean, readOnly);
}
}
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
for (int i = 0; i < manys.length; i++) {
BeanPropertyAssocMany<?> prop = manys[i];
if (includeManyProperty(prop.getName())){
// set a lazy loading proxy
prop.createReference(bean);
}
}
if (ebi != null){
if (loadedProps == null){
ebi.setLoadedProps(null);
} else {
HashSet<String> mergeProps = new HashSet<String>();
if (excludeProps != null) {
mergeProps.addAll(excludeProps);
}
mergeProps.addAll(loadedProps);
ebi.setLoadedProps(mergeProps);
}
ebi.setLoadedLazy();
}
return true;
ebi.setLoadedLazy();
return true;
}
private static void loadProperty(EntityBean bean, CachedBeanData cacheBeanData, EntityBeanIntercept ebi, BeanProperty prop) {
int propertyIndex = prop.getPropertyIndex();
if (cacheBeanData.isLoaded(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
// already loaded (lazy load on partially loaded bean)
} else {
Object data = cacheBeanData.getData(propertyIndex);
prop.setCacheDataValue(bean, data);
}
}
private boolean includeManyProperty(String name) {
if (excludeProps != null && excludeProps.contains(name)){
// ignore this property (partial bean lazy loading)
return false;
}
if (loadedProps != null){
loadedProps.add(name);
}
return true;
}
private boolean includeNonManyProperty(String name) {
if (excludeProps != null && excludeProps.contains(name)){
// ignore this property (partial bean lazy loading)
return false;
}
if (cacheLoadedProperties != null && !cacheLoadedProperties.contains(name)){
return false;
}
if (loadedProps != null){
loadedProps.add(name);
}
return true;
}
}
}
@@ -1,49 +1,44 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.HashSet;
import java.util.Set;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
* Create a new CachedBeanData based on the existing CachedBeanData and the updated bean.
*/
public class CachedBeanDataUpdate {
public static CachedBeanData update(BeanDescriptor<?> desc, CachedBeanData data, PersistRequestBean<?> updateRequest){
/**
* Create a new CachedBeanData based on the existing CachedBeanData and the updated bean.
*/
public static CachedBeanData update(BeanDescriptor<?> desc, CachedBeanData existingData, EntityBean updateBean) {
Set<String> loadedProperties = data.getLoadedProperties();
Object[] copyOfData = data.copyData();
Object updateBean = updateRequest.getBean();
Set<String> updatedProperties = updateRequest.getUpdatedProperties();
int naturalKeyUpdate = -1;
boolean mergeProperties = false;
BeanProperty[] props = desc.propertiesNonMany();
for (int i = 0; i < props.length; i++) {
if (updatedProperties.contains(props[i].getName())){
if (props[i].isNaturalKey()){
naturalKeyUpdate = i;
}
copyOfData[i] = props[i].getCacheDataValue(updateBean);
if (loadedProperties != null && !mergeProperties && !loadedProperties.contains(props[i].getName())){
mergeProperties = true;
}
}
// take a copy of the raw data and loaded status
boolean[] copyLoaded = existingData.copyLoaded();
Object[] copyData = existingData.copyData();
EntityBeanIntercept ebi = updateBean._ebean_getIntercept();
Object newNaturalKey = null;
Object oldNaturalKey = existingData.getNaturalKey();
BeanProperty[] props = desc.propertiesNonMany();
for (int i = 0; i < props.length; i++) {
// check if the properties was in the update
int propertyIndex = props[i].getPropertyIndex();
if (ebi.isLoadedProperty(propertyIndex)) {
if (props[i].isNaturalKey()) {
newNaturalKey = updateBean._ebean_getField(propertyIndex);
}
if (mergeProperties){
HashSet<String> mergeProps = new HashSet<String>();
mergeProps.addAll(loadedProperties);
mergeProps.addAll(updatedProperties);
loadedProperties = mergeProps;
}
return new CachedBeanData(null, loadedProperties, copyOfData, naturalKeyUpdate);
// set the cache safe value for the property and mark it as loaded
copyData[propertyIndex] = props[i].getCacheDataValue(updateBean);
copyLoaded[propertyIndex] = true;
}
}
return new CachedBeanData(null, copyLoaded, copyData, newNaturalKey, oldNaturalKey);
}
}
@@ -2,16 +2,26 @@ package com.avaje.ebeaninternal.server.cache;
import java.util.List;
/**
* The cached data for O2M and M2M relationships.
* <p>
* This is effectively just the Id values for each of the beans in the collection.
* </p>
*/
public class CachedManyIds {
private final List<Object> idList;
public CachedManyIds(List<Object> idList) {
this.idList = idList;
}
private final List<Object> idList;
public List<Object> getIdList() {
return idList;
}
public CachedManyIds(List<Object> idList) {
this.idList = idList;
}
public String toString() {
return idList.toString();
}
public List<Object> getIdList() {
return idList;
}
}
@@ -8,6 +8,7 @@ import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;
import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary;
/**
* Default implementation of TypeConverter.
@@ -192,6 +193,9 @@ public final class BasicTypeConverter implements Serializable {
if (value instanceof String) {
return UUID.fromString((String) value);
}
if (value instanceof byte[]) {
return ScalarTypeUUIDBinary.convertFromBytes((byte[])value);
}
return (UUID) value;
}
@@ -21,18 +21,18 @@ public abstract class BeanRequest {
/**
* The server processing the request.
*/
final SpiEbeanServer ebeanServer;
protected final SpiEbeanServer ebeanServer;
final String serverName;
protected final String serverName;
/**
* The transaction this is part of.
*/
SpiTransaction transaction;
protected SpiTransaction transaction;
boolean createdTransaction;
protected boolean createdTransaction;
boolean readOnly;
protected boolean readOnly;
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
@@ -11,7 +11,6 @@ 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;
@@ -52,6 +51,7 @@ public class BootupClasses implements ClassPathSearchMatcher {
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
@@ -311,7 +311,7 @@ public class BootupClasses implements ClassPathSearchMatcher {
} else if (isEntity(cls)) {
entityList.add(cls);
} else if (isInterestingInterface(cls)) {
return true;
@@ -4,87 +4,126 @@ package com.avaje.ebeaninternal.server.core;
* Options for controlling cache behaviour for a given type.
*/
public class CacheOptions {
private boolean useCache;
private boolean readOnly;
private String naturalKey;
private String warmingQuery;
/**
* Construct with options.
*/
public CacheOptions() {
}
/**
* Return true if this should use a cache for lazy loading.
*/
public boolean isUseCache() {
return useCache;
}
/**
* Set whether to use the bean cache for the associated type.
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
private boolean useCache;
private boolean readOnly;
private String naturalKey;
private String warmingQuery;
private int maxIdleSecs;
private long maxSecsToLive;
/**
* Construct with options.
*/
public CacheOptions() {
}
/**
* Return true if this should use a cache for lazy loading.
*/
public boolean isUseCache() {
return useCache;
}
/**
* Set whether to use the bean cache for the associated type.
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
}
/**
* Return the readOnly default setting.
*/
public boolean isReadOnly() {
return readOnly;
}
/**
* Set read Only default setting.
*/
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
/**
* Return the query used to warm the cache.
*/
public String getWarmingQuery() {
return warmingQuery;
}
/**
* Set the cache warming query.
*/
public void setWarmingQuery(String warmingQuery) {
this.warmingQuery = warmingQuery;
}
/**
* Return true if a natural key is set.
*/
public boolean isUseNaturalKeyCache() {
return naturalKey != null;
}
/**
* Return the natural key property.
*/
public String getNaturalKey() {
return naturalKey;
}
/**
* Set the natural key property.
*/
public void setNaturalKey(String naturalKey) {
if (naturalKey == null || naturalKey.length() == 0) {
naturalKey = null;
} else {
this.naturalKey = naturalKey.trim();
}
}
/**
* Return the max age of entries in seconds.
*/
public long getMaxSecsToLive() {
return maxSecsToLive;
}
/**
* Return the readOnly default setting.
*/
public boolean isReadOnly() {
return readOnly;
}
/**
* Set the max age of entries in seconds.
*/
public void setMaxSecsToLive(long maxSecsToLive) {
this.maxSecsToLive = maxSecsToLive;
}
/**
* Set read Only default setting.
*/
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
/**
* Set the max idle seconds.
*/
public void setMaxIdleSecs(int maxIdleSecs) {
this.maxIdleSecs = maxIdleSecs;
}
/**
* Return the query used to warm the cache.
*/
public String getWarmingQuery() {
return warmingQuery;
}
/**
* Return the max idle seconds.
*/
public int getMaxIdleSecs() {
return maxIdleSecs;
}
/**
* Set the cache warming query.
*/
public void setWarmingQuery(String warmingQuery) {
this.warmingQuery = warmingQuery;
}
/**
* Return true if the entry exceeds the maxIdleSecs or maxSecsToLive.
*/
public boolean isTooOldInMillis(long ageMillis) {
long secs = ageMillis / 1000;
return (maxIdleSecs > 0 && secs > maxIdleSecs) || (maxSecsToLive > 0 && secs > maxSecsToLive);
}
/**
* Return true if a natural key is set.
*/
public boolean isUseNaturalKeyCache() {
return naturalKey != null;
}
/**
* Return the natural key property.
*/
public String getNaturalKey() {
return naturalKey;
}
/**
* Set the natural key property.
*/
public void setNaturalKey(String naturalKey) {
if (naturalKey == null || naturalKey.length() == 0){
naturalKey = null;
} else {
this.naturalKey = naturalKey.trim();
}
}
}
@@ -23,6 +23,7 @@ import com.avaje.ebeaninternal.api.SpiQuery;
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.transaction.DefaultPersistenceContext;
/**
@@ -73,7 +74,7 @@ public class DefaultBeanLoader {
return batchSize;
}
public void refreshMany(Object parentBean, String propertyName) {
public void refreshMany(EntityBean parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
@@ -92,7 +93,7 @@ public class DefaultBeanLoader {
for (int i = 0; i < batch.size(); i++) {
BeanCollection<?> bc = batch.get(i);
Object ownerBean = bc.getOwnerBean();
EntityBean ownerBean = bc.getOwnerBean();
Object id = many.getParentId(ownerBean);
idList.add(id);
}
@@ -136,14 +137,17 @@ public class DefaultBeanLoader {
}
} else if (loadRequest.isLoadCache()) {
Object parentId = desc.getId(bc.getOwnerBean());
desc.cachePutMany(many, bc, parentId);
desc.cacheManyPropPut(many, bc, parentId);
}
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
Object parentBean = bc.getOwnerBean();
EntityBean parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
//ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
@@ -151,11 +155,11 @@ public class DefaultBeanLoader {
loadManyInternal(parentBean, propertyName, null, false, null, onlyIds);
}
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, null, false);
}
private void loadManyInternal(Object 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();
PersistenceContext pc = ebi.getPersistenceContext();
@@ -179,13 +183,13 @@ public class DefaultBeanLoader {
pc.put(parentId, parentBean);
}
boolean useManyIdCache = beanCollection != null && parentDesc.cacheIsUseManyId();
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi != null && ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly)) {
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) {
return;
}
}
@@ -238,7 +242,7 @@ public class DefaultBeanLoader {
logger.debug("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean());
}
} else if (useManyIdCache) {
parentDesc.cachePutMany(many, beanCollection, parentId);
parentDesc.cacheManyPropPut(many, beanCollection, parentId);
}
}
}
@@ -267,7 +271,7 @@ public class DefaultBeanLoader {
for (int i = 0; i < batch.size(); i++) {
EntityBeanIntercept ebi = batch.get(i);
Object bean = ebi.getOwner();
EntityBean bean = ebi.getOwner();
Object id = desc.getId(bean);
idList.add(id);
}
@@ -290,17 +294,6 @@ public class DefaultBeanLoader {
PersistenceContext persistenceContext = ctx.getPersistenceContext();
// query the database
for (int i = 0; i < ebis.length; i++) {
Object parentBean = ebis[i].getParentBean();
if (parentBean != null) {
// Special case for OneToOne
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
Object parentId = parentDesc.getId(parentBean);
persistenceContext.put(parentId, parentBean);
}
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(beanType);
query.setMode(Mode.LAZYLOAD_BEAN);
@@ -323,33 +316,42 @@ public class DefaultBeanLoader {
if (loadRequest.isLoadCache()) {
for (int i = 0; i < list.size(); i++) {
desc.cachePutBeanData(list.get(i));
desc.cacheBeanPutData((EntityBean)list.get(i));
}
}
for (int i = 0; i < ebis.length; i++) {
if (ebis[i].isReference()) {
// The underlying row in DB was deleted. Mark this bean as 'failed'
// but allow processing to continue until it is accessed by client code
ebis[i].setLazyLoadFailure();
}
// Check if the underlying row in DB was deleted. Mark this bean as 'failed' if
// 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(Object bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN);
public void refresh(EntityBean bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN);
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
}
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) {
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
if (EntityType.EMBEDDED == desc.getEntityType()) {
// lazy loading on an embedded bean property
EntityBean embeddedOwner = (EntityBean)ebi.getEmbeddedOwner();
int ownerIndex = ebi.getEmbeddedOwnerIndex();
refreshBeanInternal(embeddedOwner, mode, ownerIndex);
}
Object id = desc.getId(bean);
if (pc == null) {
@@ -361,10 +363,10 @@ public class DefaultBeanLoader {
}
}
if (ebi != null) {
if (embeddedOwnerIndex == -1) {
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
// lazy loading and the bean cache is active
if (desc.loadFromCache(bean, ebi, id)) {
if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) {
return;
}
}
@@ -375,17 +377,14 @@ public class DefaultBeanLoader {
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
if (ebi != null) {
Object parentBean = ebi.getParentBean();
if (parentBean != null) {
// Special case for OneToOne
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
Object parentId = parentDesc.getId(parentBean);
pc.putIfAbsent(parentId, parentBean);
}
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
if (embeddedOwnerIndex > -1) {
String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex);
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
@@ -394,12 +393,13 @@ public class DefaultBeanLoader {
query.setMode(mode);
query.setId(id);
// make sure the query doesn't use the cache
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
if (embeddedOwnerIndex > -1 || mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
// make sure the query doesn't use the cache
query.setUseCache(false);
}
if (ebi != null && ebi.isReadOnly()) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
@@ -1,10 +1,11 @@
package com.avaje.ebeaninternal.server.core;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.BeanState;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
@@ -13,9 +14,9 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
*/
public class DefaultBeanState implements BeanState {
final EntityBean entityBean;
private final EntityBean entityBean;
final EntityBeanIntercept intercept;
private final EntityBeanIntercept intercept;
public DefaultBeanState(EntityBean entityBean){
this.entityBean = entityBean;
@@ -39,14 +40,16 @@ public class DefaultBeanState implements BeanState {
}
public Set<String> getLoadedProps() {
Set<String> props = intercept.getLoadedProps();
return props == null ? null : Collections.unmodifiableSet(props);
return intercept.getLoadedPropertyNames();
}
public Set<String> getChangedProps() {
Set<String> props = intercept.getChangedProps();
return props == null ? null : Collections.unmodifiableSet(props);
}
return intercept.getDirtyPropertyNames();
}
public Map<String,ValuePair> getDirtyValues() {
return intercept.getDirtyValues();
}
public boolean isReadOnly() {
return intercept.isReadOnly();
@@ -64,14 +67,8 @@ public class DefaultBeanState implements BeanState {
entityBean.removePropertyChangeListener(listener);
}
public void setLoaded(Set<String> loadedProperties) {
intercept.setLoadedProps(loadedProperties);
intercept.setLoaded();
public void setLoaded() {
intercept.setLoaded();
}
public void setReference() {
intercept.setReference();
}
}
@@ -30,6 +30,7 @@ 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;
@@ -90,13 +91,13 @@ import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.el.ElFilter;
import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQuery;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
import com.avaje.ebeaninternal.server.query.CallableQueryIds;
import com.avaje.ebeaninternal.server.query.CallableQueryList;
import com.avaje.ebeaninternal.server.query.CallableQueryRowCount;
import com.avaje.ebeaninternal.server.query.CallableSqlQueryList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagedList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery;
import com.avaje.ebeaninternal.server.query.QueryFutureIds;
import com.avaje.ebeaninternal.server.query.QueryFutureList;
@@ -105,7 +106,6 @@ import com.avaje.ebeaninternal.server.query.SqlQueryFutureList;
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate;
import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.text.csv.TCsvReader;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
@@ -121,6 +121,12 @@ public final class DefaultServer implements SpiEbeanServer {
private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class);
private static final int IGNORE_LEADING_ELEMENTS = 5;
private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15);
private final ServerConfig serverConfig;
private final String serverName;
private final DatabasePlatform databasePlatform;
@@ -138,9 +144,7 @@ public final class DefaultServer implements SpiEbeanServer {
* false;
*/
private final boolean rollbackOnChecked;
private final boolean defaultDeleteMissingChildren;
private final boolean defaultUpdateNullProperties;
/**
* Handles the save, delete, updateSql CallableSql.
*/
@@ -224,8 +228,7 @@ public final class DefaultServer implements SpiEbeanServer {
*/
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
ServerConfig serverConfig = config.getServerConfig();
this.serverConfig = config.getServerConfig();
this.objectGraphStats = new ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics>();
this.metaInfoManager = new DefaultMetaInfoManager(this);
this.serverCacheManager = cache;
@@ -247,11 +250,6 @@ public final class DefaultServer implements SpiEbeanServer {
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
this.defaultUpdateNullProperties = "true"
.equalsIgnoreCase(config.getServerConfig().getProperty("defaultUpdateNullProperties", "false"));
this.defaultDeleteMissingChildren = "true".equalsIgnoreCase(config.getServerConfig()
.getProperty("defaultDeleteMissingChildren", "true"));
this.rollbackOnChecked = GlobalProperties.getBoolean("ebean.transaction.rollbackOnChecked", true);
this.transactionManager = config.getTransactionManager();
this.transactionScopeManager = config.getTransactionScopeManager();
@@ -307,14 +305,6 @@ public final class DefaultServer implements SpiEbeanServer {
return collectQueryOrigins;
}
public boolean isDefaultDeleteMissingChildren() {
return defaultDeleteMissingChildren;
}
public boolean isDefaultUpdateNullProperties() {
return defaultUpdateNullProperties;
}
public int getLazyLoadBatchSize() {
return lazyLoadBatchSize;
}
@@ -323,6 +313,10 @@ public final class DefaultServer implements SpiEbeanServer {
return pstmtBatch;
}
public ServerConfig getServerConfig() {
return serverConfig;
}
public DatabasePlatform getDatabasePlatform() {
return databasePlatform;
}
@@ -522,12 +516,12 @@ public final class DefaultServer implements SpiEbeanServer {
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
beanLoader.refreshMany(parentBean, propertyName, t);
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName, t);
}
public void refreshMany(Object parentBean, String propertyName) {
beanLoader.refreshMany(parentBean, propertyName);
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
}
public void loadMany(LoadManyRequest loadRequest) {
@@ -542,7 +536,7 @@ public final class DefaultServer implements SpiEbeanServer {
public void refresh(Object bean) {
beanLoader.refresh(bean);
beanLoader.refresh(checkEntityBean(bean));
}
public void loadBean(LoadBeanRequest loadRequest) {
@@ -652,29 +646,21 @@ public final class DefaultServer implements SpiEbeanServer {
// we actually need to do a query because
// we don't know the type without the
// discriminator value
BeanProperty[] idProps = desc.propertiesId();
String idNames;
switch (idProps.length) {
case 0:
throw new PersistenceException("No ID properties for this type? " + desc);
case 1:
idNames = idProps[0].getName();
break;
default:
idNames = Arrays.toString(idProps);
idNames = idNames.substring(1, idNames.length() - 1);
BeanProperty idProp = desc.getIdProperty();
if (idProp == null) {
throw new PersistenceException("No ID properties for this type? " + desc);
}
// just select the id properties and
// the discriminator column (auto added)
Query<T> query = createQuery(type);
query.select(idNames).setId(id);
query.select(idProp.getName()).setId(id);
ref = query.findUnique();
} else {
// use the default reference options
ref = desc.createReference(null, id, null);
ref = desc.createReference(null, id);
}
if (ctx != null && (ref instanceof EntityBean)) {
@@ -1133,7 +1119,7 @@ public final class DefaultServer implements SpiEbeanServer {
if (Mode.LAZYLOAD_MANY.equals(query.getMode())) {
allowOneManyFetch = false;
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect() && query.getBackgroundFetchAfter() == 0) {
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) {
// convert ALL fetch joins to Many's to be query joins
// so that limit offset type SQL clauses work
allowOneManyFetch = false;
@@ -1190,23 +1176,7 @@ public final class DefaultServer implements SpiEbeanServer {
}
// Hit the L2 bean cache
Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), query.isReadOnly());
if (cachedBean != null) {
if (context == null) {
context = new DefaultPersistenceContext();
}
context.put(query.getId(), cachedBean);
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), query);
loadContext.setPersistenceContext(context);
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
ebi.setPersistenceContext(context);
loadContext.register(null, ebi);
}
return (T) cachedBean;
return beanDescriptor.cacheBeanGet(query, context);
}
@SuppressWarnings("unchecked")
@@ -1255,17 +1225,9 @@ public final class DefaultServer implements SpiEbeanServer {
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType());
if (desc.calculateUseNaturalKeyCache(q.isUseBeanCache())) {
// check if it is a find by unique id
NaturalKeyBindParam keyBindParam = q.getNaturalKeyBindParam();
if (keyBindParam != null && desc.cacheIsNaturalKey(keyBindParam.getName())) {
Object id2 = desc.cacheGetNaturalKeyId(keyBindParam.getValue());
if (id2 != null) {
SpiQuery<T> copy = q.copy();
copy.convertWhereNaturalKeyToId(id2);
return findId(copy, t);
}
}
T bean = desc.cacheNaturalKeyLookup(q, (SpiTransaction)t);
if (bean != null) {
return bean;
}
// a query that is expected to return either 0 or 1 rows
@@ -1273,9 +1235,10 @@ public final class DefaultServer implements SpiEbeanServer {
if (list.size() == 0) {
return null;
} else if (list.size() > 1) {
String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]";
throw new PersistenceException(m);
throw new PersistenceException("Unique expecting 0 or 1 rows but got [" + list.size() + "]");
} else {
return list.get(0);
}
@@ -1458,6 +1421,12 @@ public final class DefaultServer implements SpiEbeanServer {
return new LimitOffsetPagingQuery<T>(this, spiQuery, pageSize);
}
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
}
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
@@ -1604,82 +1573,116 @@ public final class DefaultServer implements SpiEbeanServer {
* Save the bean with an explicit transaction.
*/
public void save(Object bean, Transaction t) {
if (bean == null) {
throw new NullPointerException(Message.msg("bean.isnull"));
}
persister.save(bean, t);
persister.save(checkEntityBean(bean), t);
}
/**
* Force an update using the bean updating non-null properties.
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
public void update(Object bean) {
update(bean, null, null);
update(bean, null);
}
/**
* Force an update using the bean explicitly stating which properties to
* include in the update.
*/
public void update(Object bean, Set<String> updateProps) {
update(bean, updateProps, null);
}
/**
* Force an update using the bean updating non-null properties.
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
public void update(Object bean, Transaction t) {
update(bean, null, t);
persister.update(checkEntityBean(bean), t);
}
/**
* Force an update using the bean explicitly stating which properties to
* include in the update.
* Update the bean specifying the deleteMissingChildren option.
*/
public void update(Object bean, Set<String> updateProps, Transaction t) {
update(bean, updateProps, t, defaultDeleteMissingChildren, defaultUpdateNullProperties);
public void update(Object bean, Transaction t, boolean deleteMissingChildren) {
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
}
/**
* Force an update using the bean explicitly stating which properties to
* include in the update.
* Update all beans in the collection.
*/
public void update(Object bean, Set<String> updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) {
if (bean == null) {
throw new NullPointerException(Message.msg("bean.isnull"));
public void update(Collection<?> beans) {
update(beans, null);
}
/**
* Update all beans in the collection with an explicit transaction.
*/
public void update(Collection<?> beans, Transaction t) {
if (beans == null || beans.isEmpty()) {
// Nothing to update?
return;
}
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
for (Object bean : beans) {
update(checkEntityBean(bean), trans);
}
wrap.commitIfCreated();
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
persister.forceUpdate(bean, updateProps, t, deleteMissingChildren, updateNullProperties);
}
/**
* Force the bean to be saved with an explicit insert.
* <p>
* Typically you would use save() and let Ebean determine if the bean should
* be inserted or updated. This can be useful when you are transferring data
* between databases and want to explicitly insert a bean into a different
* database that it came from.
* </p>
* Insert the bean.
*/
public void insert(Object bean) {
insert(bean, null);
}
/**
* Force the bean to be saved with an explicit insert.
* <p>
* Typically you would use save() and let Ebean determine if the bean should
* be inserted or updated. This can be useful when you are transferring data
* between databases and want to explicitly insert a bean into a different
* database that it came from.
* </p>
* Insert the bean with a transaction.
*/
public void insert(Object bean, Transaction t) {
if (bean == null) {
throw new NullPointerException(Message.msg("bean.isnull"));
}
persister.forceInsert(bean, t);
persister.insert(checkEntityBean(bean), t);
}
/**
* Insert all beans in the collection.
*/
public void insert(Collection<?> beans) {
insert(beans, null);
}
/**
* Insert all beans in the collection with a transaction.
*/
public void insert(Collection<?> beans, Transaction t) {
if (beans == null || beans.isEmpty()) {
// Nothing to insert?
return;
}
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
for (Object bean : beans) {
persister.insert(checkEntityBean(bean), trans);
}
wrap.commitIfCreated();
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
private EntityBean checkEntityBean(Object bean) {
if (bean == null) {
throw new IllegalArgumentException(Message.msg("bean.isnull"));
}
if (bean instanceof EntityBean == false) {
throw new IllegalArgumentException("Was expecting an EntityBean but got a "+bean.getClass());
}
return (EntityBean)bean;
}
/**
* Delete the associations (from the intersection table) of a ManyToMany given
* the owner bean and the propertyName of the ManyToMany collection.
@@ -1700,10 +1703,11 @@ public final class DefaultServer implements SpiEbeanServer {
*/
public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
EntityBean owner = checkEntityBean(ownerBean);
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
int rc = persister.deleteManyToManyAssociations(ownerBean, propertyName, trans);
int rc = persister.deleteManyToManyAssociations(owner, propertyName, trans);
wrap.commitIfCreated();
return rc;
@@ -1727,11 +1731,12 @@ public final class DefaultServer implements SpiEbeanServer {
*/
public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
EntityBean owner = checkEntityBean(ownerBean);
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
persister.saveManyToManyAssociations(ownerBean, propertyName, trans);
persister.saveManyToManyAssociations(owner, propertyName, trans);
wrap.commitIfCreated();
@@ -1747,21 +1752,12 @@ public final class DefaultServer implements SpiEbeanServer {
public void saveAssociation(Object ownerBean, String propertyName, Transaction t) {
if (ownerBean instanceof EntityBean) {
Set<String> loadedProps = ((EntityBean) ownerBean)._ebean_getIntercept().getLoadedProps();
if (loadedProps != null && !loadedProps.contains(propertyName)) {
// skip as property is not actually loaded in this partially
// loaded bean
logger.debug("Skip saveAssociation as property " + propertyName + " is not loaded");
return;
}
}
EntityBean owner = checkEntityBean(ownerBean);
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
persister.saveAssociation(ownerBean, propertyName, trans);
persister.saveAssociation(owner, propertyName, trans);
wrap.commitIfCreated();
@@ -1787,6 +1783,14 @@ public final class DefaultServer implements SpiEbeanServer {
return save(c.iterator(), null);
}
/**
* Perform an update or insert on each bean in the collection. Returns the
* number of beans that where saved.
*/
public int save(Collection<?> c, Transaction t) {
return save(c.iterator(), t);
}
/**
* Save all beans in the iterator with an explicit transaction.
*/
@@ -1797,7 +1801,7 @@ public final class DefaultServer implements SpiEbeanServer {
SpiTransaction trans = wrap.transaction;
int saveCount = 0;
while (it.hasNext()) {
Object bean = it.next();
EntityBean bean = checkEntityBean(it.next());
persister.save(bean, trans);
saveCount++;
}
@@ -1861,10 +1865,8 @@ public final class DefaultServer implements SpiEbeanServer {
* Delete the bean with the explicit transaction.
*/
public void delete(Object bean, Transaction t) {
if (bean == null) {
throw new NullPointerException(Message.msg("bean.isnull"));
}
persister.delete(bean, t);
persister.delete(checkEntityBean(bean), t);
}
/**
@@ -1892,7 +1894,7 @@ public final class DefaultServer implements SpiEbeanServer {
SpiTransaction trans = wrap.transaction;
int deleteCount = 0;
while (it.hasNext()) {
Object bean = it.next();
EntityBean bean = checkEntityBean(it.next());
persister.delete(bean, trans);
deleteCount++;
}
@@ -1995,13 +1997,14 @@ public final class DefaultServer implements SpiEbeanServer {
}
public Object getBeanId(Object bean) {
EntityBean eb = checkEntityBean(bean);
BeanDescriptor<?> desc = getBeanDescriptor(bean.getClass());
if (desc == null) {
String m = bean.getClass().getName() + " is NOT an Entity Bean registered with this server?";
throw new PersistenceException(m);
}
return desc.getId(bean);
return desc.getId(eb);
}
/**
@@ -2067,8 +2070,6 @@ public final class DefaultServer implements SpiEbeanServer {
return transactionManager.createQueryTransaction();
}
private static final int IGNORE_LEADING_ELEMENTS = 5;
private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15);
/**
* Create a CallStack object.
@@ -19,50 +19,55 @@ import com.avaje.ebeaninternal.util.ValueUtil;
public class DiffHelp {
/**
* Return a map of the differences between a and b.
* <p>
* A and B must be of the same type. B can be null, in which case the
* 'OldValues' of a is used to compare with (as B).
* </p>
* <p>
* This intentionally does not include as OneToMany or ManyToMany
* properties.
* </p>
*/
/**
* Return a map of the differences between a and b.
* <p>
* A and B must be of the same type. B can be null, in which case the 'dirty
* values' of a is returned.
* </p>
* <p>
* This intentionally does not include as OneToMany or ManyToMany properties.
* </p>
*/
public Map<String, ValuePair> diff(Object a, Object b, BeanDescriptor<?> desc) {
boolean oldValues = false;
if (a instanceof EntityBean == false) {
throw new IllegalArgumentException("First bean expected to be an enhanced EntityBean? bean:"+a);
}
if (b != null) {
if (b instanceof EntityBean == false) {
throw new IllegalArgumentException("Second bean expected to be an enhanced EntityBean? bean:"+b);
}
if (!a.getClass().isAssignableFrom(b.getClass())) {
throw new IllegalArgumentException("Second bean not assignable to the first bean?");
}
}
if (b == null) {
// get the old values from a
if (a instanceof EntityBean) {
EntityBean eb = (EntityBean) a;
b = eb._ebean_getIntercept().getOldValues();
oldValues = true;
}
return ((EntityBean) a)._ebean_getIntercept().getDirtyValues();
}
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
if (b == null) {
return map;
}
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
diff(null, map, (EntityBean)a, (EntityBean)b, desc);
return map;
}
public void diff(String prefix, Map<String, ValuePair> map, EntityBean first, EntityBean sec, BeanDescriptor<?> desc) {
// check the simple properties
BeanProperty[] base = desc.propertiesBaseScalar();
for (int i = 0; i < base.length; i++) {
Object aval = base[i].getValue(a);
Object bval = base[i].getValue(b);
Object aval = base[i].getValue(first);
Object bval = base[i].getValue(sec);
if (!ValueUtil.areEqual(aval, bval)) {
map.put(base[i].getName(), new ValuePair(aval, bval));
String propName = (prefix == null) ? base[i].getName() : prefix + base[i].getName();
map.put(propName, new ValuePair(aval, bval));
}
}
diffAssocOne(a, b, desc, map);
diffEmbedded(a, b, desc, map, oldValues);
return map;
diffAssocOne(prefix, first, sec, desc, map);
diffEmbedded(prefix, first, sec, desc, map);
}
/**
@@ -72,40 +77,24 @@ public class DiffHelp {
* determined to be different as is added to the map.
* </p>
*/
private void diffEmbedded(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> map,
boolean oldValues) {
private void diffEmbedded(String prefix, EntityBean a, EntityBean b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
BeanPropertyAssocOne<?>[] emb = desc.propertiesEmbedded();
for (int i = 0; i < emb.length; i++) {
Object aval = emb[i].getValue(a);
Object bval = emb[i].getValue(b);
if (oldValues) {
bval = ((EntityBean) bval)._ebean_getIntercept().getOldValues();
if (bval == null) {
continue;
}
}
EntityBean aval = (EntityBean)emb[i].getValue(a);
EntityBean bval = (EntityBean)emb[i].getValue(b);
if (!isBothNull(aval, bval)) {
String propName = (prefix == null) ? emb[i].getName() : prefix + emb[i].getName();
if (isDiffNull(aval, bval)) {
// one of the embedded beans is null
map.put(emb[i].getName(), new ValuePair(aval, bval));
map.put(propName, new ValuePair(aval, bval));
} else {
// if ANY of the properties in an Embedded bean is
// different, treat the whole bean as being different
BeanProperty[] props = emb[i].getProperties();
for (int j = 0; j < props.length; j++) {
Object aEmbPropVal = props[j].getValue(aval);
Object bEmbPropVal = props[j].getValue(bval);
if (!ValueUtil.areEqual(aEmbPropVal, bEmbPropVal)) {
// if one prop is different put the
// embedded bean in the map
map.put(emb[i].getName(), new ValuePair(aval, bval));
}
}
// recursively diff into the embedded bean
BeanDescriptor<?> embDesc = emb[i].getTargetDescriptor();
diff(emb[i].getName()+".", map, aval, bval, embDesc);
}
}
}
@@ -115,7 +104,7 @@ public class DiffHelp {
* If the properties are different by null OR if the id value is different,
* then add the Assoc One bean to the map.
*/
private void diffAssocOne(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
private void diffAssocOne(String prefix, EntityBean a, EntityBean b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
BeanPropertyAssocOne<?>[] ones = desc.propertiesOne();
@@ -124,20 +113,21 @@ public class DiffHelp {
Object bval = ones[i].getValue(b);
if (!isBothNull(aval, bval)) {
String propName = (prefix == null) ? ones[i].getName() : prefix + ones[i].getName();
if (isDiffNull(aval, bval)) {
// one of them is/was null
map.put(ones[i].getName(), new ValuePair(aval, bval));
map.put(propName, new ValuePair(aval, bval));
} else {
// check to see if the Id properties
// are different
BeanDescriptor<?> oneDesc = ones[i].getTargetDescriptor();
Object aOneId = oneDesc.getId(aval);
Object bOneId = oneDesc.getId(bval);
Object aOneId = oneDesc.getId((EntityBean)aval);
Object bOneId = oneDesc.getId((EntityBean)bval);
if (!ValueUtil.areEqual(aOneId, bOneId)) {
// the ids are different
map.put(ones[i].getName(), new ValuePair(aval, bval));
map.put(propName, new ValuePair(aval, bval));
}
}
}
@@ -116,7 +116,7 @@ public class InternalConfiguration {
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
serverConfig, beanDescriptorManager, this.getBootupClasses());
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder, backgroundExecutor);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
@@ -29,6 +29,7 @@ import com.avaje.ebeaninternal.server.deploy.DeployPropertyParserMap;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Wraps the objects involved in executing a Query.
@@ -43,25 +44,18 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private final BeanFinder<T> finder;
private final LoadContext graphContext;
private final Boolean readOnly;
private final RawSql rawSql;
private LoadContext loadContext;
private PersistenceContext persistenceContext;
private HashQuery cacheKey;
private HashQueryPlan queryPlanHash;
/**
* Flag set if background fetching taking place. In this case the transaction
* is rolled back by the background fetching thread. Background fetching
* always takes place in its own transaction.
*/
private boolean backgroundFetching;
/**
* Create the InternalQueryRequest.
*/
@@ -75,13 +69,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
graphContext.registerSecondaryQueries(query);
}
public void executeSecondaryQueries(int defaultQueryBatch) {
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
}
/**
@@ -93,7 +84,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return graphContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
return loadContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
}
/**
@@ -114,7 +105,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Return the graph context for this query.
*/
public LoadContext getGraphContext() {
return graphContext;
return loadContext;
}
/**
@@ -163,12 +154,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
@Override
public void initTransIfRequired() {
// first check if the query requires its own transaction
if (query.createOwnTransaction()) {
// using background fetch or query listener etc
transaction = ebeanServer.createQueryTransaction();
createdTransaction = true;
} else if (transaction == null) {
if (transaction == null) {
// maybe a current one
transaction = ebeanServer.getCurrentServerTransaction();
if (transaction == null) {
@@ -177,10 +163,20 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
createdTransaction = true;
}
}
// initialise the persistenceContext and loadContext
this.persistenceContext = getPersistenceContext(query, transaction);
this.graphContext.setPersistenceContext(persistenceContext);
this.loadContext = new DLoadContext(this);
this.loadContext.registerSecondaryQueries(query);
}
/**
* For iterate queries reset the persistenceContext and loadContext.
*/
public void flushPersistenceContextOnIterate() {
persistenceContext = new DefaultPersistenceContext();
loadContext.setPersistenceContext(persistenceContext);
}
/**
* Get the TransactionContext either explicitly set on the query or
* transaction scoped.
@@ -202,19 +198,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* </p>
*/
public void endTransIfRequired() {
if (createdTransaction && !backgroundFetching) {
if (createdTransaction) {
// we can rollback as readOnly transaction
transaction.rollback();
}
}
/**
* This query is using background fetching.
*/
public void setBackgroundFetching() {
backgroundFetching = true;
}
/**
* Return true if this is a find by id (rather than List Set or Map).
*/
@@ -277,12 +266,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public Map<?, ?> findMap() {
String mapKey = query.getMapKey();
if (mapKey == null) {
BeanProperty[] ids = beanDescriptor.propertiesId();
if (ids.length == 1) {
query.setMapKey(ids[0].getName());
BeanProperty idProp = beanDescriptor.getIdProperty();
if (idProp != null) {
query.setMapKey(idProp.getName());
} else {
String msg = "No mapKey specified for query";
throw new PersistenceException(msg);
throw new PersistenceException("No mapKey specified for query");
}
}
return (Map<?, ?>) queryEngine.findMany(this);
@@ -356,8 +344,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
cacheKey = query.queryHash();
// TODO: Sort out returning BeanCollection from L2 cache
return null;
return beanDescriptor.queryCacheGet(cacheKey);
}
public void putToQueryCache(BeanCollection<T> queryResult) {
@@ -380,8 +367,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
}
public void flushPersistenceContextOnIterate() {
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
/**
* Return true if the request wants to log the secondary queries (test purpose).
*/
public boolean isLogSecondaryQuery() {
return query.isLogSecondaryQuery();
}
}
@@ -12,17 +12,17 @@ import com.avaje.ebeaninternal.server.persist.PersistExecute;
public abstract class PersistRequest extends BeanRequest implements BatchPostExecute {
public enum Type {
INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
DETERMINE, INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
};
boolean persistCascade;
protected boolean persistCascade;
/**
* One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL.
*/
Type type;
protected Type type;
final PersistExecute persistExecute;
protected final PersistExecute persistExecute;
/**
* Used by CallableSqlRequest and UpdateSqlRequest.
@@ -31,7 +31,7 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
super(server, t);
this.persistExecute = persistExecute;
}
/**
* Execute a the request or queue/batch it for later execution.
*/
@@ -90,14 +90,6 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
return type;
}
/**
* Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or
* CALLABLESQL.
*/
public void setType(Type type) {
this.type = type;
}
/**
* Return true if save and delete should cascade.
*/
@@ -1,11 +1,14 @@
package com.avaje.ebeaninternal.server.core;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.OptimisticLockException;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
@@ -19,6 +22,7 @@ import com.avaje.ebeaninternal.api.TransactionEvent;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeaninternal.server.persist.PersistExecute;
import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
@@ -28,150 +32,127 @@ import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
/**
* PersistRequest for insert update or delete of a bean.
*/
public class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
protected final BeanManager<T> beanManager;
private final BeanManager<T> beanManager;
protected final BeanDescriptor<T> beanDescriptor;
private final BeanDescriptor<T> beanDescriptor;
protected final BeanPersistListener<T> beanPersistListener;
private final BeanPersistListener<T> beanPersistListener;
/**
* For per post insert update delete control.
*/
protected final BeanPersistController controller;
private final BeanPersistController controller;
/**
* The bean being persisted.
*/
private final T bean;
private final EntityBean entityBean;
/**
* The associated intercept.
*/
protected final EntityBeanIntercept intercept;
private final EntityBeanIntercept intercept;
/**
* The parent bean for unidirectional save.
*/
protected final Object parentBean;
private final Object parentBean;
protected final boolean isDirty;
private final boolean dirty;
/**
* The bean being persisted.
*/
protected final T bean;
/**
* Old values used for concurrency checking.
*/
protected T oldValues;
/**
* The concurrency mode used for update or delete.
*/
protected ConcurrencyMode concurrencyMode;
protected final Set<String> loadedProps;
private ConcurrencyMode concurrencyMode;
/**
* The unique id used for logging summary.
*/
protected Object idValue;
private Object idValue;
/**
* Hash value used to handle cascade delete both ways in a relationship.
*/
protected Integer beanHash;
protected Integer beanIdentityHash;
private Integer beanHash;
protected final Set<String> changedProps;
private boolean notifyCache;
protected boolean notifyCache;
private boolean statelessUpdate;
private boolean deleteMissingChildren;
private boolean updateNullProperties;
private final Set<String> dirtyPropertyNames;
/**
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
* appropriate caches are updated in that case.
*/
private boolean updatedManysOnly;
/**
* Used for forced update of a bean.
* Many properties that were cascade saved (and hence might need caches updated later).
*/
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, Set<String> updateProps, ConcurrencyMode concurrencyMode) {
private List<BeanPropertyAssocMany<?>> updatedManys;
super(server, t, persistExecute);
this.beanManager = mgr;
this.beanDescriptor = mgr.getBeanDescriptor();
this.beanPersistListener = beanDescriptor.getPersistListener();
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.concurrencyMode = beanDescriptor.getConcurrencyMode();
this.concurrencyMode = concurrencyMode;
this.loadedProps = updateProps;
this.changedProps = updateProps;
this.isDirty = true;
this.oldValues = bean;
if (bean instanceof EntityBean) {
this.intercept = ((EntityBean) bean)._ebean_getIntercept();
} else {
this.intercept = null;
}
}
@SuppressWarnings("unchecked")
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr,
SpiTransaction t, PersistExecute persistExecute) {
SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type) {
super(server, t, persistExecute);
this.entityBean = (EntityBean)bean;
this.intercept = entityBean._ebean_getIntercept();
this.beanManager = mgr;
this.beanDescriptor = mgr.getBeanDescriptor();
this.beanPersistListener = beanDescriptor.getPersistListener();
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.persistCascade = t.isPersistCascade();
}
if (this.type == Type.UPDATE && intercept.isNew() ) {
// 'stateless update' - set bean up for doing update
intercept.setNewBeanForUpdate();
}
// derive the set of property names now as we will pass them to a beanPersistListener later
this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames();
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.concurrencyMode = beanDescriptor.getConcurrencyMode();
this.intercept = ((EntityBean) bean)._ebean_getIntercept();
if (intercept.isReference()) {
// allowed to delete reference objects
// with no concurrency checking
this.concurrencyMode = ConcurrencyMode.NONE;
}
// this is ok to not use isNewOrDirty() as used for updates only
this.isDirty = intercept.isDirty();
if (!isDirty) {
this.changedProps = intercept.getChangedProps();
} else {
// merge changed properties on the bean with changed embedded beans
Set<String> beanChangedProps = intercept.getChangedProps();
Set<String> dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean);
this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded);
}
this.loadedProps = intercept.getLoadedProps();
this.oldValues = (T) intercept.getOldValues();
this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept);
this.dirty = intercept.isDirty();
}
/**
* Merge the changed properties for the bean and embedded beans.
* Return true if this is an insert request.
*/
private Set<String> mergeChangedProperties(Set<String> beanChangedProps, Set<String> embChanged) {
if (embChanged == null) {
return beanChangedProps;
} else if (beanChangedProps == null) {
return embChanged;
} else {
beanChangedProps.addAll(embChanged);
return beanChangedProps;
}
}
public boolean isInsert() {
return Type.INSERT == type;
}
@Override
public Set<String> getLoadedProperties() {
return intercept.getLoadedPropertyNames();
}
@Override
public Set<String> getUpdatedProperties() {
return intercept.getDirtyPropertyNames();
}
@Override
public Map<String, ValuePair> getUpdatedValues() {
return intercept.getDirtyValues();
}
public boolean isNotify(TransactionEvent txnEvent) {
this.notifyCache = beanDescriptor.isCacheNotify();
return notifyCache || isNotifyPersistListener();
}
public boolean isNotifyCache() {
return notifyCache;
}
public boolean isNotifyPersistListener() {
return beanPersistListener != null;
}
@@ -183,10 +164,10 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
if (notifyCache) {
switch (type) {
case INSERT:
beanDescriptor.cacheInsert(idValue, this);
beanDescriptor.cacheHandleInsert(idValue, this);
break;
case UPDATE:
beanDescriptor.cacheUpdate(idValue, this);
beanDescriptor.cacheHandleUpdate(idValue, this);
break;
case DELETE:
// Bean deleted from cache early via postDelete()
@@ -199,7 +180,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
beanPersistMap.add(beanDescriptor, type, idValue);
beanPersistMap.add(beanDescriptor, type, idValue);
}
public boolean notifyLocalPersistListener() {
@@ -212,7 +193,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
return beanPersistListener.inserted(bean);
case UPDATE:
return beanPersistListener.updated(bean, getUpdatedProperties());
return beanPersistListener.updated(bean, dirtyPropertyNames);
case DELETE:
return beanPersistListener.deleted(bean);
@@ -229,7 +210,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
/**
* Return true if this bean has been already been persisted
* (inserted/updated or deleted) in this transaction.
* (inserted or updated) in this transaction.
*/
public boolean isRegisteredBean() {
return transaction.isRegisteredBean(bean);
@@ -247,7 +228,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
*/
private Integer getBeanHash() {
if (beanHash == null) {
Object id = beanDescriptor.getId(bean);
Object id = beanDescriptor.getId(entityBean);
int hc = 31 * bean.getClass().getName().hashCode();
if (id != null) {
hc += id.hashCode();
@@ -276,21 +257,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
}
}
/**
* Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or
* CALLABLESQL.
*/
@Override
public void setType(Type type) {
this.type = type;
notifyCache = beanDescriptor.isCacheNotify();
if (type == Type.DELETE || type == Type.UPDATE) {
if (oldValues == null) {
oldValues = bean;
}
}
}
public BeanManager<T> getBeanManager() {
return beanManager;
}
@@ -302,13 +268,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
return beanDescriptor;
}
/**
* Return true if this is a stateless update.
*/
public boolean isStatelessUpdate() {
return statelessUpdate;
}
/**
* Return true if a stateless update should also delete any missing details
* beans.
@@ -318,25 +277,10 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
}
/**
* Return true if null properties should be updated (treated as loaded) for
* stateless updates.
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
*/
public boolean isUpdateNullProperties() {
return updateNullProperties;
}
/**
* Set to true if this is a stateless update.
* <p>
* By Stateless it means that the bean was not previously fetched (and so
* does not have it's previous state) so we are doing an update on a bean
* that was probably created from JSON or XML.
* </p>
*/
public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren, boolean updateNullProperties) {
this.statelessUpdate = statelessUpdate;
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
this.deleteMissingChildren = deleteMissingChildren;
this.updateNullProperties = updateNullProperties;
}
/**
@@ -344,7 +288,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* for EntityBeans that have not been modified.
*/
public boolean isDirty() {
return isDirty;
return dirty;
}
/**
@@ -354,20 +298,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
return concurrencyMode;
}
/**
* Set loaded properties when generated values has added properties such as
* created and updated timestamps.
*/
public void setLoadedProps(Set<String> additionalProps) {
if (intercept != null) {
intercept.setLoadedProps(additionalProps);
}
}
public Set<String> getLoadedProperties() {
return loadedProps;
}
/**
* Returns a description of the request. This is typically the bean class
* name or the base table for MapBeans.
@@ -387,25 +317,22 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
return bean;
}
/**
public EntityBean getEntityBean() {
return entityBean;
}
/**
* Return the Id value for the bean.
*/
public Object getBeanId() {
return beanDescriptor.getId(bean);
return beanDescriptor.getId(entityBean);
}
public BeanDelta createDeltaBean() {
return new BeanDelta(beanDescriptor, getBeanId());
}
/**
* Get the old values bean. This is used to perform optimistic concurrency
* checking on updates and deletes.
*/
public T getOldValues() {
return oldValues;
}
/**
* Return the parent bean for cascading save with unidirectional
* relationship.
@@ -434,11 +361,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* bean).
*/
public boolean isLoadedProperty(BeanProperty prop) {
if (loadedProps == null) {
return true;
} else {
return loadedProps.contains(prop.getName());
}
return intercept.isLoadedProperty(prop.getPropertyIndex());
}
@Override
@@ -485,13 +408,8 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
*/
public void setGeneratedKey(Object idValue) {
if (idValue != null) {
// set back to the bean so that we can use the same bean later
// for update [refer ebeanIntercept.setLoaded(true)].
idValue = beanDescriptor.convertSetId(idValue, bean);
// remember it for logging summary
this.idValue = idValue;
this.idValue = beanDescriptor.convertSetId(idValue, entityBean);
}
}
@@ -518,7 +436,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.cacheDelete(idValue, this);
beanDescriptor.cacheHandleDelete(idValue, this);
}
/**
@@ -597,18 +515,18 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* </p>
*/
public ConcurrencyMode determineConcurrencyMode() {
if (loadedProps != null) {
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.firstVersionProperty();
if (prop != null && loadedProps.contains(prop.getName())) {
// OK to use version property
} else {
concurrencyMode = ConcurrencyMode.ALL;
}
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.getVersionProperty();
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
// OK to use version property
} else {
concurrencyMode = ConcurrencyMode.NONE;
}
}
return concurrencyMode;
}
@@ -619,7 +537,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* </p>
*/
public boolean isDynamicUpdateSql() {
return beanDescriptor.isUpdateChangesOnly() || (loadedProps != null);
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
}
/**
@@ -630,37 +548,74 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* </p>
*/
public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) {
if (beanDescriptor.isUpdateChangesOnly()) {
return new GenerateDmlRequest(emptyStringAsNull, changedProps, loadedProps, oldValues);
} else {
return new GenerateDmlRequest(emptyStringAsNull, loadedProps, loadedProps, oldValues);
}
}
/**
* Return the updated properties. If this returns null then all the
* properties on the bean where updated.
*/
public Set<String> getUpdatedProperties() {
if (changedProps != null) {
return changedProps;
}
return loadedProps;
return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly());
}
/**
* Test if the property value has changed and if so include it in the
* update.
*/
public boolean hasChanged(BeanProperty prop) {
if (changedProps == null) {
return false;
}
return changedProps.contains(prop.getName());
public boolean isAddToUpdate(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
public List<DerivedRelationshipData> getDerivedRelationships() {
return transaction.getDerivedRelationship(bean);
public List<DerivedRelationshipData> getDerivedRelationships() {
return transaction.getDerivedRelationship(bean);
}
public void postInsert() {
// mark all properties as loaded after an insert to support immediate update
int len = intercept.getPropertyLength();
for (int i = 0; i < len; i++) {
intercept.setLoadedProperty(i);
}
}
public boolean isReference() {
return beanDescriptor.isReference(intercept);
}
/**
* This many property has been cascade saved. Keep note of this and update the 'many property'
* cache on post commit.
*/
public void addUpdatedManyProperty(BeanPropertyAssocMany<?> updatedAssocMany) {
//if (notifyCache) {
if (updatedManys == null) {
updatedManys = new ArrayList<BeanPropertyAssocMany<?>>(5);
}
updatedManys.add(updatedAssocMany);
//}
}
/**
* Return the list of cascade updated many properties (can be null).
*/
public List<BeanPropertyAssocMany<?>> getUpdatedManyCollections() {
return updatedManys;
}
/**
* Check if any of its many properties where cascade saved and hence we need to update related
* many property caches.
*/
public void checkUpdatedManysOnly() {
if (!dirty && updatedManys != null) {
// set the flag and register for post commit processing if there
// is caching or registered listeners
if (idValue == null) {
this.idValue = beanDescriptor.getId(entityBean);
}
updatedManysOnly = true;
addEvent();
}
}
/**
* Return true if only many properties where updated.
*/
public boolean isUpdatedManysOnly() {
return updatedManysOnly;
}
}
@@ -1,12 +1,12 @@
package com.avaje.ebeaninternal.server.core;
import java.util.Collection;
import java.util.Set;
import com.avaje.ebean.CallableSql;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.Update;
import com.avaje.ebean.bean.EntityBean;
/**
@@ -14,26 +14,31 @@ import com.avaje.ebean.Update;
*/
public interface Persister {
/**
* Update the bean.
*/
public void update(EntityBean entityBean, Transaction t);
/**
* Force an Update using the given bean.
* Update the bean specifying deleteMissingChildren.
*/
public void forceUpdate(Object entityBean, Set<String> updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties);
public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren);
/**
* Force an Insert using the given bean.
*/
public void forceInsert(Object entityBean, Transaction t);
public void insert(EntityBean entityBean, Transaction t);
/**
* Insert or update the bean depending on its state.
*/
public void save(Object entityBean, Transaction t);
public void save(EntityBean entityBean, Transaction t);
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
*/
public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t);
public void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t);
/**
* Save an association (OneToMany, ManyToOne, OneToOne or ManyToMany).
@@ -45,12 +50,12 @@ public interface Persister {
* @param t
* the transaction to use.
*/
public void saveAssociation(Object parentBean, String propertyName, Transaction t);
public void saveAssociation(EntityBean parentBean, String propertyName, Transaction t);
/**
* Delete the associations of a ManyToMany given the owner bean and the property name of the ManyToMany.
*/
public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t);
public int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t);
/**
* Delete a bean given it's type and id value.
@@ -63,7 +68,7 @@ public interface Persister {
/**
* Delete the bean.
*/
public void delete(Object entityBean, Transaction t);
public void delete(EntityBean entityBean, Transaction t);
/**
* Delete multiple beans given a collection of Id values.
@@ -201,12 +201,12 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
}
BeanProperty[] ids = descriptor.propertiesId();
BeanProperty idProp = descriptor.getIdProperty();
if (ids.length == 0){
if (idProp == null){
// No comma + new line
ctx.removeLast().removeLast();
} else if (ids.length > 1 || ddl.isInlinePrimaryKeyConstraint()) {
} else if (ddl.isInlinePrimaryKeyConstraint()) {
// The Primary Key constraint was inlined with the column
// ... No comma + new line
ctx.removeLast().removeLast();
@@ -216,7 +216,7 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
String pkName = ddl.getPrimaryKeyName(table);
ctx.write(" constraint ").write(pkName).write(" primary key (");
VisitorUtil.visit(ids, new AbstractPropertyVisitor() {
VisitorUtil.visit(idProp, new AbstractPropertyVisitor() {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
@@ -43,36 +43,36 @@ public class VisitorUtil {
/**
* Visit the bean using a visitor.
*/
public static void visitBean(BeanDescriptor<?> desc, BeanVisitor visitor) {
public static void visitBean(BeanDescriptor<?> desc, BeanVisitor visitor) {
if (visitor.visitBean(desc)) {
if (visitor.visitBean(desc)) {
BeanProperty[] propertiesId = desc.propertiesId();
for (int i = 0; i < propertiesId.length; i++) {
visit(visitor, propertiesId[i]);
}
BeanPropertyAssocOne<?> unidirectional = desc.getUnidirectional();
if (unidirectional != null){
visit(visitor, unidirectional);
}
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
for (int i = 0; i < propertiesNonTransient.length; i++) {
BeanProperty p = propertiesNonTransient[i];
if (!p.isFormula() && !p.isSecondaryTable()){
visit(visitor, p);
}
}
visitor.visitBeanEnd(desc);
}
}
private static void visit(BeanVisitor visitor, BeanProperty p) {
PropertyVisitor pv = visitor.visitProperty(p);
if (pv != null){
visit(p, pv);
BeanProperty idProp = desc.getIdProperty();
if (idProp != null) {
visit(visitor, idProp);
}
BeanPropertyAssocOne<?> unidirectional = desc.getUnidirectional();
if (unidirectional != null) {
visit(visitor, unidirectional);
}
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
for (int i = 0; i < propertiesNonTransient.length; i++) {
BeanProperty p = propertiesNonTransient[i];
if (!p.isFormula() && !p.isSecondaryTable()) {
visit(visitor, p);
}
}
}
visitor.visitBeanEnd(desc);
}
}
private static void visit(BeanVisitor visitor, BeanProperty p) {
PropertyVisitor pv = visitor.visitProperty(p);
if (pv != null) {
visit(p, pv);
}
}
/**
* Visit all the properties.
@@ -8,6 +8,7 @@ import com.avaje.ebean.Transaction;
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;
/**
@@ -41,22 +42,22 @@ public interface BeanCollectionHelp<T> {
/**
* Add a bean to the List Set or Map.
*/
public void add(BeanCollection<?> collection, Object bean);
public void add(BeanCollection<?> collection, EntityBean bean);
/**
* Create a lazy loading proxy for a List Set or Map.
*/
public BeanCollection<T> createReference(Object parentBean, String propertyName);
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName);
/**
* Refresh the List Set or Map.
*/
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean);
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean);
/**
* Apply the new refreshed BeanCollection to the appropriate property of the parent bean.
*/
public void refresh(BeanCollection<?> bc, Object parentBean);
public void refresh(BeanCollection<?> bc, EntityBean parentBean);
/**
* Write the collection out as json.
@@ -0,0 +1,42 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.Collection;
import java.util.Map;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.BeanCollection;
/**
* Utility methods for BeanCollections.
*/
public class BeanCollectionUtil {
/**
* Return the details of the collection or map taking care to avoid
* unnecessary fetching of the data.
*/
public static Collection<?> getActualEntries(Object o) {
if (o == null) {
return null;
}
if (o instanceof BeanCollection<?>) {
BeanCollection<?> bc = (BeanCollection<?>) o;
if (!bc.isPopulated()) {
return null;
}
// For maps this is a collection of Map.Entry, otherwise it
// returns a collection of beans
return bc.getActualEntries();
}
if (o instanceof Map<?, ?>) {
// yes, we want the entrySet (to set the keys)
return ((Map<?, ?>) o).entrySet();
} else if (o instanceof Collection<?>) {
return ((Collection<?>) o);
}
throw new PersistenceException("expecting a Map or Collection but got [" + o.getClass().getName() + "]");
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,608 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.cache.ServerCache;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean;
import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean;
import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate;
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Helper for BeanDescriptor that manages the bean, query and collection caches.
*
* @param <T> The entity bean type
*/
public final class BeanDescriptorCacheHelp<T> {
public static final Logger queryLog = LoggerFactory.getLogger("org.avaje.ebean.cache.QUERY");
public static final Logger beanLog = LoggerFactory.getLogger("org.avaje.ebean.cache.BEAN");
public static final Logger manyLog = LoggerFactory.getLogger("org.avaje.ebean.cache.COLL");
public static final Logger natLog = LoggerFactory.getLogger("org.avaje.ebean.cache.NATKEY");
private final BeanDescriptor<T> desc;
private final ServerCacheManager cacheManager;
private final CacheOptions cacheOptions;
/**
* Flag indicating this bean has no relationships.
*/
private final boolean cacheSharableBeans;
private final Class<T> beanType;
private final String cacheName;
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
private ServerCache beanCache;
private ServerCache naturalKeyCache;
private ServerCache queryCache;
public BeanDescriptorCacheHelp(BeanDescriptor<T> desc, ServerCacheManager cacheManager, CacheOptions cacheOptions,
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
this.desc = desc;
this.beanType = desc.getBeanType();
this.cacheName = beanType.getSimpleName();
this.cacheManager = cacheManager;
this.cacheOptions = cacheOptions;
this.cacheSharableBeans = cacheSharableBeans;
this.propertiesOneImported = propertiesOneImported;
}
/**
* Initialise the cache once the server has started.
*/
public void initialise() {
if (cacheOptions.isUseNaturalKeyCache()) {
this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType);
}
if (cacheOptions.isUseCache()) {
this.beanCache = cacheManager.getBeanCache(beanType);
}
}
/**
* Execute the warming cache query (if defined) and load the cache.
*/
public void runCacheWarming(EbeanServer ebeanServer) {
if (cacheOptions == null) {
return;
}
String warmingQuery = cacheOptions.getWarmingQuery();
if (warmingQuery != null && warmingQuery.trim().length() > 0) {
Query<?> query = ebeanServer.createQuery(beanType, warmingQuery);
query.setUseCache(true);
query.setReadOnly(true);
query.setLoadBeanCache(true);
List<?> list = query.findList();
if (beanLog.isInfoEnabled()) {
beanLog.info("Loaded {} cache with [{}] beans", cacheName, list.size());
}
}
}
/**
* Return true if there is currently query caching for this type of bean.
*/
public boolean isQueryCaching() {
return queryCache != null;
}
/**
* Return true if there is currently bean caching for this type of bean.
*/
public boolean isBeanCaching() {
return beanCache != null;
}
/**
* Return true if the persist request needs to notify the cache.
*/
public boolean isCacheNotify() {
if (isBeanCaching() || isQueryCaching()) {
return true;
}
for (int i = 0; i < propertiesOneImported.length; i++) {
if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) {
return true;
}
}
return false;
}
public CacheOptions getCacheOptions() {
return cacheOptions;
}
/**
* Clear the query cache.
*/
public void queryCacheClear() {
if (queryCache != null) {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" CLEAR {}", cacheName);
}
queryCache.clear();
}
}
/**
* Get a query result from the query cache.
*/
@SuppressWarnings("unchecked")
public BeanCollection<T> queryCacheGet(Object id) {
if (queryCache == null) {
return null;
} else {
return (BeanCollection<T>) queryCache.get(id);
}
}
/**
* Put a query result into the query cache.
*/
public void queryCachePut(Object id, BeanCollection<T> query) {
if (queryCache == null) {
queryCache = cacheManager.getQueryCache(beanType);
}
if (queryLog.isDebugEnabled()) {
queryLog.debug(" PUT {} {}", cacheName, id);
}
queryCache.put(id, query);
}
public void manyPropRemove(Object parentId, String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
if (manyLog.isDebugEnabled()) {
manyLog.debug(" REMOVE {}({}).{}", cacheName, parentId, propertyName);
}
collectionIdsCache.remove(parentId);
}
public void manyPropClear(String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
if (manyLog.isDebugEnabled()) {
manyLog.debug(" CLEAR {}(*).{} ", cacheName, propertyName);
}
collectionIdsCache.clear();
}
/**
* Return the CachedManyIds for a given bean many property. Returns null if not in the cache.
*/
public CachedManyIds manyPropGet(Object parentId, String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
CachedManyIds entry = (CachedManyIds) collectionIdsCache.get(parentId);
if (entry == null) {
if (manyLog.isTraceEnabled()) {
manyLog.trace(" GET {}({}).{} - cache miss", cacheName, parentId, propertyName);
}
} else if (manyLog.isDebugEnabled()) {
manyLog.debug(" GET {}({}).{} - hit", cacheName, parentId, propertyName);
}
return entry;
}
/**
* Try to load the bean collection from cache return true if successful.
*/
public boolean manyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId, Boolean readOnly) {
CachedManyIds entry = manyPropGet(parentId, many.getName());
if (entry == null) {
// not in cache so return unsuccessful
return false;
}
Object ownerBean = bc.getOwnerBean();
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
PersistenceContext persistenceContext = ebi.getPersistenceContext();
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
List<Object> idList = entry.getIdList();
bc.checkEmptyLazyLoad();
for (int i = 0; i < idList.size(); i++) {
Object id = idList.get(i);
Object refBean = targetDescriptor.createReference(readOnly, id);
EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept();
many.add(bc, (EntityBean) refBean);
persistenceContext.put(id, refBean);
refEbi.setPersistenceContext(persistenceContext);
}
return true;
}
/**
* Put the beanCollection into the cache.
*/
public void manyPropPut(BeanPropertyAssocMany<?> many, Object details, Object parentId) {
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
ArrayList<Object> idList = new ArrayList<Object>();
// get the underlying collection of beans (in the List, Set or Map)
Collection<?> actualDetails = BeanCollectionUtil.getActualEntries(details);
for (Object bean : actualDetails) {
// Collect the id values
idList.add(targetDescriptor.getId((EntityBean) bean));
}
CachedManyIds entry = new CachedManyIds(idList);
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, many.getName());
if (manyLog.isDebugEnabled()) {
manyLog.debug(" PUT {}({}).{} - ids:{}", cacheName, parentId, many.getName(), entry);
}
collectionIdsCache.put(parentId, entry);
}
public T naturalKeyLookup(SpiQuery<T> query, SpiTransaction t) {
if (!isNaturalKeyCaching(query.isUseBeanCache())) {
// no natural key caching for this query
return null;
}
// check if it is a find by unique id (using the natural key)
NaturalKeyBindParam keyBindParam = query.getNaturalKeyBindParam();
if (keyBindParam == null || !isNaturalKey(keyBindParam.getName())) {
// query is not appropriate
return null;
}
// try to lookup the id using the natural key
Object id = naturalKeyCache.get(keyBindParam.getValue());
if (natLog.isTraceEnabled()) {
natLog.trace(" LOOKUP {}({}) - id:{}", cacheName, keyBindParam.getValue(), id);
}
if (id == null) {
return null;
}
// try looking up into the bean cache using the id
T cacheBean = beanCacheGetInternal(id, query.isReadOnly());
if (cacheBean != null) {
setupContext(cacheBean, query, getPersistenceContext(t));
}
return cacheBean;
}
private PersistenceContext getPersistenceContext(SpiTransaction t) {
PersistenceContext context = null;
if (t == null) {
t = desc.getEbeanServer().getCurrentServerTransaction();
}
if (t != null) {
context = t.getPersistenceContext();
}
return context;
}
private boolean isNaturalKeyCaching(Boolean queryUseCache) {
return naturalKeyCache != null && (queryUseCache == null || queryUseCache.booleanValue());
}
private boolean isNaturalKey(String propName) {
return propName != null && propName.equals(cacheOptions.getNaturalKey());
}
/**
* For a bean built from the cache this sets up its persistence context for future lazy loading etc.
*/
private void setupContext(Object bean, SpiQuery<T> query, PersistenceContext context) {
if (context == null) {
context = new DefaultPersistenceContext();
}
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
EntityBean entityBean = (EntityBean)bean;
EntityBeanIntercept ebi = entityBean._ebean_getIntercept();
ebi.setPersistenceContext(context);
Object id = desc.getId(entityBean);
context.put(id, bean);
}
/**
* Return the beanCache creating it if necessary.
*/
private ServerCache getBeanCache() {
if (beanCache == null) {
beanCache = cacheManager.getBeanCache(beanType);
}
return beanCache;
}
/**
* Clear the bean cache.
*/
public void beanCacheClear() {
if (beanCache != null) {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" CLEAR {}", cacheName);
}
beanCache.clear();
}
}
public CachedBeanData beanExtractData(EntityBean bean) {
return CachedBeanDataFromBean.extract(desc, bean);
}
public void beanLoadData(EntityBean bean, CachedBeanData data) {
CachedBeanDataToBean.load(desc, bean, data);
}
/**
* Put a bean into the bean cache.
*/
public void beanCachePut(EntityBean bean) {
CachedBeanData beanData = beanExtractData(bean);
Object id = desc.getId(bean);
if (beanLog.isDebugEnabled()) {
beanLog.debug(" PUT {}({})", cacheName, id);
}
getBeanCache().put(id, beanData);
if (beanData.isNaturalKeyUpdate() && naturalKeyCache != null) {
Object naturalKey = beanData.getNaturalKey();
if (naturalKey != null) {
if (natLog.isDebugEnabled()) {
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id);
}
naturalKeyCache.put(naturalKey, id);
}
}
}
public CachedBeanData beanCacheGetData(Object id) {
return (CachedBeanData) getBeanCache().get(id);
}
public T beanCacheGet(SpiQuery<T> query, PersistenceContext context) {
T bean = beanCacheGetInternal(query.getId(), query.isReadOnly());
if (bean != null) {
setupContext(bean, query, context);
}
return bean;
}
/**
* Return a bean from the bean cache.
*/
@SuppressWarnings("unchecked")
private T beanCacheGetInternal(Object id, Boolean readOnly) {
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
if (data == null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - cache miss", cacheName, id);
}
return null;
}
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
Object bean = data.getSharableBean();
if (bean != null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit shared bean", cacheName, id);
}
return (T) bean;
}
}
EntityBean bean = desc.createEntityBean();
desc.convertSetId(id, bean);
if (Boolean.TRUE.equals(readOnly)) {
bean._ebean_getIntercept().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.
*/
public void beanCacheRemove(Object id) {
if (beanCache != null) {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({})", cacheName, id);
}
beanCache.remove(id);
}
for (int i = 0; i < propertiesOneImported.length; i++) {
propertiesOneImported[i].cacheClear();
}
}
/**
* Returns true if it managed to populate/load the bean from the cache.
*/
public boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) {
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id);
if (cacheData == null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, id);
}
return false;
}
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
if (lazyLoadProperty > -1 && !cacheData.isLoaded(lazyLoadProperty)) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD {}({}) - cache miss on property", cacheName, id);
}
return false;
}
CachedBeanDataToBean.load(desc, bean, cacheData);
if (beanLog.isDebugEnabled()) {
beanLog.debug(" LOAD {}({}) - hit", cacheName, id);
}
return true;
}
/**
* Remove a bean from the cache given its Id.
*/
public void handleDelete(Object id, PersistRequestBean<T> deleteRequest) {
if (queryCache != null) {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" CLEAR {}(*) - delete trigger", cacheName);
}
queryCache.clear();
}
if (beanCache != null) {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({})", cacheName, id);
}
beanCache.remove(id);
}
for (int i = 0; i < propertiesOneImported.length; i++) {
BeanPropertyAssocMany<?> many = propertiesOneImported[i].getRelationshipProperty();
if (many != null) {
propertiesOneImported[i].cacheDelete(true, deleteRequest.getEntityBean());
}
}
}
public void handleInsert(Object id, PersistRequestBean<T> insertRequest) {
if (queryCache != null) {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" CLEAR {}(*) - insert trigger", cacheName);
}
queryCache.clear();
}
for (int i = 0; i < propertiesOneImported.length; i++) {
propertiesOneImported[i].cacheDelete(false, insertRequest.getEntityBean());
}
}
/**
* Update the cached bean data.
*/
public void handleUpdate(Object id, PersistRequestBean<T> updateRequest) {
if (queryCache != null) {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" CLEAR {}(*) - update trigger", cacheName);
}
queryCache.clear();
}
List<BeanPropertyAssocMany<?>> manyCollections = updateRequest.getUpdatedManyCollections();
if (manyCollections != null) {
// clear the appropriate manyProp caches first
for (int i = 0; i < manyCollections.size(); i++) {
manyPropRemove(id, manyCollections.get(i).getName());
}
}
// check if the bean itself was updated
if (!updateRequest.isUpdatedManysOnly()) {
// update the bean cache entry if it exists
ServerCache cache = getBeanCache();
CachedBeanData existingData = (CachedBeanData) cache.get(id);
if (existingData != null) {
if (isCachedDataTooOld(existingData)) {
// just remove the entry from the cache
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({}) - entry too old", cacheName, id);
}
cache.remove(id);
} else {
// Update the cache data with the changes from our update
CachedBeanData newData = CachedBeanDataUpdate.update(desc, existingData, updateRequest.getEntityBean());
if (beanLog.isDebugEnabled()) {
beanLog.debug(" UPDATE {}({})", cacheName, id);
}
cache.put(id, newData);
if (newData.isNaturalKeyUpdate() && naturalKeyCache != null) {
Object oldKey = newData.getOldNaturalKey();
Object newKey = newData.getNaturalKey();
if (natLog.isDebugEnabled()) {
natLog.debug(".. update {} PUT({}, {}) REMOVE({})", cacheName, newKey, id, oldKey);
}
if (oldKey != null) {
naturalKeyCache.remove(oldKey);
}
if (newKey != null) {
naturalKeyCache.put(newKey, id);
}
}
}
}
}
if (manyCollections != null) {
for (int i = 0; i < manyCollections.size(); i++) {
BeanPropertyAssocMany<?> many = manyCollections.get(i);
Object manyValue = many.getValue(updateRequest.getEntityBean());
manyPropPut(many, manyValue, id);
}
}
}
private boolean isCachedDataTooOld(CachedBeanData existingData) {
return cacheOptions.isTooOldInMillis(System.currentTimeMillis() - existingData.getWhenCreated());
}
/**
* Invalidate parts of cache due to SqlUpdate or external modification etc.
*/
public void handleBulkUpdate(TableIUD tableIUD) {
// inserts don't invalidate the bean cache
if (tableIUD.isUpdateOrDelete()) {
beanCacheClear();
}
// any change invalidates the query cache
queryCacheClear();
}
}
@@ -61,6 +61,7 @@ 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.type.TypeManager;
@@ -148,6 +149,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final XmlConfig xmlConfig;
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
/**
* Create for a given database dbConfig.
*/
@@ -176,6 +179,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly();
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
@@ -221,8 +225,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
public IdBinder createIdBinder(BeanProperty[] uids) {
return idBinderFactory.createIdBinder(uids);
public IdBinder createIdBinder(BeanProperty idProperty) {
return idBinderFactory.createIdBinder(idProperty);
}
public void deploy() {
@@ -280,7 +284,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
List<BeanDescriptor<?>> list = getBeanDescriptors(tableIUD.getTableName());
if (list != null) {
for (int i = 0; i < list.size(); i++) {
list.get(i).cacheNotify(tableIUD);
list.get(i).cacheHandleBulkUpdate(tableIUD);
}
}
}
@@ -1009,6 +1013,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
desc.setUpdateChangesOnly(updateChangesOnly);
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
@@ -1298,43 +1304,34 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// abstract classes as well.
Class<?> beanType = desc.getBeanType();
Class<?> factType = desc.getFactoryType();
BeanReflect beanReflect = reflectFactory.create(beanType, factType);
BeanReflectProperties reflectProps = new BeanReflectProperties(beanType);
BeanReflect beanReflect = reflectFactory.create(beanType);
desc.setBeanReflect(beanReflect);
desc.setProperties(reflectProps.getProperties());
try {
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
String propName = prop.getName();
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
String propName = prop.getName();
Integer pos = reflectProps.getPropertyIndex(propName);
if (pos == null) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
if (desc.isAbstract() || beanReflect.isVanillaOnly()) {
// use reflection in the case of imported abstract class
// with
// inheritance. Refer Bug 166
prop.setGetter(ReflectGetter.create(prop));
prop.setSetter(ReflectSetter.create(prop));
} else {
// use generated code for getting setting property values
BeanReflectGetter getter = beanReflect.getGetter(propName);
BeanReflectSetter setter = beanReflect.getSetter(propName);
prop.setGetter(getter);
prop.setSetter(setter);
if (getter == null) {
// should never happen
String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?";
throw new RuntimeException(m);
}
}
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);
}
} catch (IllegalArgumentException e) {
Class<?> superClass = desc.getBeanType().getSuperclass();
String msg = "Error with [" + desc.getFullName() + "] I believe it is not enhanced but it's superClass [" + superClass + "] is?"
+ " (You are not allowed to mix enhancement in a single inheritance hierarchy)";
throw new PersistenceException(msg, e);
}
}
@@ -1345,13 +1342,15 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void setConcurrencyMode(DeployBeanDescriptor<?> desc) {
if (!desc.getConcurrencyMode().equals(ConcurrencyMode.ALL)) {
if (desc.getConcurrencyMode() != null) {
// concurrency mode explicitly set during deployment
return;
}
if (checkForVersionProperties(desc)) {
desc.setConcurrencyMode(ConcurrencyMode.VERSION);
} else {
desc.setConcurrencyMode(ConcurrencyMode.NONE);
}
}
@@ -1390,91 +1389,35 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
Class<?> beanClass = desc.getBeanType();
if (desc.isAbstract()) {
if (hasEntityBeanInterface(beanClass)) {
checkEnhanced(desc, beanClass);
} else {
checkSubclass(desc, beanClass);
}
return;
if (!hasEntityBeanInterface(beanClass)) {
throw new IllegalStateException("Bean "+beanClass+" is not enhanced?");
}
try {
Object testBean = null;
try {
testBean = beanClass.newInstance();
} catch (InstantiationException e) {
// expected when no default constructor
logger.debug("no default constructor on " + beanClass + " e:" + e);
} catch (IllegalAccessException e) {
// expected when no default constructor
logger.debug("no default constructor on " + beanClass + " e:" + e);
}
if (testBean instanceof EntityBean == false) {
checkSubclass(desc, beanClass);
} else {
String className = beanClass.getName();
try {
// check that it really is enhanced (rather than mixed
// enhancement)
String marker = ((EntityBean) testBean)._ebean_getMarker();
if (!marker.equals(className)) {
String msg = "Error with [" + desc.getFullName() + "] It has not been enhanced but it's superClass ["
+ beanClass.getSuperclass() + "] is?" + " (You are not allowed to mix enhancement in a single inheritance hierarchy)"
+ " marker[" + marker + "] className[" + className + "]";
throw new PersistenceException(msg);
}
} catch (AbstractMethodError e) {
throw new PersistenceException("Old Ebean v1.0 enhancement detected in Ebean v1.1 - please do a clean enhancement.", e);
}
checkEnhanced(desc, beanClass);
}
} catch (PersistenceException ex) {
throw ex;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
private void checkEnhanced(DeployBeanDescriptor<?> desc, Class<?> beanClass) {
// the bean already implements EntityBean
checkInheritedClasses(true, beanClass);
desc.setFactoryType(beanClass);
enhancedClassCount++;
}
checkInheritedClasses(beanClass);
private void checkSubclass(DeployBeanDescriptor<?> desc, Class<?> beanClass) {
throw new PersistenceException("Entity type "+beanClass+" is not an enhanced entity bean. Subclassing is not longer supported in Ebean");
if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) {
enhancedClassCount++;
}
}
/**
* Check that the inherited classes are the same as the entity bean (aka all
* enhanced or all dynamically subclassed).
*/
private void checkInheritedClasses(boolean ensureEnhanced, Class<?> beanClass) {
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
// we got to the top of the inheritance
return;
}
boolean isClassEnhanced = EntityBean.class.isAssignableFrom(superclass);
if (ensureEnhanced != isClassEnhanced) {
String msg;
if (ensureEnhanced) {
msg = "Class [" + superclass + "] is not enhanced and [" + beanClass + "] is - (you can not mix!!)";
} else {
msg = "Class [" + superclass + "] is enhanced and [" + beanClass + "] is not - (you can not mix!!)";
}
throw new IllegalStateException(msg);
if (!EntityBean.class.isAssignableFrom(superclass)) {
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
}
// recursively continue up the inheritance hierarchy
checkInheritedClasses(ensureEnhanced, superclass);
checkInheritedClasses(superclass);
}
/**
@@ -32,6 +32,6 @@ public interface BeanDescriptorMap {
*/
public EncryptKey getEncryptKey(String tableName, String columnName);
public IdBinder createIdBinder(BeanProperty[] uids);
public IdBinder createIdBinder(BeanProperty id);
}
@@ -16,16 +16,4 @@ public class BeanEmbeddedMeta {
return properties;
}
/**
* Return true if at least one property is a version property.
*/
public boolean isEmbeddedVersion() {
for (int i = 0; i < properties.length; i++) {
if (properties[i].isVersion()){
return true;
}
}
return false;
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.StringFormatter;
import com.avaje.ebean.text.StringParser;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
@@ -95,7 +96,7 @@ public final class BeanFkeyProperty implements ElPropertyValue {
/**
* Returns null as not an AssocOne.
*/
public Object[] getAssocOneIdValues(Object value) {
public Object[] getAssocOneIdValues(EntityBean value) {
return null;
}
@@ -159,7 +160,7 @@ public final class BeanFkeyProperty implements ElPropertyValue {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public void elSetReference(Object bean) {
public void elSetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@@ -167,15 +168,15 @@ public final class BeanFkeyProperty implements ElPropertyValue {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public void elSetValue(Object bean, Object value, boolean populate, boolean reference) {
public void elSetValue(EntityBean bean, Object value, boolean populate) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetValue(Object bean) {
public Object elGetValue(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetReference(Object bean) {
public Object elGetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@@ -0,0 +1,186 @@
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import java.util.Set;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.event.BeanPersistAdapter;
import com.avaje.ebean.event.BeanPersistRequest;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Helper that looks for methods annotated with lifecycle events and registers an adapter for them.
* <p>
* This includes PrePerist, PostPerist, PreUpdate, PostUpdate, PreRemove, PostRemove and PostLoad
* lifecycle events.
* </p>
*/
public class BeanLifecycleAdapterFactory {
private static final Logger logger = LoggerFactory.getLogger(BeanLifecycleAdapterFactory.class);
/**
* Register a BeanPersistController for methods annotated with lifecycle events.
*/
public void addLifecycleMethods(DeployBeanDescriptor<?> deployDesc) {
Method[] methods = deployDesc.getBeanType().getMethods();
MethodHolder methodHolder = new MethodHolder();
for (Method m : methods) {
methodHolder.checkMethod(m);
}
if (methodHolder.hasListener()) {
deployDesc.addPersistController(new Adapter(methodHolder));
}
}
/**
* Holds Methods for the lifecycle events.s
*/
private static class MethodHolder {
private boolean hasListener;
private Method preInsert;
private Method postInsert;
private Method preUpdate;
private Method postUpdate;
private Method preDelete;
private Method postDelete;
private Method postLoad;
private boolean hasListener() {
return hasListener;
}
private void checkMethod(Method method) {
if (method.isAnnotationPresent(PrePersist.class)) {
preInsert = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostPersist.class)) {
postInsert = method;
hasListener = true;
}
if (method.isAnnotationPresent(PreUpdate.class)) {
preUpdate = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostUpdate.class)) {
postUpdate = method;
hasListener = true;
}
if (method.isAnnotationPresent(PreRemove.class)) {
preDelete = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostRemove.class)) {
postDelete = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostLoad.class)) {
postLoad = method;
hasListener = true;
}
}
}
/**
* BeanPersistAdapter using reflection to invoke lifecycle methods.
*/
private static class Adapter extends BeanPersistAdapter {
private final MethodHolder methodHolder;
private Adapter(MethodHolder methodHolder) {
this.methodHolder = methodHolder;
}
@Override
public boolean isRegisterFor(Class<?> cls) {
// Not used
return false;
}
private void invoke(Method method, Object bean) {
try {
method.invoke(bean);
} catch (Exception e) {
logger.error("Error invoking lifecycle adapter", e);
}
}
private void invoke(Method method, BeanPersistRequest<?> request) {
invoke(method, request.getBean());
}
@Override
public boolean preDelete(BeanPersistRequest<?> request) {
if (methodHolder.preDelete != null) {
invoke(methodHolder.preDelete, request);
}
return true;
}
@Override
public boolean preInsert(BeanPersistRequest<?> request) {
if (methodHolder.preInsert != null) {
invoke(methodHolder.preInsert, request);
}
return true;
}
@Override
public boolean preUpdate(BeanPersistRequest<?> request) {
if (methodHolder.preUpdate != null) {
invoke(methodHolder.preUpdate, request);
}
return true;
}
@Override
public void postDelete(BeanPersistRequest<?> request) {
if (methodHolder.postDelete != null) {
invoke(methodHolder.postDelete, request);
}
}
@Override
public void postInsert(BeanPersistRequest<?> request) {
if (methodHolder.postInsert != null) {
invoke(methodHolder.postInsert, request);
}
}
@Override
public void postUpdate(BeanPersistRequest<?> request) {
if (methodHolder.postUpdate != null) {
invoke(methodHolder.postUpdate, request);
}
}
@Override
public void postLoad(Object bean, Set<String> includedProperties) {
if (methodHolder.postLoad != null) {
invoke(methodHolder.postLoad, bean);
}
}
}
}
@@ -10,6 +10,7 @@ import com.avaje.ebean.Transaction;
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.ebean.common.BeanList;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
@@ -35,11 +36,11 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
public void setLoader(BeanCollectionLoader loader) {
this.loader = loader;
}
/**
* Internal add bypassing any modify listening.
*/
public void add(BeanCollection<?> collection, Object bean) {
public void add(BeanCollection<?> collection, EntityBean bean) {
collection.internalAdd(bean);
}
@@ -70,7 +71,7 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
this.list = list;
}
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
list.add(bean);
}
}
@@ -90,20 +91,20 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
return beanList;
}
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName) {
BeanList<T> beanList = new BeanList<T>(loader, parentBean, propertyName);
beanList.setModifyListening(many.getModifyListenMode());
return beanList;
}
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean) {
BeanList<?> newBeanList = (BeanList<?>) server.findList(query, t);
refresh(newBeanList, parentBean);
}
public void refresh(BeanCollection<?> bc, Object parentBean) {
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
BeanList<?> newBeanList = (BeanList<?>) bc;
@@ -152,7 +153,7 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
ctx.appendComma();
}
Object detailBean = list.get(j);
targetDescriptor.jsonWrite(ctx, detailBean);
targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean);
}
ctx.endAssocMany();
}
@@ -11,6 +11,7 @@ import com.avaje.ebean.Transaction;
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.ebean.common.BeanMap;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
@@ -93,7 +94,7 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
this.map = map;
}
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
Object keyValue = beanProperty.getValue(bean);
map.put(keyValue, bean);
}
@@ -111,18 +112,15 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
return beanMap;
}
/**
* Internal add bypassing any modify listening.
*/
public void add(BeanCollection<?> collection, Object bean) {
public void add(BeanCollection<?> collection, EntityBean bean) {
Object keyValue = beanProperty.getValueIntercept(bean);
((BeanMap<?,?>) collection).internalPut(keyValue, bean);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName) {
BeanMap beanMap = new BeanMap(loader, parentBean, propertyName);
if (many != null) {
@@ -131,12 +129,12 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
return beanMap;
}
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
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, Object parentBean) {
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
BeanMap<?, ?> newBeanMap = (BeanMap<?, ?>) bc;
Map<?, ?> current = (Map<?, ?>) many.getValue(parentBean);
@@ -187,7 +185,7 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
}
//FIXME: json write map key ...
Object detailBean = entry.getValue();
targetDescriptor.jsonWrite(ctx, detailBean);
targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean);
}
ctx.endAssocMany();
}
@@ -27,13 +27,13 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
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.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.util.ValueUtil;
/**
* Description of a property of a bean. Includes its deployment information such
@@ -74,6 +74,11 @@ public class BeanProperty implements ElPropertyValue {
*/
final boolean unidirectionalShadow;
/**
* Flag set if this maps to the inheritance discriminator column
*/
final boolean discriminator;
/**
* Flag to mark the property as embedded. This could be on
* BeanPropertyAssocOne rather than here. Put it here for checking Id type
@@ -141,6 +146,8 @@ public class BeanProperty implements ElPropertyValue {
*/
final String name;
final int propertyIndex;
/**
* The reflected field.
*/
@@ -249,8 +256,6 @@ public class BeanProperty implements ElPropertyValue {
final DbEncryptFunction dbEncryptFunction;
final boolean dynamicSubclassWithInheritance;
int deployOrder;
final boolean jsonSerialize;
@@ -265,12 +270,10 @@ public class BeanProperty implements ElPropertyValue {
this.descriptor = descriptor;
this.name = InternString.intern(deploy.getName());
if (descriptor != null) {
this.dynamicSubclassWithInheritance = (descriptor.isDynamicSubclass() && descriptor.hasInheritance());
} else {
this.dynamicSubclassWithInheritance = false;
}
this.propertyIndex = deploy.getPropertyIndex();
this.unidirectionalShadow = deploy.isUndirectionalShadow();
this.discriminator = deploy.isDiscriminator();
this.localEncrypted = deploy.isLocalEncrypted();
this.dbEncrypted = deploy.isDbEncrypted();
this.dbEncryptedType = deploy.getDbEncryptedType();
@@ -309,12 +312,6 @@ public class BeanProperty implements ElPropertyValue {
this.readMethod = deploy.getReadMethod();
this.writeMethod = deploy.getWriteMethod();
this.getter = deploy.getGetter();
if (descriptor != null && getter == null) {
if (!unidirectionalShadow) {
String m = "Null Getter for: " + getFullBeanName();
throw new RuntimeException(m);
}
}
this.setter = deploy.getSetter();
this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null);
@@ -363,7 +360,7 @@ public class BeanProperty implements ElPropertyValue {
this.descriptor = source.descriptor;
this.name = InternString.intern(source.getName());
this.dynamicSubclassWithInheritance = source.dynamicSubclassWithInheritance;
this.propertyIndex = source.propertyIndex;
this.dbColumn = InternString.intern(override.getDbColumn());
this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin());
@@ -372,6 +369,7 @@ public class BeanProperty implements ElPropertyValue {
this.fetchEager = source.fetchEager;
this.unidirectionalShadow = source.unidirectionalShadow;
this.discriminator = source.discriminator;
this.localEncrypted = source.isLocalEncrypted();
this.isTransient = source.isTransient();
this.secondaryTable = source.isSecondaryTable();
@@ -473,14 +471,14 @@ public class BeanProperty implements ElPropertyValue {
return formula;
}
public boolean hasChanged(Object bean, Object oldValues) {
Object value = getValue(bean);
Object oldVal = getValue(oldValues);
return !ValueUtil.areEqual(value, oldVal);
/**
* Return true if this property maps to the inheritance discriminator column.
*/
public boolean isDiscriminator() {
return discriminator;
}
public void copyProperty(Object sourceBean, Object destBean) {
public void copyProperty(EntityBean sourceBean, EntityBean destBean) {
Object value = getValue(sourceBean);
setValue(destBean, value);
}
@@ -512,14 +510,14 @@ public class BeanProperty implements ElPropertyValue {
* Add any extra joins required to support this property. Generally a no
* operation except for a OneToOne exported.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (formula && sqlFormulaJoin != null) {
ctx.appendFormulaJoin(sqlFormulaJoin, forceOuterJoin);
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
} else if (secondaryTableJoin != null) {
String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix);
secondaryTableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
secondaryTableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -561,7 +559,7 @@ public class BeanProperty implements ElPropertyValue {
return owningType.isAssignableFrom(type);
}
public Object readSetOwning(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
try {
Object value = scalarType.read(ctx.getDataReader());
@@ -599,7 +597,7 @@ public class BeanProperty implements ElPropertyValue {
return scalarType.read(ctx.getDataReader());
}
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
public Object readSet(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
try {
Object value = scalarType.read(ctx.getDataReader());
@@ -690,15 +688,9 @@ public class BeanProperty implements ElPropertyValue {
* Set the value of the property without interception or
* PropertyChangeSupport.
*/
public void setValue(Object bean, Object value) {
public void setValue(EntityBean bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.set(bean, value);
} else {
Object[] args = new Object[1];
args[0] = value;
writeMethod.invoke(bean, args);
}
setter.set(bean, value);
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType
@@ -710,15 +702,9 @@ public class BeanProperty implements ElPropertyValue {
/**
* Set the value of the property.
*/
public void setValueIntercept(Object bean, Object value) {
public void setValueIntercept(EntityBean bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.setIntercept(bean, value);
} else {
Object[] args = new Object[1];
args[0] = value;
writeMethod.invoke(bean, args);
}
setter.setIntercept(bean, value);
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType
@@ -729,34 +715,20 @@ public class BeanProperty implements ElPropertyValue {
private static Object[] NO_ARGS = new Object[0];
/**
* Return the property value taking inheritance into account.
*/
public Object getValueWithInheritance(Object bean) {
if (dynamicSubclassWithInheritance) {
return descriptor.getBeanPropertyWithInheritance(bean, name);
}
return getValue(bean);
}
public Object getCacheDataValue(Object bean){
public Object getCacheDataValue(EntityBean bean){
return getValue(bean);
}
public void setCacheDataValue(Object bean, Object cacheData, Object oldValues, boolean readOnly){
public void setCacheDataValue(EntityBean bean, Object cacheData){
setValue(bean, cacheData);
}
/**
* Return the value of the property method.
*/
public Object getValue(Object bean) {
public Object getValue(EntityBean bean) {
try {
if (bean instanceof EntityBean) {
return getter.get(bean);
} else {
return readMethod.invoke(bean, NO_ARGS);
}
return getter.get(bean);
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error.";
@@ -777,13 +749,9 @@ public class BeanProperty implements ElPropertyValue {
}
}
public Object getValueIntercept(Object bean) {
public Object getValueIntercept(EntityBean bean) {
try {
if (bean instanceof EntityBean) {
return getter.getIntercept(bean);
} else {
return readMethod.invoke(bean, NO_ARGS);
}
return getter.getIntercept(bean);
} catch (Exception ex) {
String beanType = bean == null ? "null" : bean.getClass().getName();
String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error.";
@@ -798,24 +766,21 @@ public class BeanProperty implements ElPropertyValue {
return convertToLogicalType(value);
}
public void elSetReference(Object bean) {
throw new RuntimeException("Should not be called");
}
public void elSetValue(Object bean, Object value, boolean populate, boolean reference) {
public void elSetValue(EntityBean bean, Object value, boolean populate) {
if (bean != null) {
setValueIntercept(bean, value);
// Not using setValueIntercept at this stage
setValue(bean, value);
}
}
public Object elGetValue(Object bean) {
public Object elGetValue(EntityBean bean) {
if (bean == null) {
return null;
}
return getValueIntercept(bean);
}
public Object elGetReference(Object bean) {
public Object elGetReference(EntityBean bean) {
throw new RuntimeException("Not expected to call this");
}
@@ -826,6 +791,13 @@ public class BeanProperty implements ElPropertyValue {
return name;
}
/**
* Return the position of this property in the enhanced bean.
*/
public int getPropertyIndex() {
return propertyIndex;
}
public String getElName() {
return name;
}
@@ -851,7 +823,7 @@ public class BeanProperty implements ElPropertyValue {
return false;
}
public Object[] getAssocOneIdValues(Object bean) {
public Object[] getAssocOneIdValues(EntityBean bean) {
// Returns null as not an AssocOne.
return null;
}
@@ -1014,6 +986,13 @@ public class BeanProperty implements ElPropertyValue {
return isTransient;
}
/**
* Return true if this property is loadable from a resultSet.
*/
public boolean isLoadProperty() {
return !isTransient || formula;
}
/**
* Return true if this is a version column used for concurrency checking.
*/
@@ -1177,7 +1156,7 @@ public class BeanProperty implements ElPropertyValue {
}
@SuppressWarnings("unchecked")
public void jsonWrite(WriteJsonContext ctx, Object bean) {
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
if(!jsonSerialize){
return;
}
@@ -1189,7 +1168,7 @@ public class BeanProperty implements ElPropertyValue {
}
}
public void jsonRead(ReadJsonContext ctx, Object bean) {
public void jsonRead(ReadJsonContext ctx, EntityBean bean) {
if(!jsonDeserialize){
return;
}
@@ -4,18 +4,19 @@ import java.util.ArrayList;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdEmbedded;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdMultiple;
import com.avaje.ebeaninternal.server.deploy.id.ImportedIdSimple;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
@@ -129,22 +130,15 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
/**
* Add table join with table alias based on prefix.
*/
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, prefix, ctx);
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(joinType, prefix, ctx);
}
/**
* Add table join with explicit table alias.
*/
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, a1, a2, ctx);
}
/**
* Add table join with explicit table alias.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
tableJoin.addInnerJoin(a1, a2, ctx);
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(joinType, a1, a2, ctx);
}
/**
@@ -216,14 +210,12 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
/**
* Return true if the unique id properties are all not null for this bean.
*/
public boolean hasId(Object bean) {
public boolean hasId(EntityBean bean) {
BeanDescriptor<?> targetDesc = getTargetDescriptor();
BeanProperty[] uids = targetDesc.propertiesId();
for (int i = 0; i < uids.length; i++) {
Object value = uids[i].getValue(bean);
BeanProperty idProp = targetDesc.getIdProperty();
if (idProp != null) {
Object value = idProp.getValue(bean);
if (value == null) {
return false;
}
@@ -311,39 +303,36 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
*/
protected ImportedId createImportedId(BeanPropertyAssoc<?> owner, BeanDescriptor<?> target, TableJoin join) {
BeanProperty[] props = target.propertiesId();
BeanProperty idProp = target.getIdProperty();
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isSqlSelectBased()){
String dbColumn = owner.getDbColumn();
return new ImportedIdSimple(owner, dbColumn, props[0], 0);
return new ImportedIdSimple(owner, dbColumn, idProp, 0);
}
TableJoinColumn[] cols = join.columns();
if (props.length == 1) {
if (!props[0].isEmbedded()) {
// simple single scalar id
if (cols.length != 1){
String msg = "No Imported Id column for ["+props[0]+"] in table ["+join.getTable()+"]";
logger.error(msg);
return null;
} else {
return createImportedScalar(owner, cols[0], props, others);
}
if (idProp == null) {
return null;
}
if (!idProp.isEmbedded()) {
// simple single scalar id
if (cols.length != 1){
String msg = "No Imported Id column for ["+idProp+"] in table ["+join.getTable()+"]";
logger.error(msg);
return null;
} else {
// embedded id
BeanPropertyAssocOne<?> embProp = (BeanPropertyAssocOne<?>)props[0];
BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar();
ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others);
return new ImportedIdEmbedded(owner, embProp, scalars);
BeanProperty[] idProps = {idProp};
return createImportedScalar(owner, cols[0], idProps, others);
}
} else {
// Concatenated key that is not embedded
ImportedIdSimple[] scalars = createImportedList(owner, cols, props, others);
return new ImportedIdMultiple(owner, scalars);
// embedded id
BeanPropertyAssocOne<?> embProp = (BeanPropertyAssocOne<?>)idProp;
BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar();
ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others);
return new ImportedIdEmbedded(owner, embProp, scalars);
}
}
@@ -8,6 +8,9 @@ import java.util.List;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Expression;
import com.avaje.ebean.Query;
@@ -17,6 +20,7 @@ import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
@@ -34,59 +38,62 @@ import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
*/
public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
/**
* Join for manyToMany intersection table.
*/
final TableJoin intersectionJoin;
private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class);
/**
* Join for manyToMany intersection table.
*/
private final TableJoin intersectionJoin;
/**
* For ManyToMany this is the Inverse join used to build reference queries.
*/
final TableJoin inverseJoin;
/**
* For ManyToMany this is the Inverse join used to build reference queries.
*/
private final TableJoin inverseJoin;
/**
* Flag to indicate that this is a unidirectional relationship.
*/
final boolean unidirectional;
/**
* Flag to indicate that this is a unidirectional relationship.
*/
private final boolean unidirectional;
/**
* Flag to indicate manyToMany relationship.
*/
final boolean manyToMany;
/**
* Flag to indicate manyToMany relationship.
*/
private final boolean manyToMany;
final String fetchOrderBy;
private final String fetchOrderBy;
final String mapKey;
private final String mapKey;
/**
* The type of the many, set, list or map.
*/
final ManyType manyType;
/**
* The type of the many, set, list or map.
*/
private final ManyType manyType;
final String serverName;
private final ModifyListenMode modifyListenMode;
final ModifyListenMode modifyListenMode;
private BeanProperty mapKeyProperty;
/**
* Derived list of exported property and matching foreignKey
*/
private ExportedProperty[] exportedProperties;
private String exportedPropertyBindProto = "?";
BeanProperty mapKeyProperty;
/**
* Derived list of exported property and matching foreignKey
*/
ExportedProperty[] exportedProperties;
/**
* Property on the 'child' bean that links back to the 'master'.
*/
private BeanPropertyAssocOne<?> childMasterProperty;
/**
* Property on the 'child' bean that links back to the 'master'.
*/
BeanPropertyAssocOne<?> childMasterProperty;
private boolean embeddedExportedProperties;
boolean embeddedExportedProperties;
private BeanCollectionHelp<T> help;
BeanCollectionHelp<T> help;
ImportedId importedId;
String deleteByParentIdSql;
String deleteByParentIdInSql;
private ImportedId importedId;
private String deleteByParentIdSql;
private String deleteByParentIdInSql;
/**
* Create this property.
@@ -95,12 +102,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
super(owner, descriptor, deploy);
this.unidirectional = deploy.isUnidirectional();
this.manyToMany = deploy.isManyToMany();
this.serverName = descriptor.getServerName();
this.manyType = deploy.getManyType();
this.mapKey = deploy.getMapKey();
this.fetchOrderBy = deploy.getFetchOrderBy();
this.intersectionJoin = deploy.createIntersectionTableJoin();
this.inverseJoin = deploy.createInverseTableJoin();
this.modifyListenMode = deploy.getModifyListenMode();
@@ -132,6 +136,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
exportedProperties = createExported();
if (exportedProperties.length > 0){
embeddedExportedProperties = exportedProperties[0].isEmbedded();
exportedPropertyBindProto = deriveExportedPropertyBindProto();
}
String delStmt;
@@ -148,7 +153,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
/**
* Add the bean to the appropriate collection on the parent bean.
*/
public void addBeanToCollectionWithCreate(Object parentBean, Object detailBean) {
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) {
BeanCollection<?> bc = (BeanCollection<?>)super.getValue(parentBean);
if (bc == null) {
bc = (BeanCollection<?>)help.createEmpty(false);
@@ -157,23 +162,35 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
help.add(bc, detailBean);
}
public boolean isEmptyBeanCollection(EntityBean bean) {
Object val = getValue(bean);
if (val == null) {
return true;
}
if (val instanceof BeanCollection<?>) {
// if empty and not been cleared or elements removed
return ((BeanCollection<?>)val).isEmptyAndUntouched();
}
return false;
}
@Override
public Object getValue(Object bean) {
public Object getValue(EntityBean bean) {
return super.getValue(bean);
}
@Override
public Object getValueIntercept(Object bean) {
public Object getValueIntercept(EntityBean bean) {
return super.getValueIntercept(bean);
}
@Override
public void setValue(Object bean, Object value) {
public void setValue(EntityBean bean, Object value) {
super.setValue(bean, value);
}
@Override
public void setValueIntercept(Object bean, Object value) {
public void setValueIntercept(EntityBean bean, Object value) {
super.setValueIntercept(bean, value);
}
@@ -244,45 +261,76 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
query.where().raw(expr, bindValues.toArray());
}
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
String rawWhere = deriveWhereParentIdSql(true,"");
String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size());
String expr = rawWhere+inClause;
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = server.find(getPropertyType())
.where().raw(expr).query();
int pos = 1;
for (int i = 0; i < parentIdist.size(); i++) {
pos = bindWhereParendId(pos, q, parentIdist.get(i));
}
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds);
q.where().not(idIn);
}
return server.findIds(q, t);
String rawWhere = deriveWhereParentIdSql(true, "");
String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto);
String expr = rawWhere + inClause;
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = server.find(getPropertyType()).where().raw(expr).query();
int pos = 1;
for (int i = 0; i < parentIdist.size(); i++) {
pos = bindWhereParendId(pos, q, parentIdist.get(i));
}
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds);
q.where().not(idIn);
}
return server.findIds(q, t);
}
private SqlUpdate deleteByParentIdList(List<Object> parentIdist) {
private SqlUpdate deleteByParentIdList(List<Object> parentIdist) {
StringBuilder sb = new StringBuilder(100);
sb.append(deleteByParentIdInSql);
StringBuilder sb = new StringBuilder(100);
sb.append(deleteByParentIdInSql);
String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size());
sb.append(inClause);
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
for (int i = 0; i < parentIdist.size(); i++) {
bindWhereParendId(delete, parentIdist.get(i));
}
return delete;
String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto);
sb.append(inClause);
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
for (int i = 0; i < parentIdist.size(); i++) {
bindWhereParendId(delete, parentIdist.get(i));
}
return delete;
}
private String deriveExportedPropertyBindProto() {
if (exportedProperties.length == 1) {
return "?";
}
StringBuilder sb = new StringBuilder();
sb.append("(");
for (int i = 0; i < exportedProperties.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append("?");
}
sb.append(")");
return sb.toString();
}
private String buildInClauseBinding(int size, String bindProto) {
StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1)));
sb.append(" in");
sb.append(" (");
for (int i = 0; i < size; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(bindProto);
}
sb.append(") ");
return sb.toString();
}
/**
* Set the lazy load server to help create reference collections (that lazy
@@ -324,7 +372,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
@Override
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
public Object readSet(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
return null;
}
@@ -342,21 +390,21 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return true;
}
public void add(BeanCollection<?> collection, Object bean) {
public void add(BeanCollection<?> collection, EntityBean bean) {
help.add(collection, bean);
}
/**
* Refresh the appropriate list set or map.
*/
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean) {
help.refresh(server, query, t, parentBean);
}
/**
* Apply the refreshed BeanCollection to the property of the parentBean.
*/
public void refresh(BeanCollection<?> bc, Object parentBean) {
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
help.refresh(bc, parentBean);
}
@@ -364,7 +412,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
* Return the Id values from the given bean.
*/
@Override
public Object[] getAssocOneIdValues(Object bean) {
public Object[] getAssocOneIdValues(EntityBean bean) {
return targetDescriptor.getIdBinder().getIdValues(bean);
}
@@ -435,7 +483,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
* Set the join properties from the parent bean to the child bean.
* This is only valid for OneToMany and NOT valid for ManyToMany.
*/
public void setJoinValuesToChild(Object parent, Object child, Object mapKeyValue) {
public void setJoinValuesToChild(EntityBean parent, EntityBean child, Object mapKeyValue) {
if (mapKeyProperty != null){
mapKeyProperty.setValue(child, mapKeyValue);
@@ -468,7 +516,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return mapKey;
}
public BeanCollection<?> createReferenceIfNull(Object parentBean) {
public BeanCollection<?> createReferenceIfNull(EntityBean parentBean) {
Object v = getValue(parentBean);
if (v instanceof BeanCollection<?>){
@@ -479,7 +527,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
}
public BeanCollection<?> createReference(Object parentBean) {
public BeanCollection<?> createReference(EntityBean parentBean) {
BeanCollection<?> ref = help.createReference(parentBean, name);
setValue(parentBean, ref);
@@ -494,7 +542,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return help.getBeanCollectionAdd(bc, mapKey);
}
public Object getParentId(Object parentBean) {
public Object getParentId(EntityBean parentBean) {
return descriptor.getId(parentBean);
}
@@ -506,7 +554,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
for (int i=0; i < parentIds.size(); i++) {
for (int y = 0; y < exportedProperties.length; y++) {
Object compId = parentIds.get(i);
expandedList.add(exportedProperties[y].getValue(compId));
expandedList.add(exportedProperties[y].getValue((EntityBean)compId));
}
}
return expandedList;
@@ -518,8 +566,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
sqlUpd.addParameter(parentId);
return;
}
EntityBean parent = (EntityBean)parentId;
for (int i = 0; i < exportedProperties.length; i++) {
Object embVal = exportedProperties[i].getValue(parentId);
Object embVal = exportedProperties[i].getValue(parent);
sqlUpd.addParameter(embVal);
}
}
@@ -531,8 +580,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
} else {
EntityBean parent = (EntityBean)parentId;
for (int i = 0; i < exportedProperties.length; i++) {
Object embVal = exportedProperties[i].getValue(parentId);
Object embVal = exportedProperties[i].getValue(parent);
q.setParameter(pos++, embVal);
}
}
@@ -574,7 +624,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return sb.toString();
}
public void setPredicates(SpiQuery<?> query, Object parentBean) {
public void setPredicates(SpiQuery<?> query, EntityBean parentBean) {
if (manyToMany){
// for ManyToMany lazy loading we need to include a
@@ -585,8 +635,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (embeddedExportedProperties) {
// use the EmbeddedId object instead of the parentBean
BeanProperty[] uids = descriptor.propertiesId();
parentBean = uids[0].getValue(parentBean);
BeanProperty idProp = descriptor.getIdProperty();
parentBean = (EntityBean)idProp.getValue(parentBean);
}
for (int i = 0; i < exportedProperties.length; i++) {
@@ -618,13 +668,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
*/
private ExportedProperty[] createExported() {
BeanProperty[] uids = descriptor.propertiesId();
BeanProperty idProp = descriptor.getIdProperty();
ArrayList<ExportedProperty> list = new ArrayList<ExportedProperty>();
if (uids.length == 1 && uids[0].isEmbedded()) {
if (idProp != null && idProp.isEmbedded()) {
BeanPropertyAssocOne<?> one = (BeanPropertyAssocOne<?>) uids[0];
BeanPropertyAssocOne<?> one = (BeanPropertyAssocOne<?>) idProp;
BeanDescriptor<?> targetDesc = one.getTargetDescriptor();
BeanProperty[] emIds = targetDesc.propertiesBaseScalar();
try {
@@ -634,12 +684,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
} catch (PersistenceException e){
// not found as individual scalar properties
e.printStackTrace();
logger.error("Could not find a exported property?", e);
}
} else {
for (int i = 0; i < uids.length; i++) {
ExportedProperty expProp = findMatch(false, uids[i]);
if (idProp != null) {
ExportedProperty expProp = findMatch(false, idProp);
list.add(expProp);
}
}
@@ -741,7 +791,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
throw new PersistenceException(msg);
}
public IntersectionRow buildManyDeleteChildren(Object parentBean, ArrayList<Object> excludeDetailIds) {
public IntersectionRow buildManyDeleteChildren(EntityBean parentBean, ArrayList<Object> excludeDetailIds) {
IntersectionRow row = new IntersectionRow(tableJoin.getTable());
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
@@ -751,14 +801,14 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return row;
}
public IntersectionRow buildManyToManyDeleteChildren(Object parentBean) {
public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean) {
IntersectionRow row = new IntersectionRow(intersectionJoin.getTable());
buildExport(row, parentBean);
return row;
}
public IntersectionRow buildManyToManyMapBean(Object parent, Object other) {
public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other) {
IntersectionRow row = new IntersectionRow(intersectionJoin.getTable());
@@ -767,11 +817,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return row;
}
private void buildExport(IntersectionRow row, Object parentBean) {
private void buildExport(IntersectionRow row, EntityBean parentBean) {
if (embeddedExportedProperties) {
BeanProperty[] uids = descriptor.propertiesId();
parentBean = uids[0].getValue(parentBean);
BeanProperty idProp = descriptor.getIdProperty();
parentBean = (EntityBean)idProp.getValue(parentBean);
}
for (int i = 0; i < exportedProperties.length; i++) {
Object val = exportedProperties[i].getValue(parentBean);
@@ -785,7 +835,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
* Set the predicates for lazy loading of the association.
* Handles predicates for both OneToMany and ManyToMany.
*/
private void buildImport(IntersectionRow row, Object otherBean) {
private void buildImport(IntersectionRow row, EntityBean otherBean) {
importedId.buildImport(row, otherBean);
}
@@ -793,12 +843,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
/**
* Return true if the otherBean has an Id value.
*/
public boolean hasImportedId(Object otherBean) {
public boolean hasImportedId(EntityBean otherBean) {
return null != targetDescriptor.getId(otherBean);
}
public void jsonWrite(WriteJsonContext ctx, Object bean) {
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
if(!this.jsonSerialize){
return;
}
@@ -819,7 +869,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
}
public void jsonRead(ReadJsonContext ctx, Object bean){
public void jsonRead(ReadJsonContext ctx, EntityBean bean){
if(!this.jsonDeserialize){
return;
}
@@ -836,7 +886,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
// probably empty array
break;
}
Object detailBean = detailBeanState.getBean();
EntityBean detailBean = (EntityBean)detailBeanState.getBean();
add.addBean(detailBean);
if (bean != null && childMasterProperty != null){
@@ -844,15 +894,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
childMasterProperty.setValue(detailBean, bean);
detailBeanState.setLoaded(childMasterProperty.getName());
}
detailBeanState.setLoadedState();
if (!ctx.readArrayNext()){
break;
}
} while(true);
setValue(bean, collection);
}
}
@@ -14,6 +14,7 @@ import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
@@ -22,6 +23,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
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;
@@ -34,8 +36,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
private final boolean oneToOneExported;
private final boolean embeddedVersion;
private final boolean importedPrimaryKey;
private final LocalHelp localHelp;
@@ -78,11 +78,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// Overriding of the columns and use table alias of owning BeanDescriptor
BeanEmbeddedMeta overrideMeta = BeanEmbeddedMetaFactory.create(owner, deploy, descriptor);
embeddedProps = overrideMeta.getProperties();
if (id) {
embeddedVersion = false;
} else {
embeddedVersion = overrideMeta.isEmbeddedVersion();
}
embeddedPropsMap = new HashMap<String, BeanProperty>();
for (int i = 0; i < embeddedProps.length; i++) {
embeddedPropsMap.put(embeddedProps[i].getName(), embeddedProps[i]);
@@ -91,7 +86,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
} else {
embeddedProps = null;
embeddedPropsMap = null;
embeddedVersion = false;
}
localHelp = createHelp(embedded, oneToOneExported);
}
@@ -115,6 +109,13 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
}
/**
* Return the property value as an entity bean.
*/
public EntityBean getValueAsEntityBean(EntityBean owner) {
return (EntityBean)getValue(owner);
}
public void setRelationshipProperty(BeanPropertyAssocMany<?> relationshipProperty){
this.relationshipProperty = relationshipProperty;
@@ -126,22 +127,22 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
public void cacheClear() {
if (targetDescriptor.isBeanCaching() && relationshipProperty != null) {
targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName());
targetDescriptor.cacheManyPropClear(relationshipProperty.getName());
}
}
public void cacheDelete(boolean clearOnNull, Object bean) {
public void cacheDelete(boolean clearOnNull, EntityBean bean) {
if (targetDescriptor.isBeanCaching() && relationshipProperty != null) {
Object assocBean = getValue(bean);
if (assocBean != null) {
Object parentId = targetDescriptor.getId(assocBean);
Object parentId = targetDescriptor.getId((EntityBean)assocBean);
if (parentId != null) {
targetDescriptor.cacheRemoveCachedManyIds(parentId, relationshipProperty.getName());
targetDescriptor.cacheManyPropRemove(parentId, relationshipProperty.getName());
return;
}
}
if (clearOnNull) {
targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName());
targetDescriptor.cacheManyPropClear(relationshipProperty.getName());
}
}
}
@@ -249,8 +250,9 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
} else {
int pos = 1;
EntityBean parent = (EntityBean)parentId;
for (int i = 0; i < exportedProperties.length; i++) {
Object embVal = exportedProperties[i].getValue(parentId);
Object embVal = exportedProperties[i].getValue(parent);
q.setParameter(pos++, embVal);
}
}
@@ -270,41 +272,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return true;
}
private boolean hasChangedEmbedded(Object bean, Object oldValues) {
Object embValue = getValue(oldValues);
if (embValue instanceof EntityBean) {
// the embedded bean .. has its own old values
return ((EntityBean) embValue)._ebean_getIntercept().isNewOrDirty();
}
if (embValue == null) {
return getValue(bean) != null;
} else {
return false;
}
}
@Override
public boolean hasChanged(Object bean, Object oldValues) {
if (embedded) {
return hasChangedEmbedded(bean, oldValues);
}
Object value = getValue(bean);
Object oldVal = getValue(oldValues);
if (oneToOneExported) {
// FKey on other side
return false;
} else {
if (value == null) {
return oldVal != null;
} else if (oldValues == null) {
return true;
}
return importedId.hasChanged(value, oldVal);
}
}
/**
* Return meta data for the deployment of the embedded bean specific to this
* property.
@@ -342,13 +309,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return oneToOneExported;
}
/**
* Returns true if the associated bean has version properties.
*/
public boolean isEmbeddedVersion() {
return embeddedVersion;
}
/**
* If true this bean maps to the primary key.
*/
@@ -364,32 +324,30 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return getPropertyType();
}
public Object getCacheDataValue(Object bean){
if (embedded) {
throw new RuntimeException();
public Object getCacheDataValue(EntityBean bean){
Object ap = getValue(bean);
if (ap == null){
return null;
}
if (embedded) {
return targetDescriptor.cacheBeanExtractData((EntityBean) ap);
} else {
Object ap = getValue(bean);
if (ap == null){
return null;
} else {
return targetDescriptor.getId(ap);
}
return targetDescriptor.getId((EntityBean)ap);
}
}
public void setCacheDataValue(Object bean, Object cacheData, Object oldValues, boolean readOnly){
@Override
public void setCacheDataValue(EntityBean bean, Object cacheData){
if (cacheData != null) {
if (embedded){
throw new RuntimeException();
EntityBean embeddedBean = targetDescriptor.createEntityBean();
targetDescriptor.cacheBeanLoadData(embeddedBean, (CachedBeanData) cacheData);
setValue(bean, embeddedBean);
} else {
T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData, null);
T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData);
setValue(bean, ref);
if (oldValues != null){
setValue(oldValues, ref);
}
if (readOnly){
((EntityBean)ref)._ebean_intercept().setReadOnly(true);
}
}
}
}
@@ -398,7 +356,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
* Return the Id values from the given bean.
*/
@Override
public Object[] getAssocOneIdValues(Object bean) {
public Object[] getAssocOneIdValues(EntityBean bean) {
return targetDescriptor.getIdBinder().getIdValues(bean);
}
@@ -441,7 +399,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
* value.
*/
public Object createEmbeddedId() {
return getTargetDescriptor().createBean();
return getTargetDescriptor().createEntityBean();
}
/**
@@ -451,15 +409,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return targetDescriptor.createEntityBean();
}
public void elSetReference(Object bean) {
Object value = getValueIntercept(bean);
if (value != null) {
((EntityBean) value)._ebean_getIntercept().setReference();
}
}
@Override
public Object elGetReference(Object bean) {
public Object elGetReference(EntityBean bean) {
Object value = getValueIntercept(bean);
if (value == null) {
value = targetDescriptor.createEntityBean();
@@ -495,13 +446,13 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
*/
private ExportedProperty[] createExported() {
BeanProperty[] uids = descriptor.propertiesId();
BeanProperty idProp = descriptor.getIdProperty();
ArrayList<ExportedProperty> list = new ArrayList<ExportedProperty>();
if (uids.length == 1 && uids[0].isEmbedded()) {
if (idProp != null && idProp.isEmbedded()) {
BeanPropertyAssocOne<?> one = (BeanPropertyAssocOne<?>) uids[0];
BeanPropertyAssocOne<?> one = (BeanPropertyAssocOne<?>) idProp;
BeanDescriptor<?> targetDesc = one.getTargetDescriptor();
BeanProperty[] emIds = targetDesc.propertiesBaseScalar();
try {
@@ -515,8 +466,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
} else {
for (int i = 0; i < uids.length; i++) {
ExportedProperty expProp = findMatch(false, uids[i]);
if (idProp != null) {
ExportedProperty expProp = findMatch(false, idProp);
list.add(expProp);
}
}
@@ -558,14 +509,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (!isTransient) {
localHelp.appendFrom(ctx, forceOuterJoin);
localHelp.appendFrom(ctx, joinType);
}
}
@Override
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
public Object readSet(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
boolean assignable = (type == null || owningType.isAssignableFrom(type));
return localHelp.readSet(ctx, bean, assignable);
}
@@ -579,6 +530,24 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// pass in null for the bean so any data read is ignored
return localHelp.read(ctx);
}
@Override
public void setValue(EntityBean bean, Object value) {
super.setValue(bean, value);
if (value instanceof EntityBean) {
EntityBean embedded = (EntityBean)value;
embedded._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex);
}
}
@Override
public void setValueIntercept(EntityBean bean, Object value) {
super.setValueIntercept(bean, value);
if (value instanceof EntityBean) {
EntityBean embedded = (EntityBean)value;
embedded._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex);
}
}
@Override
public void loadIgnore(DbReadContext ctx) {
@@ -615,11 +584,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
abstract Object read(DbReadContext ctx) throws SQLException;
abstract Object readSet(DbReadContext ctx, Object bean, boolean assignAble) throws SQLException;
abstract Object readSet(DbReadContext ctx, EntityBean bean, boolean assignAble) throws SQLException;
abstract void appendSelect(DbSqlContext ctx, boolean subQuery);
abstract void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
abstract void appendFrom(DbSqlContext ctx, SqlJoinType joinType);
}
@@ -632,7 +601,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException {
Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException {
Object dbVal = read(ctx);
if (bean != null && assignable) {
// set back to the parent bean
@@ -665,7 +634,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
}
@Override
@@ -694,7 +663,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException {
Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException {
Object val = read(ctx);
if (bean != null && assignable) {
setValue(bean, val);
@@ -733,16 +702,13 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return existing;
}
// parent always null for this case (but here to document)
Object parent = null;
Boolean readOnly = ctx.isReadOnly();
Object ref;
if (targetInheritInfo != null) {
// for inheritance hierarchy create the correct type for this row...
ref = rowDescriptor.createReference(readOnly, id, parent);
// for inheritance hierarchy create the correct type for this row...
ref = rowDescriptor.createReference(readOnly, id);
} else {
ref = targetDescriptor.createReference(readOnly, id, parent);
ref = targetDescriptor.createReference(readOnly, id);
}
Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref);
@@ -764,11 +730,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (targetInheritInfo != null) {
// add join to support the discriminator column
String relativePrefix = ctx.getRelativePrefix(name);
tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -802,7 +768,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
* Read and set a Reference bean.
*/
@Override
Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException {
Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException {
Object dbVal = read(ctx);
if (bean != null && assignable) {
@@ -828,8 +794,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
if (existing != null) {
return existing;
}
Object parent = null;
Object ref = targetDescriptor.createReference(ctx.isReadOnly(), id, parent);
Object ref = targetDescriptor.createReference(ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
if (Boolean.TRUE.equals(ctx.isReadOnly())) {
@@ -846,8 +811,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
// set appropriate tableAlias for
// the exported id columns
// set appropriate tableAlias for the exported id columns
String relativePrefix = ctx.getRelativePrefix(getName());
ctx.pushTableAlias(relativePrefix);
@@ -859,15 +823,15 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
String relativePrefix = ctx.getRelativePrefix(getName());
tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@Override
public void jsonWrite(WriteJsonContext ctx, Object bean) {
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
Object value = getValueIntercept(bean);
if (value == null){
@@ -878,20 +842,43 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// 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, value);
refDesc.jsonWrite(ctx, (EntityBean)value);
ctx.endAssocOne();
ctx.popParentBean();
}
}
}
}
@Override
public void jsonRead(ReadJsonContext ctx, Object bean){
T assocBean = targetDescriptor.jsonReadBean(ctx, name);
setValue(bean, assocBean);
public void jsonRead(ReadJsonContext ctx, EntityBean bean){
if (targetDescriptor != null) {
T assocBean = targetDescriptor.jsonReadBean(ctx, name);
setValue(bean, assocBean);
}
}
public boolean isReference(Object detailBean) {
EntityBean eb = (EntityBean)detailBean;
return targetDescriptor.isReference(eb._ebean_getIntercept());
}
/**
* Set the parent bean to the child bean if it has not already been set.
*/
public void setParentBeanToChild(EntityBean parent, EntityBean child) {
if (mappedBy != null) {
BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy);
if (beanProperty != null && beanProperty.getValue(child) == null) {
// set the 'parent' bean to the 'child' bean
beanProperty.setValue(child, parent);
}
}
}
}
@@ -4,6 +4,7 @@ import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
@@ -89,7 +90,7 @@ public class BeanPropertyCompound extends BeanProperty {
* Get the underlying compound type.
*/
@SuppressWarnings("unchecked")
public Object getValueUnderlying(Object bean) {
public Object getValueUnderlying(EntityBean bean) {
Object value = getValue(bean);
if (typeConverter != null){
@@ -97,27 +98,7 @@ public class BeanPropertyCompound extends BeanProperty {
}
return value;
}
@Override
public Object getValue(Object bean) {
return super.getValue(bean);
}
@Override
public Object getValueIntercept(Object bean) {
return super.getValueIntercept(bean);
}
@Override
public void setValue(Object bean, Object value) {
super.setValue(bean, value);
}
@Override
public void setValueIntercept(Object bean, Object value) {
super.setValueIntercept(bean, value);
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
if (chain == null) {
@@ -154,7 +135,7 @@ public class BeanPropertyCompound extends BeanProperty {
}
@Override
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
public Object readSet(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
boolean assignable = (type == null || owningType.isAssignableFrom(type));
@@ -192,17 +173,17 @@ public class BeanPropertyCompound extends BeanProperty {
}
@Override
public Object elGetReference(Object bean) {
public Object elGetReference(EntityBean bean) {
return bean;
}
public void jsonWrite(WriteJsonContext ctx, Object bean) {
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
Object valueObject = getValueIntercept(bean);
compoundType.jsonWrite(ctx, valueObject, name);
}
public void jsonRead(ReadJsonContext ctx, Object bean){
public void jsonRead(ReadJsonContext ctx, EntityBean bean){
Object objValue = compoundType.jsonRead(ctx);
setValue(bean, objValue);
@@ -73,7 +73,7 @@ public class BeanPropertyCompoundRoot {
* Set the value of the property without interception or
* PropertyChangeSupport.
*/
public void setRootValue(Object bean, Object value) {
public void setRootValue(EntityBean bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.set(bean, value);
@@ -92,7 +92,7 @@ public class BeanPropertyCompoundRoot {
/**
* Set the value of the property.
*/
public void setRootValueIntercept(Object bean, Object value) {
public void setRootValueIntercept(EntityBean bean, Object value) {
try {
if (bean instanceof EntityBean) {
setter.setIntercept(bean, value);
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
@@ -30,20 +31,21 @@ public class BeanPropertyCompoundScalar extends BeanProperty {
@SuppressWarnings("unchecked")
@Override
public Object getValue(Object valueObject) {
if (typeConverter != null){
valueObject = typeConverter.unwrapValue(valueObject);
public Object getValue(EntityBean valueObject) {
Object val = valueObject;
if (typeConverter != null){
val = typeConverter.unwrapValue(val);
}
return ctProperty.getValue(valueObject);
return ctProperty.getValue(val);
}
@Override
public void setValue(Object bean, Object value) {
public void setValue(EntityBean bean, Object value) {
setValueInCompound(bean, value, false);
}
@SuppressWarnings("unchecked")
public void setValueInCompound(Object bean, Object value, boolean intercept) {
public void setValueInCompound(EntityBean bean, Object value, boolean intercept) {
Object compoundValue = ctProperty.setValue(bean, value);
@@ -65,7 +67,7 @@ public class BeanPropertyCompoundScalar extends BeanProperty {
* No interception on embedded scalar values inside a CVO.
*/
@Override
public void setValueIntercept(Object bean, Object value) {
public void setValueIntercept(EntityBean bean, Object value) {
setValueInCompound(bean, value, true);
}
@@ -73,28 +75,23 @@ public class BeanPropertyCompoundScalar extends BeanProperty {
* No interception on embedded scalar values inside a CVO.
*/
@Override
public Object getValueIntercept(Object bean) {
public Object getValueIntercept(EntityBean bean) {
return getValue(bean);
}
@Override
public Object elGetReference(Object bean) {
public Object elGetReference(EntityBean bean) {
return getValue(bean);
}
@Override
public Object elGetValue(Object bean) {
public Object elGetValue(EntityBean bean) {
return getValue(bean);
}
@Override
public void elSetReference(Object bean) {
super.elSetReference(bean);
}
@Override
public void elSetValue(Object bean, Object value, boolean populate, boolean reference) {
super.elSetValue(bean, value, populate, reference);
public void elSetValue(EntityBean bean, Object value, boolean populate) {//, boolean reference) {
super.elSetValue(bean, value, populate);
}
@@ -10,6 +10,7 @@ import com.avaje.ebean.Transaction;
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.ebean.common.BeanSet;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
@@ -72,15 +73,12 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
this.set = set;
}
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
set.add(bean);
}
}
/**
* Internal add bypassing any modify listening.
*/
public void add(BeanCollection<?> collection, Object bean) {
public void add(BeanCollection<?> collection, EntityBean bean) {
collection.internalAdd(bean);
}
@@ -95,20 +93,20 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
return beanSet;
}
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
public BeanCollection<T> createReference(EntityBean parentBean, String propertyName) {
BeanSet<T> beanSet = new BeanSet<T>(loader, parentBean, propertyName);
beanSet.setModifyListening(many.getModifyListenMode());
return beanSet;
}
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
public void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean) {
BeanSet<?> newBeanSet = (BeanSet<?>)server.findSet(query, t);
refresh(newBeanSet, parentBean);
}
public void refresh(BeanCollection<?> bc, Object parentBean) {
public void refresh(BeanCollection<?> bc, EntityBean parentBean) {
BeanSet<?> newBeanSet = (BeanSet<?>)bc;
@@ -158,7 +156,7 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
if (count++ > 0){
ctx.appendComma();
}
targetDescriptor.jsonWrite(ctx, detailBean);
targetDescriptor.jsonWrite(ctx, (EntityBean)detailBean);
}
ctx.endAssocMany();
}
@@ -12,6 +12,7 @@ import com.avaje.ebean.event.BeanPersistListener;
public class ChainedBeanPersistListener<T> implements BeanPersistListener<T> {
private final List<BeanPersistListener<T>> list;
private final BeanPersistListener<T>[] chain;
/**
@@ -156,7 +156,6 @@ public class DRawSqlSelect {
}
}
selectProps.setIncludedProperties(includedProps);
SqlTreeNode sqlRoot = new SqlTreeNodeRoot(desc, selectProps, null, withId);
sqlTree.setRootNode(sqlRoot);
@@ -193,11 +193,10 @@ public final class DRawSqlSelectColumnsParser {
}
}
BeanProperty[] propertiesId = desc.propertiesId();
for (int i = 0; i < propertiesId.length; i++) {
BeanProperty prop = propertiesId[i];
if (isMatch(prop, searchColumn)) {
return prop;
BeanProperty idProp = desc.getIdProperty();
if (idProp != null) {
if (isMatch(idProp, searchColumn)) {
return idProp;
}
}
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.deploy;
import java.util.Map;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -72,12 +73,12 @@ public interface DbReadContext {
/**
* Set back the bean that has just been loaded with its id.
*/
public void setLoadedBean(Object loadedBean, Object id, Object lazyLoadParentId);
public void setLoadedBean(EntityBean loadedBean, Object id, Object lazyLoadParentId);
/**
* Set back the 'detail' bean that has just been loaded.
*/
public void setLoadedManyBean(Object loadedBean);
public void setLoadedManyBean(EntityBean loadedBean);
/**
* Return the query mode.
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Used to provide context during sql construction.
*/
@@ -72,7 +74,7 @@ public interface DbSqlContext {
* Append a Sql Formula join. This converts the "${ta}" keyword to the current
* table alias.
*/
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin);
public void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType);
/**
* Return the current content length.
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
/**
@@ -32,7 +33,7 @@ public class ExportedProperty {
/**
* Return the property value from the bean.
*/
public Object getValue(Object bean){
public Object getValue(EntityBean bean){
return property.getValue(bean);
}
@@ -6,344 +6,339 @@ import java.util.HashMap;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInheritInfo;
import com.avaje.ebeaninternal.server.query.SqlTreeProperties;
/**
* Represents a node in the Inheritance tree. Holds information regarding Super
* Subclass support.
* Represents a node in the Inheritance tree. Holds information regarding Super Subclass support.
*/
public class InheritInfo {
private final String discriminatorStringValue;
private final Object discriminatorValue;
private final String discriminatorStringValue;
private final Object discriminatorValue;
private final String discriminatorColumn;
private final String discriminatorColumn;
private final int discriminatorType;
private final int discriminatorType;
private final int discriminatorLength;
private final int discriminatorLength;
private final String where;
private final String where;
private final Class<?> type;
private final Class<?> type;
private final ArrayList<InheritInfo> children = new ArrayList<InheritInfo>();
private final ArrayList<InheritInfo> children = new ArrayList<InheritInfo>();
/**
* Map of discriminator values to InheritInfo.
*/
private final HashMap<String, InheritInfo> discMap;
/**
* Map of class types to InheritInfo (taking into account subclass proxy classes).
*/
private final HashMap<String, InheritInfo> typeMap;
/**
* Map of discriminator values to InheritInfo.
*/
private final HashMap<String, InheritInfo> discMap;
private final InheritInfo parent;
/**
* Map of class types to InheritInfo (taking into account subclass proxy classes).
*/
private final HashMap<String, InheritInfo> typeMap;
private final InheritInfo root;
private final InheritInfo parent;
private BeanDescriptor<?> descriptor;
private final InheritInfo root;
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
// this is a root node
root = this;
discMap = new HashMap<String, InheritInfo>();
typeMap = new HashMap<String, InheritInfo>();
registerWithRoot(this);
private BeanDescriptor<?> descriptor;
} else {
this.root = r;
// register with the root node...
discMap = null;
typeMap = null;
root.registerWithRoot(this);
}
}
/**
* Visit all the children in the inheritance tree.
*/
public void visitChildren(InheritInfoVisitor visitor) {
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
visitor.visit(child);
child.visitChildren(visitor);
}
}
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a save cascade on it.
*/
public boolean isSaveRecurseSkippable() {
return root.isNodeSaveRecurseSkippable();
}
private boolean isNodeSaveRecurseSkippable() {
if (!descriptor.isSaveRecurseSkippable()){
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeSaveRecurseSkippable()){
return false;
}
}
return true;
}
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a delete cascade on it.
*/
public boolean isDeleteRecurseSkippable() {
return root.isNodeDeleteRecurseSkippable();
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
// this is a root node
root = this;
discMap = new HashMap<String, InheritInfo>();
typeMap = new HashMap<String, InheritInfo>();
registerWithRoot(this);
} else {
this.root = r;
// register with the root node...
discMap = null;
typeMap = null;
root.registerWithRoot(this);
}
}
/**
* Visit all the children in the inheritance tree.
*/
public void visitChildren(InheritInfoVisitor visitor) {
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
visitor.visit(child);
child.visitChildren(visitor);
}
}
/**
* return true if anything in the inheritance hierarchy has a relationship with a save cascade on
* it.
*/
public boolean isSaveRecurseSkippable() {
return root.isNodeSaveRecurseSkippable();
}
private boolean isNodeSaveRecurseSkippable() {
if (!descriptor.isSaveRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeSaveRecurseSkippable()) {
return false;
}
}
return true;
}
/**
* return true if anything in the inheritance hierarchy has a relationship with a delete cascade
* on it.
*/
public boolean isDeleteRecurseSkippable() {
return root.isNodeDeleteRecurseSkippable();
}
private boolean isNodeDeleteRecurseSkippable() {
if (!descriptor.isDeleteRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeDeleteRecurseSkippable()) {
return false;
}
}
return true;
}
/**
* Set the descriptor for this node.
*/
public void setDescriptor(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
/**
* Return the associated BeanDescriptor for this node.
*/
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
/**
* Get the bean property additionally looking in the sub types.
*/
public BeanProperty findSubTypeProperty(String propertyName) {
BeanProperty prop = null;
for (int i = 0, x = children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
// recursively search this child bean descriptor
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
if (prop != null) {
return prop;
}
}
private boolean isNodeDeleteRecurseSkippable() {
if (!descriptor.isDeleteRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeDeleteRecurseSkippable()) {
return false;
}
}
return true;
return null;
}
/**
* Add the local properties for each sub class below this one.
*/
public void addChildrenProperties(SqlTreeProperties selectProps) {
for (int i = 0, x = children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
selectProps.add(childInfo.descriptor.propertiesLocal());
childInfo.addChildrenProperties(selectProps);
}
/**
* Set the descriptor for this node.
*/
public void setDescriptor(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
/**
* Return the associated BeanDescriptor for this node.
*/
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
/**
* Get the bean property additionally looking in the sub types.
*/
public BeanProperty findSubTypeProperty(String propertyName) {
BeanProperty prop = null;
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
// recursively search this child bean descriptor
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
if (prop != null){
return prop;
}
}
return null;
}
/**
* Add the local properties for each sub class below this one.
*/
public void addChildrenProperties(SqlTreeProperties selectProps) {
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
selectProps.add(childInfo.descriptor.propertiesLocal());
childInfo.addChildrenProperties(selectProps);
}
}
}
/**
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
/**
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
String discValue = ctx.getDataReader().getString();
return readType(discValue);
}
/**
* Return the associated InheritInfo for this discriminator value.
*/
public InheritInfo readType(String discValue) {
String discValue = ctx.getDataReader().getString();
return readType(discValue);
}
if (discValue == null) {
return null;
}
/**
* Return the associated InheritInfo for this discriminator value.
*/
public InheritInfo readType(String discValue) {
InheritInfo typeInfo = root.getType(discValue);
if (typeInfo == null) {
String m = "Inheritance type for discriminator value [" + discValue + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
if (discValue == null) {
return null;
}
/**
* Return the associated InheritInfo for this bean type.
*/
public InheritInfo readType(Class<?> beanType) {
InheritInfo typeInfo = root.getTypeByClass(beanType);
if (typeInfo == null) {
String m = "Inheritance type for bean type [" + beanType.getName() + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
InheritInfo typeInfo = root.getType(discValue);
if (typeInfo == null) {
throw new PersistenceException("Inheritance type for discriminator value [" + discValue + "] was not found?");
}
return typeInfo;
}
/**
* Create an EntityBean for this type.
*/
public Object createBean() {
return descriptor.createBean();
}
/**
* Return the IdBinder for this type.
*/
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
/**
* return the type.
*/
public Class<?> getType() {
return type;
}
/**
* Return the associated InheritInfo for this bean type.
*/
public InheritInfo readType(Class<?> beanType) {
/**
* Return the root node of the tree.
* <p>
* The root has a map of discriminator values to types.
* </p>
*/
public InheritInfo getRoot() {
return root;
}
/**
* Return the parent node.
*/
public InheritInfo getParent() {
return parent;
InheritInfo typeInfo = root.getTypeByClass(beanType);
if (typeInfo == null) {
throw new PersistenceException("Inheritance type for bean type [" + beanType.getName() + "] was not found?");
}
/**
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorValue == null);
}
return typeInfo;
}
/**
* Return true if this is the root node.
*/
public boolean isRoot() {
return parent == null;
}
/**
* Create an EntityBean for this type.
*/
public EntityBean createEntityBean() {
return descriptor.createEntityBean();
}
/**
* For a discriminator get the inheritance information for this tree.
*/
public InheritInfo getType(String discValue) {
return discMap.get(discValue);
}
/**
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
return typeMap.get(beanType.getName());
/**
* Return the IdBinder for this type.
*/
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
/**
* return the type.
*/
public Class<?> getType() {
return type;
}
/**
* Return the root node of the tree.
* <p>
* The root has a map of discriminator values to types.
* </p>
*/
public InheritInfo getRoot() {
return root;
}
/**
* Return the parent node.
*/
public InheritInfo getParent() {
return parent;
}
/**
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorValue == null);
}
/**
* Return true if this is the root node.
*/
public boolean isRoot() {
return parent == null;
}
/**
* For a discriminator get the inheritance information for this tree.
*/
public InheritInfo getType(String discValue) {
return discMap.get(discValue);
}
/**
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
return typeMap.get(beanType.getName());
}
private void registerWithRoot(InheritInfo info) {
if (info.getDiscriminatorStringValue() != null) {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
typeMap.put(info.getType().getName(), info);
}
private void registerWithRoot(InheritInfo info) {
if (info.getDiscriminatorStringValue() != null) {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
typeMap.put(info.getType().getName(), info);
}
/**
* Add a child node.
*/
public void addChild(InheritInfo childInfo) {
children.add(childInfo);
}
/**
* Add a child node.
*/
public void addChild(InheritInfo childInfo) {
children.add(childInfo);
}
/**
* Return the derived where for the discriminator.
*/
public String getWhere() {
/**
* Return the derived where for the discriminator.
*/
public String getWhere() {
return where;
}
return where;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn() {
return discriminatorColumn;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn() {
return discriminatorColumn;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType() {
return discriminatorType;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType() {
return discriminatorType;
}
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
return discriminatorLength;
}
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
return discriminatorLength;
}
/**
* Return the discriminator value for this node.
*/
public String getDiscriminatorStringValue() {
return discriminatorStringValue;
}
/**
* Return the discriminator value for this node.
*/
public String getDiscriminatorStringValue() {
return discriminatorStringValue;
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
public String toString() {
return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]";
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
public String toString() {
return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]";
}
}
@@ -1,85 +0,0 @@
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
/**
* For abstract classes that hold the id property we need to
* use reflection to get the id values some times.
* <p>
* This provides the BeanReflectGetter objects to do that.
* </p>
* @author rbygrave
*/
public class ReflectGetter {
/**
* Create a reflection based BeanReflectGetter for getting the
* id from abstract inheritance hierarchy object.
*/
public static BeanReflectGetter create(DeployBeanProperty prop) {
if (!prop.isId()){
// not expecting this to ever be used/called
return new NonIdGetter(prop.getFullBeanName());
} else {
String property = prop.getFullBeanName();
Method readMethod = prop.getReadMethod();
if (readMethod == null){
String m = "Abstract class with no readMethod for "+property;
throw new RuntimeException(m);
}
return new IdGetter(property, readMethod);
}
}
public static class IdGetter implements BeanReflectGetter {
public static final Object[] NO_ARGS = new Object[0];
private final Method readMethod;
private final String property;
public IdGetter(String property, Method readMethod) {
this.property = property;
this.readMethod = readMethod;
}
public Object get(Object bean) {
try {
return readMethod.invoke(bean, NO_ARGS);
} catch (Exception e) {
String m = "Error on ["+property+"] using readMethod "+readMethod;
throw new RuntimeException(m, e);
}
}
public Object getIntercept(Object bean) {
return get(bean);
}
}
public static class NonIdGetter implements BeanReflectGetter {
private final String property;
public NonIdGetter(String property) {
this.property = property;
}
public Object get(Object bean) {
String m = "Not expecting this method to be called on ["+property
+"] as it is a NON ID property on an abstract class";
throw new RuntimeException(m);
}
public Object getIntercept(Object bean) {
return get(bean);
}
}
}
@@ -1,53 +0,0 @@
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
/**
* A place holder for BeanReflectSetter that should never be called.
* <p>
* This is for properties of classes that are abstract and at the root
* of an inheritance hierarchy.
* </p>
* @author rbygrave
*/
public class ReflectSetter {
/**
* Creates place holder objects that should never be called.
*/
public static BeanReflectSetter create(DeployBeanProperty prop) {
String fullName = prop.getFullBeanName();
Method writeMethod = prop.getWriteMethod();
return new RefCalled(fullName, writeMethod);
}
static class RefCalled implements BeanReflectSetter {
final String fullName;
final Method writeMethod;
RefCalled(String fullName, Method writeMethod) {
this.fullName = fullName;
this.writeMethod = writeMethod;
}
public void set(Object bean, Object value) {
Object[] a = new Object[1];
a[0] = value;
try {
writeMethod.invoke(bean, a);
} catch (Exception e) {
String beanType = bean == null ? "null" : bean.getClass().toString();
String msg = "Error setting value on "+fullName+" value["+value+"] on type["+beanType+"]";
throw new RuntimeException(msg, e);
}
}
public void setIntercept(Object bean, Object value) {
String msg = "Not expecting setIntercept to be called. Refer Bug 368";
throw new RuntimeException(msg);
}
}
}
@@ -3,12 +3,14 @@ package com.avaje.ebeaninternal.server.deploy;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join to another table.
@@ -31,9 +33,9 @@ public final class TableJoin {
private final String table;
/**
* The type of join. LEFT OUTER etc.
* The type of join as per deployment (cardinality and optionality).
*/
private final String type;
private final SqlJoinType type;
/**
* The persist cascade info.
@@ -59,7 +61,7 @@ public final class TableJoin {
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
this.table = InternString.intern(deploy.getTable());
this.type = InternString.intern(deploy.getType());
this.type = deploy.getType();
this.cascadeInfo = deploy.getCascadeInfo();
this.inheritInfo = deploy.getInheritInfo();
@@ -103,7 +105,7 @@ public final class TableJoin {
}
}
public Object readSet(DbReadContext ctx, Object bean, Class<?> type) throws SQLException {
public Object readSet(DbReadContext ctx, EntityBean bean, Class<?> type) throws SQLException {
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].readSet(ctx, bean, type);
}
@@ -148,7 +150,7 @@ public final class TableJoin {
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public String getType() {
public SqlJoinType getType() {
return type;
}
@@ -156,32 +158,26 @@ public final class TableJoin {
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type.equals(LEFT_OUTER);
return type == SqlJoinType.OUTER;
}
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
String[] names = SplitName.split(prefix);
String a1 = ctx.getTableAlias(names[0]);
String a2 = ctx.getTableAlias(prefix);
return addJoin(forceOuterJoin, a1, a2, ctx);
return addJoin(joinType, a1, a2, ctx);
}
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2, inheritance);
String joinLiteral = joinType.getLiteral(type);
ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance);
return forceOuterJoin || LEFT_OUTER.equals(type);
}
/**
* Explicitly add a (non-outer) join.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
ctx.addJoin(JOIN, table, columns(), a1, a2, inheritance);
return joinType.autoToOuter(type);
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -17,7 +18,7 @@ public class GeneratedCounter implements GeneratedProperty {
/**
* Always returns a 1.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
Integer i = Integer.valueOf(1);
return BasicTypeConverter.convert(i, numberType);
}
@@ -25,7 +26,7 @@ public class GeneratedCounter implements GeneratedProperty {
/**
* Increments the current value by one.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
Number currVal = (Number) prop.getValue(bean);
Integer nextVal = Integer.valueOf(currVal.intValue() + 1);
return BasicTypeConverter.convert(nextVal, numberType);
@@ -38,6 +39,11 @@ public class GeneratedCounter implements GeneratedProperty {
return true;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
/**
* Include this in every insert setting initial counter value to 1.
*/
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -14,14 +15,14 @@ public class GeneratedCounterInteger implements GeneratedProperty {
/**
* Always returns a 1.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return Integer.valueOf(1);
}
/**
* Increments the current value by one.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
Integer i = (Integer) prop.getValue(bean);
return Integer.valueOf(i.intValue() + 1);
}
@@ -32,6 +33,11 @@ public class GeneratedCounterInteger implements GeneratedProperty {
public boolean includeInUpdate() {
return true;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
/**
* Include this in every insert setting initial counter value to 1.
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -14,14 +15,14 @@ public class GeneratedCounterLong implements GeneratedProperty {
/**
* Always returns a 1.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return Long.valueOf(1);
}
/**
* Increments the current value by one.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
Long i = (Long) prop.getValue(bean);
return Long.valueOf(i.longValue() + 1);
}
@@ -33,6 +34,11 @@ public class GeneratedCounterLong implements GeneratedProperty {
return true;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
/**
* Include this in every insert setting initial counter value to 1.
*/
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import java.util.Date;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -12,14 +13,14 @@ public class GeneratedInsertDate implements GeneratedProperty {
/**
* Return the current time as a Timestamp.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return new Date(System.currentTimeMillis());
}
/**
* Just returns the beans original insert timestamp value.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return prop.getValue(bean);
}
@@ -30,6 +31,11 @@ public class GeneratedInsertDate implements GeneratedProperty {
return false;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
/**
* Return true.
*/
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -10,14 +11,14 @@ public class GeneratedInsertLong implements GeneratedProperty {
/**
* Return the current time as a Timestamp.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return Long.valueOf(System.currentTimeMillis());
}
/**
* Just returns the beans original insert timestamp value.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return prop.getValue(bean);
}
@@ -28,6 +29,11 @@ public class GeneratedInsertLong implements GeneratedProperty {
return false;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
/**
* Return true.
*/
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import java.sql.Timestamp;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -12,14 +13,14 @@ public class GeneratedInsertTimestamp implements GeneratedProperty {
/**
* Return the current time as a Timestamp.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return new Timestamp(System.currentTimeMillis());
}
/**
* Just returns the beans original insert timestamp value.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return prop.getValue(bean);
}
@@ -29,6 +30,11 @@ public class GeneratedInsertTimestamp implements GeneratedProperty {
public boolean includeInUpdate() {
return false;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
/**
* Return true.
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -11,12 +12,12 @@ public interface GeneratedProperty {
/**
* Get the generated insert value for a specific property of a bean.
*/
public Object getInsertValue(BeanProperty prop, Object bean);
public Object getInsertValue(BeanProperty prop, EntityBean bean);
/**
* Get the generated update value for a specific property of a bean.
*/
public Object getUpdateValue(BeanProperty prop, Object bean);
public Object getUpdateValue(BeanProperty prop, EntityBean bean);
/**
* Return true if this should always be includes in an update statement.
@@ -25,6 +26,12 @@ public interface GeneratedProperty {
* </p>
*/
public boolean includeInUpdate();
/**
* Return true if the property should be included in an update even if
* it is not loaded (ie. Last Updated Timestamp).
*/
public boolean includeInAllUpdates();
/**
* Return true if this should be included in insert statements.
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import java.util.Date;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -13,14 +14,14 @@ public class GeneratedUpdateDate implements GeneratedProperty {
/**
* Return now as a Timestamp.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return new Date(System.currentTimeMillis());
}
/**
* Return now as a Timestamp.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return new Date(System.currentTimeMillis());
}
@@ -30,6 +31,11 @@ public class GeneratedUpdateDate implements GeneratedProperty {
public boolean includeInUpdate() {
return true;
}
@Override
public boolean includeInAllUpdates() {
return true;
}
/**
* Include this in every insert.
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -10,14 +11,14 @@ public class GeneratedUpdateLong implements GeneratedProperty {
/**
* Return now as a Timestamp.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return Long.valueOf(System.currentTimeMillis());
}
/**
* Return now as a Timestamp.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return Long.valueOf(System.currentTimeMillis());
}
@@ -28,6 +29,11 @@ public class GeneratedUpdateLong implements GeneratedProperty {
return true;
}
@Override
public boolean includeInAllUpdates() {
return true;
}
/**
* Include this in every insert.
*/
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import java.sql.Timestamp;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
@@ -12,14 +13,14 @@ public class GeneratedUpdateTimestamp implements GeneratedProperty {
/**
* Return now as a Timestamp.
*/
public Object getInsertValue(BeanProperty prop, Object bean) {
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return new Timestamp(System.currentTimeMillis());
}
/**
* Return now as a Timestamp.
*/
public Object getUpdateValue(BeanProperty prop, Object bean) {
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return new Timestamp(System.currentTimeMillis());
}
@@ -30,6 +31,11 @@ public class GeneratedUpdateTimestamp implements GeneratedProperty {
return true;
}
@Override
public boolean includeInAllUpdates() {
return true;
}
/**
* Include this in every insert.
*/
@@ -6,6 +6,8 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -42,6 +44,11 @@ public interface IdBinder {
*/
public String getIdProperty();
/**
* Return the Id BeanProperty.
*/
public BeanProperty getBeanProperty();
/**
* Find a BeanProperty that is mapped to the database column.
*/
@@ -81,7 +88,7 @@ public interface IdBinder {
/**
* Return the id values for a given bean.
*/
public Object[] getIdValues(Object bean);
public Object[] getIdValues(EntityBean bean);
/**
* Build a string of the logical expressions.
@@ -129,7 +136,7 @@ public interface IdBinder {
* Read the id value from the result set and set it to the bean also returning
* it.
*/
public Object readSet(DbReadContext ctx, Object bean) throws SQLException;
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException;
/**
* Ignore the appropriate number of scalar properties for this id.
@@ -152,11 +159,6 @@ public interface IdBinder {
*/
public String getBindIdSql(String baseTableAlias);
/**
* Return the id properties in flat form.
*/
public BeanProperty[] getProperties();
/**
* Cast or convert the Id value if necessary and optionally set it.
* <p>
@@ -168,6 +170,6 @@ public interface IdBinder {
* If the bean is not null, then the value is set to the bean.
* </p>
*/
public Object convertSetId(Object idValue, Object bean);
public Object convertSetId(Object idValue, EntityBean bean);
}

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