mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
188
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
637cbc6489 | ||
|
|
5d09ca16d6 | ||
|
|
6ed1f25e22 | ||
|
|
b8ae443074 | ||
|
|
ab9762bbd9 | ||
|
|
2c90b239cd | ||
|
|
13806767a3 | ||
|
|
40e43b9d5f | ||
|
|
29f1e63146 | ||
|
|
5cf008b521 | ||
|
|
3e788d4598 | ||
|
|
8a29454893 | ||
|
|
62c083a6ea | ||
|
|
7f8e4be396 | ||
|
|
2ad39974e8 | ||
|
|
664e95d87b | ||
|
|
5cf668f65b | ||
|
|
31f56621ff | ||
|
|
11a6cd04ea | ||
|
|
fdb573bfe2 | ||
|
|
ae0e1fe691 | ||
|
|
0511e01273 | ||
|
|
f3bd38536c | ||
|
|
139a558165 | ||
|
|
53b40f7ce0 | ||
|
|
e50656871d | ||
|
|
f7ece0f13c | ||
|
|
562c6fafc9 | ||
|
|
63e9f603ec | ||
|
|
0c4fc4eea3 | ||
|
|
2dca1bdf5b | ||
|
|
6403a83e0e | ||
|
|
b57b91ffef | ||
|
|
4ba5702211 | ||
|
|
2e7aeae004 | ||
|
|
3f2676ab49 | ||
|
|
3f29acb25c | ||
|
|
6d1a41f979 | ||
|
|
aee46955a7 | ||
|
|
38a93b9814 | ||
|
|
5f2ad0dc62 | ||
|
|
ece7f481bc | ||
|
|
fa4d0380e5 | ||
|
|
713ba4889d | ||
|
|
0543745e18 | ||
|
|
4e2bc6cce6 | ||
|
|
f2d422ff5a | ||
|
|
176197513d | ||
|
|
1e3035c558 | ||
|
|
17f7143447 | ||
|
|
394dfc96d1 | ||
|
|
452ce78c3d | ||
|
|
4cb13760c2 | ||
|
|
5cc2eff0d4 | ||
|
|
af9a01be77 | ||
|
|
cceb7c035c | ||
|
|
1ddc103582 | ||
|
|
b973cc3b86 | ||
|
|
10f0ea9bae | ||
|
|
c49dd54500 | ||
|
|
bf0944bb2a | ||
|
|
59ba6244c1 | ||
|
|
f88524b34c | ||
|
|
6838b4df4f | ||
|
|
5db7ae504e | ||
|
|
723ae6bffc | ||
|
|
33b173dd55 | ||
|
|
7966d74eb3 | ||
|
|
28cb2e210d | ||
|
|
1109d412e7 | ||
|
|
3e33ece65a | ||
|
|
1dd200fba2 | ||
|
|
f0ea977f91 | ||
|
|
e830d8efc7 | ||
|
|
0b2ba2a82e | ||
|
|
df526b8f3c | ||
|
|
8fe1f32e78 | ||
|
|
c5ea93fb8e | ||
|
|
dd83bc4d26 | ||
|
|
9b4b7a1a01 | ||
|
|
548fa745d6 | ||
|
|
7d83e3f49e | ||
|
|
974284bf8a | ||
|
|
b446f00cd8 | ||
|
|
a4095b4ae5 | ||
|
|
af6dbd0dbe | ||
|
|
db17ddd069 | ||
|
|
b6b85225e7 | ||
|
|
b57abfa732 | ||
|
|
4a4927b1f3 | ||
|
|
6ac34bec97 | ||
|
|
82725e6a98 | ||
|
|
a744c8b375 | ||
|
|
5e2a34de39 | ||
|
|
8a9ab39a01 | ||
|
|
515256abfd | ||
|
|
84e5a6145e | ||
|
|
1f5618d0e3 | ||
|
|
b1de821817 | ||
|
|
f4dceb97b0 | ||
|
|
ad79f175da | ||
|
|
a4b145bc2d | ||
|
|
5491b27528 | ||
|
|
9211c7e6ab | ||
|
|
5bcb6cd116 | ||
|
|
534f2c8b07 | ||
|
|
7ad390934e | ||
|
|
2a651c38e3 | ||
|
|
ddd30f9ea0 | ||
|
|
01d41b7eea | ||
|
|
4cdea33073 | ||
|
|
33d7e10ad8 | ||
|
|
2201f9220e | ||
|
|
b8743a5ef2 | ||
|
|
b587aa8172 | ||
|
|
c37fa83675 | ||
|
|
08602afcff | ||
|
|
5b98d82f58 | ||
|
|
22cd5b0e82 | ||
|
|
93855478dd | ||
|
|
59b86d5b19 | ||
|
|
27afb9ee2e | ||
|
|
a49cd42a0c | ||
|
|
8c8442ec58 | ||
|
|
41f3173adf | ||
|
|
1a4192b40f | ||
|
|
fffda7c789 | ||
|
|
b7f6596bcb | ||
|
|
1ea49dd206 | ||
|
|
32e4f114b4 | ||
|
|
fcbedda597 | ||
|
|
2055349d76 | ||
|
|
44ee64cde5 | ||
|
|
9f916998c9 | ||
|
|
2ddef37632 | ||
|
|
538ebdc899 | ||
|
|
d6e0d661fb | ||
|
|
289deb397a | ||
|
|
68b4aab837 | ||
|
|
563293f7a9 | ||
|
|
5049694fd3 | ||
|
|
a9c8d1960c | ||
|
|
066b409a65 | ||
|
|
54ad2e0ad0 | ||
|
|
77f7bc2a7e | ||
|
|
f773d74597 | ||
|
|
90b23c4985 | ||
|
|
60b4ad410b | ||
|
|
56215aafa3 | ||
|
|
5ba66e4d78 | ||
|
|
0eb7139426 | ||
|
|
d16d92db54 | ||
|
|
dde59db411 | ||
|
|
1a13fc9742 | ||
|
|
e1ec0bcdfe | ||
|
|
27803afe64 | ||
|
|
c011dab3e2 | ||
|
|
fb7abb44e0 | ||
|
|
14034ffaa0 | ||
|
|
75bcbc06e1 | ||
|
|
bc75736780 | ||
|
|
ed0fe7fd57 | ||
|
|
696dabc658 | ||
|
|
89f7d2bace | ||
|
|
4c4e084e43 | ||
|
|
2dccb818a2 | ||
|
|
a7df7da984 | ||
|
|
b012720a5e | ||
|
|
1451ec0084 | ||
|
|
0d2101ca7c | ||
|
|
a22496a57d | ||
|
|
654b10154e | ||
|
|
f4357c2767 | ||
|
|
0d1c9e2883 | ||
|
|
5e2d592c2a | ||
|
|
2344f57f84 | ||
|
|
fc628d336d | ||
|
|
18d9c53ce3 | ||
|
|
811d298355 | ||
|
|
f7a37537db | ||
|
|
0c9d95619b | ||
|
|
96e4b4640f | ||
|
|
a3c8ed88ca | ||
|
|
d8391ba90c | ||
|
|
c4d41ecb00 | ||
|
|
39f5284a76 | ||
|
|
4ec62a5772 | ||
|
|
eb1eafe98c |
@@ -6,4 +6,12 @@
|
||||
target/
|
||||
logs/
|
||||
log/
|
||||
/db
|
||||
/mydb.db
|
||||
|
||||
|
||||
# Intellij project files
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
[](https://waffle.io/ebean-orm/avaje-ebeanorm)
|
||||
avaje-ebeanorm
|
||||
==============
|
||||
|
||||
Main EbeanORM artifact
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<version>4.1.4</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<version>4.1.8</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -48,6 +48,12 @@
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
@@ -77,13 +83,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
@@ -94,7 +93,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>4.1.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -136,9 +135,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,23 +173,21 @@
|
||||
<!-- 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.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>main</id>
|
||||
<phase>process-classes</phase>
|
||||
<configuration>
|
||||
<classSource>target/classes</classSource>
|
||||
<packages>com.avaje.ebean.meta.**</packages>
|
||||
<transformArgs>debug=1</transformArgs>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>enhance</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<!-- Not going to enhance Model bean -->
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>process-test-classes</phase>
|
||||
@@ -214,8 +218,29 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<doctitle>Ebean 4</doctitle>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<phase>site</phase>
|
||||
<goals>
|
||||
<goal>aggregate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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,89 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the entity bean as dirty.
|
||||
* <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.
|
||||
* This is used so that when a bean that is otherwise unmodified is updated with the version
|
||||
* property updated.
|
||||
* <p>
|
||||
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
|
||||
* dirty so that it is not skipped.
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Customer customer = Ebean.find(Customer, id);
|
||||
*
|
||||
* // mark the bean as dirty so that a save() or update() will
|
||||
* // increment the version property
|
||||
* Ebean.markAsDirty(customer);
|
||||
* Ebean.save(customer);
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public static void markAsDirty(Object bean) throws OptimisticLockException {
|
||||
serverMgr.getPrimaryServer().markAsDirty(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* <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("ModifiedNameNoOCC");
|
||||
*
|
||||
* // 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("ModifiedNameNoOCC");
|
||||
* 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 +557,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,129 @@ 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;
|
||||
|
||||
/**
|
||||
* Marks the entity bean as dirty.
|
||||
* <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.
|
||||
* This is used so that when a bean that is otherwise unmodified is updated the version
|
||||
* property is updated.
|
||||
* <p>
|
||||
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
|
||||
* dirty so that it is not skipped.
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Customer customer = ebeanServer.find(Customer, id);
|
||||
*
|
||||
* // mark the bean as dirty so that a save() or update() will
|
||||
* // increment the version property
|
||||
* ebeanServer.markAsDirty(customer);
|
||||
* ebeanServer.save(customer);
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public void markAsDirty(Object bean);
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* <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("ModifiedNameNoOCC");
|
||||
*
|
||||
* // 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("ModifiedNameNoOCC");
|
||||
* 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("ModifiedNameNoOCC");
|
||||
*
|
||||
* // 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 +1123,5 @@ public interface EbeanServer {
|
||||
*/
|
||||
public JsonContext createJsonContext();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import com.avaje.ebean.common.BootupEbeanManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Creates EbeanServer instances.
|
||||
@@ -26,14 +24,9 @@ import org.slf4j.LoggerFactory;
|
||||
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
|
||||
* convenient way of using the 'default/primary' EbeanServer.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Bygrave
|
||||
*
|
||||
*/
|
||||
public class EbeanServerFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EbeanServerFactory.class);
|
||||
|
||||
private static BootupEbeanManager serverFactory = createServerFactory();
|
||||
|
||||
/**
|
||||
@@ -69,27 +62,10 @@ public class EbeanServerFactory {
|
||||
|
||||
private static BootupEbeanManager createServerFactory() {
|
||||
|
||||
// String d___ =
|
||||
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
|
||||
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
|
||||
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
|
||||
String implClassName = System.getProperty("ebean.serverfactory", dflt);
|
||||
|
||||
int delaySecs = GlobalProperties.getInt("ebean.start.delay", 0);
|
||||
if (delaySecs > 0) {
|
||||
try {
|
||||
// perhaps useful to delay the startup to give time to
|
||||
// attach a debugger when running in a server like tomcat.
|
||||
String m = "Ebean sleeping " + delaySecs + " seconds due to ebean.start.delay";
|
||||
logger.info(m);
|
||||
Thread.sleep(delaySecs * 1000);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
String m = "Interrupting debug.start.delay of " + delaySecs;
|
||||
logger.error(m, e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
// use a client side implementation?
|
||||
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,594 @@
|
||||
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 abstract class Model {
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
*
|
||||
* <p>
|
||||
* Example:
|
||||
* <pre class="code">
|
||||
* Transaction transaction = Customer.db().beginTransaction();
|
||||
* try {
|
||||
*
|
||||
* // turn off cascade persist for this transaction
|
||||
* transaction.setPersistCascade(false);
|
||||
*
|
||||
* // extra control over jdbc batching for this transaction
|
||||
* transaction.setBatchGetGeneratedKeys(false);
|
||||
* transaction.setBatchMode(true);
|
||||
* transaction.setBatchSize(20);
|
||||
*
|
||||
* Customer customer = new Customer();
|
||||
* customer.setName("Roberto");
|
||||
* customer.save();
|
||||
*
|
||||
* Customer otherCustomer = new Customer();
|
||||
* otherCustomer.setName("Franko");
|
||||
* otherCustomer.save();
|
||||
*
|
||||
* transaction.commit();
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public static 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 static EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the entity bean as dirty.
|
||||
* <p>
|
||||
* This is used so that when a bean that is otherwise unmodified is updated the version
|
||||
* property is updated.
|
||||
* <p>
|
||||
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
|
||||
* dirty so that it is not skipped.
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Customer customer = Customer.find.byId(id);
|
||||
*
|
||||
* // mark the bean as dirty so that a save() or update() will
|
||||
* // increment the version property
|
||||
* customer.markAsDirty();
|
||||
* customer.save();
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -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<Order> listener = ...;
|
||||
*
|
||||
* Query<Order> 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<Order> 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<Order> listener = ...;
|
||||
*
|
||||
* Query<Order> 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);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -135,18 +136,33 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* Note that lazy loading also works with object graphs built with RawSql.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
*/
|
||||
public final class RawSql implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final ResultSet resultSet;
|
||||
|
||||
private final Sql sql;
|
||||
|
||||
private final ColumnMapping columnMapping;
|
||||
|
||||
protected RawSql(Sql sql, ColumnMapping columnMapping) {
|
||||
/**
|
||||
* Construct with a ResultSet and properties that the columns map to.
|
||||
* <p>
|
||||
* The properties listed in the propertyNames must be in the same order as the columns in the
|
||||
* resultSet.
|
||||
* <p>
|
||||
* When a query executes this RawSql object then it will close the resultSet.
|
||||
*/
|
||||
public RawSql(ResultSet resultSet, String... propertyNames) {
|
||||
this.resultSet = resultSet;
|
||||
this.sql = null;
|
||||
this.columnMapping = new ColumnMapping(propertyNames);
|
||||
}
|
||||
|
||||
protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
|
||||
this.resultSet = resultSet;
|
||||
this.sql = sql;
|
||||
this.columnMapping = columnMapping;
|
||||
}
|
||||
@@ -158,6 +174,14 @@ public final class RawSql implements Serializable {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the resultSet if this is a ResultSet based RawSql.
|
||||
*/
|
||||
public ResultSet getResultSet() {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column mapping for the SQL columns to bean properties.
|
||||
*/
|
||||
@@ -169,6 +193,9 @@ public final class RawSql implements Serializable {
|
||||
* Return the hash for this query.
|
||||
*/
|
||||
public int queryHash() {
|
||||
if (resultSet != null) {
|
||||
return 31 * columnMapping.queryHash();
|
||||
}
|
||||
return 31 * sql.queryHash() + columnMapping.queryHash();
|
||||
}
|
||||
|
||||
@@ -329,6 +356,7 @@ public final class RawSql implements Serializable {
|
||||
private final LinkedHashMap<String, Column> dbColumnMap;
|
||||
|
||||
private final Map<String, String> propertyMap;
|
||||
|
||||
private final Map<String, Column> propertyColumnMap;
|
||||
|
||||
private final boolean parsed;
|
||||
@@ -364,6 +392,26 @@ public final class RawSql implements Serializable {
|
||||
this.propertyColumnMap = null;
|
||||
this.dbColumnMap = new LinkedHashMap<String, Column>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for ResultSet use.
|
||||
*/
|
||||
protected ColumnMapping(String... propertyNames) {
|
||||
this.immutable = false;
|
||||
this.parsed = false;
|
||||
this.propertyMap = null;
|
||||
//this.propertyColumnMap = null;
|
||||
this.dbColumnMap = new LinkedHashMap<String, Column>();
|
||||
|
||||
int hc = 31;
|
||||
int pos = 0;
|
||||
for (String prop : propertyNames) {
|
||||
hc = 31 * hc + prop.hashCode();
|
||||
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
|
||||
}
|
||||
propertyColumnMap = dbColumnMap;
|
||||
this.queryHashCode = hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an immutable ColumnMapping based on collected information.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
import com.avaje.ebean.RawSql.ColumnMapping;
|
||||
import com.avaje.ebean.RawSql.Sql;
|
||||
|
||||
@@ -10,8 +12,6 @@ import com.avaje.ebean.RawSql.Sql;
|
||||
* named query.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @see RawSql
|
||||
*/
|
||||
public class RawSqlBuilder {
|
||||
@@ -21,10 +21,23 @@ public class RawSqlBuilder {
|
||||
*/
|
||||
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
|
||||
|
||||
private final ResultSet resultSet;
|
||||
|
||||
private final Sql sql;
|
||||
|
||||
private final ColumnMapping columnMapping;
|
||||
|
||||
/**
|
||||
* Create and return a RawSql object based on the resultSet and list of properties the columns in
|
||||
* the resultSet map to.
|
||||
* <p>
|
||||
* The properties listed in the propertyNames must be in the same order as the columns in the
|
||||
* resultSet.
|
||||
*/
|
||||
public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
|
||||
return new RawSql(resultSet, propertyNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an unparsed RawSqlBuilder. Unlike a parsed one this query can not be
|
||||
* modified - so no additional WHERE or HAVING expressions can be added to
|
||||
@@ -58,9 +71,17 @@ public class RawSqlBuilder {
|
||||
return new RawSqlBuilder(sql2, mapping);
|
||||
}
|
||||
|
||||
|
||||
private RawSqlBuilder(ResultSet resultSet, ColumnMapping columnMapping) {
|
||||
this.resultSet = resultSet;
|
||||
this.columnMapping = columnMapping;
|
||||
this.sql = null;
|
||||
}
|
||||
|
||||
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
|
||||
this.sql = sql;
|
||||
this.columnMapping = columnMapping;
|
||||
this.resultSet = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +113,7 @@ public class RawSqlBuilder {
|
||||
* has been defined.
|
||||
*/
|
||||
public RawSql create() {
|
||||
return new RawSql(sql, columnMapping.createImmutableCopy());
|
||||
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,4 +122,6 @@ public class RawSqlBuilder {
|
||||
protected Sql getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ColumnHstore {
|
||||
|
||||
}
|
||||
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
|
||||
/**
|
||||
* Use a version column.
|
||||
*/
|
||||
VERSION,
|
||||
|
||||
/**
|
||||
* Use all the columns (except Lobs).
|
||||
*/
|
||||
ALL
|
||||
VERSION
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* An annotation for declaring an index on a single column.
|
||||
*
|
||||
* @author rvbiljouw
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Index {
|
||||
|
||||
/**
|
||||
* Name of the index
|
||||
*
|
||||
* @return index name
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -55,21 +55,6 @@ import com.avaje.ebean.TxType;
|
||||
* Ebean.save(order);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* During development and testing you can set a debug level which will log the
|
||||
* transaction begin, commit and rollback events so that you can easily confirm
|
||||
* it is behaving as you would expect.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* ## in ebean.properties file
|
||||
*
|
||||
* ## Log transaction begins and ends etc
|
||||
* ## (0=NoLogging 1=minimal ... 9=logAll)
|
||||
* ebean.debug.transaction=3
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -158,34 +137,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
public Collection<?> getActualEntries();
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
* Can be used by client code that is paging through results using
|
||||
* setFirstRow() setMaxRows(). If this returns true then the client can
|
||||
* display a 'next' button etc.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasMoreRows();
|
||||
|
||||
/**
|
||||
* Set to true when maxRows is hit but there are actually more rows available.
|
||||
* This is set so that client code knows that there is more data available.
|
||||
*/
|
||||
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,31 +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.
|
||||
*/
|
||||
protected boolean hasMoreRows;
|
||||
|
||||
protected ModifyHolder<E> modifyHolder;
|
||||
|
||||
protected ModifyListenMode modifyListenMode;
|
||||
@@ -70,6 +52,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 +69,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 +112,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);
|
||||
@@ -154,66 +145,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
* Can be used by client code that is paging through results using
|
||||
* setFirstRow() setMaxRows(). If this returns true then the client can
|
||||
* display a 'next' button etc.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasMoreRows() {
|
||||
return hasMoreRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true when maxRows is hit but there are actually more rows available.
|
||||
* This is set so that client code knows that there is more data available.
|
||||
*/
|
||||
public void setHasMoreRows(boolean hasMoreRows) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +153,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
sb.append("BeanList ");
|
||||
if (isReadOnly()) {
|
||||
sb.append("readOnly ");
|
||||
@@ -148,7 +163,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(list.size()).append("] ");
|
||||
sb.append("hasMoreRows[").append(hasMoreRows).append("] ");
|
||||
sb.append("list").append(list).append("");
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -257,7 +271,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +154,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
sb.append("BeanMap ");
|
||||
if (isReadOnly()) {
|
||||
sb.append("readOnly ");
|
||||
@@ -149,7 +164,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(map.size()).append("]");
|
||||
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
|
||||
sb.append(" map").append(map);
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -209,7 +223,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +143,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
sb.append("BeanSet ");
|
||||
if (isReadOnly()) {
|
||||
sb.append("readOnly ");
|
||||
@@ -138,7 +153,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(set.size()).append("]");
|
||||
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
|
||||
sb.append(" set").append(set);
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -217,7 +231,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ public class DataSourceConfig {
|
||||
|
||||
private int isolationLevel = Transaction.READ_COMMITTED;
|
||||
|
||||
private boolean autoCommit;
|
||||
|
||||
private String heartbeatSql;
|
||||
|
||||
private int heartbeatFreqSecs = 30;
|
||||
@@ -131,6 +133,20 @@ public class DataSourceConfig {
|
||||
public void setIsolationLevel(int isolationLevel) {
|
||||
this.isolationLevel = isolationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return autoCommit setting.
|
||||
*/
|
||||
public boolean isAutoCommit() {
|
||||
return autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to turn on autoCommit.
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) {
|
||||
this.autoCommit = autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum number of connections the pool should maintain.
|
||||
@@ -450,6 +466,7 @@ public class DataSourceConfig {
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
|
||||
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
|
||||
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
|
||||
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
|
||||
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A map like structure of properties.
|
||||
@@ -68,9 +67,8 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
|
||||
synchronized void putAll(Map<String, String> keyValueMap) {
|
||||
Iterator<Entry<String, String>> it = keyValueMap.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, String> entry = it.next();
|
||||
|
||||
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Helper used to load the PropertyMap.
|
||||
*/
|
||||
@@ -81,9 +79,7 @@ final class PropertyMapLoader {
|
||||
}
|
||||
|
||||
// put values in initially without any evaluation
|
||||
Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<Object, Object> entry = it.next();
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String key = ((String) entry.getKey()).toLowerCase();
|
||||
String val = ((String) entry.getValue());
|
||||
if (val != null) {
|
||||
|
||||
@@ -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,58 @@ 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. */
|
||||
/**
|
||||
* Set to true if the DataSource uses autoCommit.
|
||||
* <p>
|
||||
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
private boolean autoCommitMode;
|
||||
|
||||
/**
|
||||
* The data source JNDI name if using a JNDI DataSource.
|
||||
*/
|
||||
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<?>>();
|
||||
@@ -544,6 +589,20 @@ public class ServerConfig {
|
||||
public void setDataSourceJndiName(String dataSourceJndiName) {
|
||||
this.dataSourceJndiName = dataSourceJndiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
public boolean isAutoCommitMode() {
|
||||
return autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
public void setAutoCommitMode(boolean autoCommitMode) {
|
||||
this.autoCommitMode = autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value used to represent TRUE in the database.
|
||||
@@ -764,6 +823,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 +1005,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.
|
||||
*/
|
||||
@@ -1213,6 +1302,7 @@ public class ServerConfig {
|
||||
|
||||
loadDataSourceSettings(p);
|
||||
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", false);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
|
||||
namingConvention = createNamingConvention(p);
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
|
||||
@@ -1235,8 +1325,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 +1342,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);
|
||||
|
||||
@@ -57,6 +57,13 @@ public final class TableName {
|
||||
this.name = split[len - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a qualifiedTableName that might include a catalog and schema and just return the table name.
|
||||
*/
|
||||
public static String parse(String qualifiedTableName) {
|
||||
return new TableName(qualifiedTableName).getName();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getQualifiedName();
|
||||
}
|
||||
@@ -121,6 +128,18 @@ public final class TableName {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a catalog and schema prefix if they exist to the string builder.
|
||||
*/
|
||||
public void appendCatalogAndSchema(StringBuilder buffer) {
|
||||
if (catalog != null) {
|
||||
buffer.append(catalog).append(".");
|
||||
}
|
||||
if (schema != null) {
|
||||
buffer.append(schema).append(".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is table name is valid i.e. it has at least a name.
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@ import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -14,40 +15,61 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class DatabasePlatform {
|
||||
|
||||
/** The Constant logger. */
|
||||
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
|
||||
|
||||
/** The open quote used by quoted identifiers. */
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
*/
|
||||
protected String openQuote = "\"";
|
||||
|
||||
/** The close quote used by quoted identifiers. */
|
||||
/**
|
||||
* The close quote used by quoted identifiers.
|
||||
*/
|
||||
protected String closeQuote = "\"";
|
||||
|
||||
/** For limit/offset, row_number etc limiting of SQL queries. */
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
*/
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/** Mapping of JDBC to Database types. */
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
*/
|
||||
protected DbTypeMap dbTypeMap = new DbTypeMap();
|
||||
|
||||
/** DB specific DDL syntax. */
|
||||
/**
|
||||
* DB specific DDL syntax.
|
||||
*/
|
||||
protected DbDdlSyntax dbDdlSyntax = new DbDdlSyntax();
|
||||
|
||||
/** Defines DB identity/sequence features. */
|
||||
/**
|
||||
* Defines DB identity/sequence features.
|
||||
*/
|
||||
protected DbIdentity dbIdentity = new DbIdentity();
|
||||
|
||||
/** The JDBC type to map booleans to (by default). */
|
||||
/**
|
||||
* The JDBC type to map booleans to (by default).
|
||||
*/
|
||||
protected int booleanDbType = Types.BOOLEAN;
|
||||
|
||||
/** The JDBC type to map Blob to. */
|
||||
/**
|
||||
* The JDBC type to map Blob to.
|
||||
*/
|
||||
protected int blobDbType = Types.BLOB;
|
||||
|
||||
/** The JDBC type to map Clob to. */
|
||||
/**
|
||||
* The JDBC type to map Clob to.
|
||||
*/
|
||||
protected int clobDbType = Types.CLOB;
|
||||
|
||||
/** For Oracle treat empty strings as null. */
|
||||
/**
|
||||
* For Oracle treat empty strings as null.
|
||||
*/
|
||||
protected boolean treatEmptyStringsAsNull;
|
||||
|
||||
/** The name. */
|
||||
/**
|
||||
* The database platform name.
|
||||
*/
|
||||
protected String name = "generic";
|
||||
|
||||
/**
|
||||
@@ -57,6 +79,11 @@ public class DatabasePlatform {
|
||||
*/
|
||||
private static final char BACK_TICK = '`';
|
||||
|
||||
/**
|
||||
* The like clause. Can be overridden to disable default escape character.
|
||||
*/
|
||||
protected String likeClause = "like ?";
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
protected boolean idInExpandedForm;
|
||||
@@ -299,4 +326,13 @@ public class DatabasePlatform {
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the like clause used by this database platform.
|
||||
* <p>
|
||||
* This may include an escape clause to disable a default escape character.
|
||||
*/
|
||||
public String getLikeClause() {
|
||||
return likeClause;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.config.TableName;
|
||||
|
||||
/**
|
||||
* Used to support DB specific syntax for DDL generation.
|
||||
*/
|
||||
@@ -9,7 +11,7 @@ public class DbDdlSyntax {
|
||||
|
||||
private boolean inlinePrimaryKeyConstraint = false;
|
||||
|
||||
private boolean addOneToOneUniqueContraint = false;
|
||||
private boolean addOneToOneUniqueContraint = true;
|
||||
|
||||
private int maxConstraintNameLength = 32;
|
||||
|
||||
@@ -43,6 +45,17 @@ public class DbDdlSyntax {
|
||||
return pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column definition for an identity column.
|
||||
*/
|
||||
public String getIdentityColumnDefn(String columnDefn) {
|
||||
String identity = getIdentity();
|
||||
if (identity != null && identity.length() > 0) {
|
||||
return columnDefn+" "+identity;
|
||||
}
|
||||
return columnDefn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identity clause for DB's that have identities.
|
||||
*/
|
||||
@@ -248,26 +261,29 @@ public class DbDdlSyntax {
|
||||
this.inlinePrimaryKeyConstraint = inlinePrimaryKeyConstraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a fully index name.
|
||||
*/
|
||||
public String getIndexName(String table, String propName, int ixCount) {
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("ix_");
|
||||
buffer.append(table);
|
||||
buffer.append("_");
|
||||
buffer.append(propName);
|
||||
|
||||
StringBuilder buffer = new StringBuilder(30);
|
||||
buffer.append("ix_").append(TableName.parse(table));
|
||||
buffer.append("_").append(propName);
|
||||
|
||||
addSuffix(buffer, ixCount);
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a fully qualified foreign key constraint name.
|
||||
*/
|
||||
public String getForeignKeyName(String table, String propName, int fkCount) {
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("fk_");
|
||||
buffer.append(table);
|
||||
buffer.append("_");
|
||||
buffer.append(propName);
|
||||
StringBuilder buffer = new StringBuilder(30);
|
||||
buffer.append("fk_").append(TableName.parse(table));
|
||||
buffer.append("_").append(propName);
|
||||
|
||||
addSuffix(buffer, fkCount);
|
||||
|
||||
@@ -276,15 +292,10 @@ public class DbDdlSyntax {
|
||||
|
||||
/**
|
||||
* Adds the suffix.
|
||||
*
|
||||
* @param buffer
|
||||
* the buffer
|
||||
* @param count
|
||||
* the count
|
||||
*/
|
||||
protected void addSuffix(StringBuilder buffer, int count) {
|
||||
final String suffixNr = Integer.toString(count);
|
||||
final int suffixLen = suffixNr.length() + 1;
|
||||
String suffixNr = Integer.toString(count);
|
||||
int suffixLen = suffixNr.length() + 1;
|
||||
|
||||
if (buffer.length() + suffixLen > maxConstraintNameLength) {
|
||||
buffer.setLength(maxConstraintNameLength - suffixLen);
|
||||
|
||||
@@ -14,6 +14,7 @@ public class H2Platform extends DatabasePlatform {
|
||||
super();
|
||||
this.name = "h2";
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.likeClause = "like ? escape''";
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
|
||||
@@ -17,22 +17,21 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
|
||||
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
sb.append("select ");
|
||||
if (request.isDistinct()) {
|
||||
sb.append("distinct ");
|
||||
}
|
||||
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
maxRows = maxRows + 1;
|
||||
}
|
||||
|
||||
if (maxRows > 0 || firstRow > 0) {
|
||||
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ").append(maxRows);
|
||||
sb.append(" ").append(LIMIT).append(" ").append(maxRows);
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
|
||||
@@ -26,9 +26,7 @@ public class MsSqlServer2005SqlLimiter implements SqlLimiter {
|
||||
|
||||
int lastRow = request.getMaxRows();
|
||||
if (lastRow > 0) {
|
||||
// fetch 1 more than we return so that
|
||||
// we know if more rows are available
|
||||
lastRow = lastRow + firstRow + 1;
|
||||
lastRow = lastRow + firstRow;
|
||||
}
|
||||
|
||||
if (firstRow < 1) {
|
||||
|
||||
@@ -20,6 +20,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
public MySqlPlatform() {
|
||||
super();
|
||||
this.name = "mysql";
|
||||
this.likeClause = "like ? escape''";
|
||||
this.selectCountWithAlias = true;
|
||||
this.dbEncrypt = new MySqlDbEncrypt();
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Postgres v8.3 specific platform.
|
||||
* <p>
|
||||
* No support for getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
public class Postgres8Platform extends DatabasePlatform {
|
||||
|
||||
public Postgres8Platform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
|
||||
if (colAlias == null) {
|
||||
// Postgres requires the "as" keyword for column alias
|
||||
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
|
||||
}
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
|
||||
|
||||
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
|
||||
|
||||
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.CLOB, new DbType("text"));
|
||||
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
|
||||
|
||||
dbDdlSyntax.setDropTableCascade("cascade");
|
||||
dbDdlSyntax.setDropIfExists("if exists");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Postgres specific sequence IdGenerator.
|
||||
*/
|
||||
@Override
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
public class PostgresDdlSyntax extends DbDdlSyntax {
|
||||
|
||||
/**
|
||||
* Map bigint, integer and smallint into their equivilent serial types.
|
||||
*/
|
||||
@Override
|
||||
public String getIdentityColumnDefn(String columnDefn) {
|
||||
|
||||
//smallserial, serial and bigserial
|
||||
if ("bigint".equalsIgnoreCase(columnDefn)) {
|
||||
return "bigserial";
|
||||
}
|
||||
if ("integer".equalsIgnoreCase(columnDefn)) {
|
||||
return "serial";
|
||||
}
|
||||
if ("smallint".equalsIgnoreCase(columnDefn)) {
|
||||
return "smallserial";
|
||||
}
|
||||
|
||||
return columnDefn;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,27 +4,38 @@ import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Postgres v8.3 specific platform.
|
||||
* Postgres v9 specific platform.
|
||||
* <p>
|
||||
* No support for getGeneratedKeys.
|
||||
* Uses serial types and getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
public class PostgresPlatform extends DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Unique jdbc type id defined for hstore type.
|
||||
*/
|
||||
public static final int TYPE_HSTORE = 4001;
|
||||
|
||||
public PostgresPlatform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
this.likeClause = "like ? escape''";
|
||||
|
||||
this.dbDdlSyntax = new PostgresDdlSyntax();
|
||||
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
// Use Identity and getGeneratedKeys
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
|
||||
@@ -36,8 +47,8 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
|
||||
|
||||
dbTypeMap.put(TYPE_HSTORE, new DbType("hstore"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
|
||||
@@ -15,7 +15,7 @@ public class RowNumberSqlLimiter implements SqlLimiter {
|
||||
*/
|
||||
private static final String ROW_NUMBER_AS = ") as rn, ";
|
||||
|
||||
final String rowNumberWindowAlias;
|
||||
private final String rowNumberWindowAlias;
|
||||
|
||||
/**
|
||||
* Specify the name of the rowNumberWindowAlias.
|
||||
@@ -30,16 +30,18 @@ public class RowNumberSqlLimiter implements SqlLimiter {
|
||||
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(60 + dbSql.length());
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
|
||||
int lastRow = request.getMaxRows();
|
||||
if (lastRow > 0) {
|
||||
lastRow = lastRow + firstRow + 1;
|
||||
lastRow = lastRow + firstRow;
|
||||
}
|
||||
|
||||
sb.append("select * from (").append(NEW_LINE);
|
||||
sb.append("select * from ( ");
|
||||
|
||||
sb.append("select ");
|
||||
if (request.isDistinct()) {
|
||||
@@ -50,9 +52,9 @@ public class RowNumberSqlLimiter implements SqlLimiter {
|
||||
sb.append(request.getDbOrderBy());
|
||||
sb.append(ROW_NUMBER_AS);
|
||||
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
sb.append(NEW_LINE).append(") ");
|
||||
sb.append(" ) ");
|
||||
sb.append(rowNumberWindowAlias);
|
||||
sb.append(" where ");
|
||||
if (firstRow > 0) {
|
||||
|
||||
@@ -35,38 +35,40 @@ public class RownumSqlLimiter implements SqlLimiter {
|
||||
// :MAX_ROW_TO_FETCH )
|
||||
// where rnum >= :MIN_ROW_TO_FETCH;
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(60 + dbSql.length());
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
|
||||
int lastRow = request.getMaxRows();
|
||||
if (lastRow > 0) {
|
||||
lastRow = lastRow + firstRow + 1;
|
||||
lastRow = lastRow + firstRow;
|
||||
}
|
||||
|
||||
sb.append("select * ").append(NEW_LINE).append("from ( ");
|
||||
sb.append("select * from ( ");
|
||||
|
||||
sb.append("select ");
|
||||
if (useFirstRowsHint && request.getMaxRows() > 0) {
|
||||
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows() + 1).append(") */ ");
|
||||
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
|
||||
}
|
||||
|
||||
sb.append("rownum ").append(rnum).append(", a.* ").append(NEW_LINE);
|
||||
sb.append(" from (");//
|
||||
sb.append("rownum ").append(rnum).append(", a.* ");
|
||||
sb.append(" from (");
|
||||
|
||||
sb.append(" select ");
|
||||
if (request.isDistinct()) {
|
||||
sb.append("distinct ");
|
||||
}
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
sb.append(NEW_LINE).append(" ) a ");
|
||||
sb.append(NEW_LINE).append(" ) a ");
|
||||
if (lastRow > 0) {
|
||||
sb.append(NEW_LINE).append(" where rownum <= ").append(lastRow);
|
||||
sb.append(" where rownum <= ").append(lastRow);
|
||||
}
|
||||
sb.append(NEW_LINE).append(" ) ");
|
||||
sb.append(" ) ");
|
||||
if (firstRow > 0) {
|
||||
sb.append(NEW_LINE).append("where ");
|
||||
sb.append(" where ");
|
||||
sb.append(rnum).append(" > ").append(firstRow);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,12 @@ public class SqlAnywhereLimiter implements SqlLimiter {
|
||||
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(60 + dbSql.length());
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
// fetch 1 more than we return so that
|
||||
// we know if more rows are available
|
||||
maxRows = maxRows + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SELECT TOP xx START AT xx ... FROM ...
|
||||
@@ -31,7 +28,7 @@ public class SqlAnywhereLimiter implements SqlLimiter {
|
||||
if (firstRow > 0) {
|
||||
sb.append("start at ").append(firstRow + 1).append(" ");
|
||||
}
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import com.avaje.ebean.Query;
|
||||
/**
|
||||
* The request object for the query that can have sql limiting applied to it
|
||||
* (such as a LIMIT OFFSET clause).
|
||||
*
|
||||
* @author rob
|
||||
*/
|
||||
public interface SqlLimitRequest {
|
||||
|
||||
|
||||
@@ -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("anniversary", "dd-MMM-yyyy");
|
||||
* csvReader.addProperty("billingAddress.line1");
|
||||
* csvReader.addProperty("billingAddress.city");
|
||||
* csvReader.addReference("billingAddress.country.code");
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
@@ -31,13 +53,15 @@ public class ManyWhereJoins implements Serializable {
|
||||
String join = elProp.getElPrefix();
|
||||
BeanProperty p = elProp.getBeanProperty();
|
||||
if (p instanceof BeanPropertyAssocMany<?>){
|
||||
join = addManyToJoin(join, p.getName());
|
||||
join = addManyToJoin(join, p.getName());
|
||||
}
|
||||
if (join != null){
|
||||
joins.add(join);
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
joins.add(join+"."+secondaryTableJoinPrefix);
|
||||
addJoin(join);
|
||||
if (p != null) {
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
addJoin(join+"."+secondaryTableJoinPrefix);
|
||||
}
|
||||
}
|
||||
addParentJoins(join);
|
||||
}
|
||||
@@ -58,11 +82,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 +102,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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.ExpressionFactory;
|
||||
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
|
||||
|
||||
public interface SpiExpressionFactory extends ExpressionFactory {
|
||||
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
|
||||
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
public ExpressionFactory createExpressionFactory();
|
||||
|
||||
}
|
||||
|
||||
@@ -44,9 +44,14 @@ public interface SpiExpressionRequest {
|
||||
* Return the ordered list of bind values for all expressions in this request.
|
||||
*/
|
||||
public ArrayList<Object> getBindValues();
|
||||
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
*/
|
||||
public int nextParameter();
|
||||
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
*/
|
||||
public int nextParameter();
|
||||
|
||||
/**
|
||||
* Append a DB Like clause.
|
||||
*/
|
||||
public void appendLike();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -524,12 +532,24 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* Return the index of the first row to return in the query.
|
||||
*/
|
||||
public int getFirstRow();
|
||||
|
||||
/**
|
||||
* Internally set by Ebean when this query must use the DISTINCT keyword.
|
||||
* <p>
|
||||
* This does not exclude/remove the use of the id property.
|
||||
*/
|
||||
public Query<T> setSqlDistinct(boolean sqlDistinct);
|
||||
|
||||
/**
|
||||
* return true if this query uses DISTINCT.
|
||||
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
|
||||
*/
|
||||
public boolean isDistinctQuery();
|
||||
|
||||
/**
|
||||
* Return true if this query has been specified by a user to use DISTINCT.
|
||||
*/
|
||||
public boolean isDistinct();
|
||||
|
||||
|
||||
/**
|
||||
* Set default select clauses where none have been explicitly defined.
|
||||
*/
|
||||
@@ -545,19 +565,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.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,11 @@ import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
*/
|
||||
public interface SpiTransaction extends Transaction {
|
||||
|
||||
/**
|
||||
* End the transaction when had query only use.
|
||||
*/
|
||||
public void endQueryOnly();
|
||||
|
||||
/**
|
||||
* Return the string prefix with the transactin id and label used in logging.
|
||||
*/
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+59
-85
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+35
-100
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+32
-37
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheTuning;
|
||||
@@ -89,9 +88,7 @@ public class DefaultCacheHolder {
|
||||
}
|
||||
|
||||
public void clearAll() {
|
||||
Iterator<ServerCache> it = concMap.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ServerCache serverCache = it.next();
|
||||
for (ServerCache serverCache : concMap.values()) {
|
||||
serverCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,9 +111,7 @@ public class DefaultServerCache implements ServerCache {
|
||||
|
||||
int hc = reset ? removedHitCount.getAndSet(0) : removedHitCount.get();
|
||||
|
||||
Iterator<CacheEntry> it = map.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
CacheEntry cacheEntry = it.next();
|
||||
for (CacheEntry cacheEntry : map.values()) {
|
||||
hc += cacheEntry.getHitCount(reset);
|
||||
}
|
||||
|
||||
@@ -268,10 +266,6 @@ public class DefaultServerCache implements ServerCache {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
private Iterator<CacheEntry> cacheEntries() {
|
||||
return map.values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* The task used to periodically trim the cache.
|
||||
*/
|
||||
@@ -296,7 +290,7 @@ public class DefaultServerCache implements ServerCache {
|
||||
long idleExpire = System.currentTimeMillis() - (maxIdleSecs*1000);
|
||||
long ttlExpire = System.currentTimeMillis() - (maxSecsToLive*1000);
|
||||
|
||||
Iterator<CacheEntry> it = cacheEntries();
|
||||
Iterator<CacheEntry> it = map.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
CacheEntry cacheEntry = it.next();
|
||||
if (maxIdleSecs > 0 && idleExpire > cacheEntry.getLastAccessTime()) {
|
||||
|
||||
@@ -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,16 @@ 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;
|
||||
|
||||
boolean readOnly;
|
||||
protected boolean createdTransaction;
|
||||
|
||||
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
@@ -62,29 +60,19 @@ public abstract class BeanRequest {
|
||||
if (transaction == null || !transaction.isActive()) {
|
||||
// create an implicit transaction to execute this query
|
||||
transaction = ebeanServer.createServerTransaction(false, -1);
|
||||
// commented out for performance reasons...
|
||||
// TODO: review performance of trans.setReadOnly(true)
|
||||
//if (readOnlyTransaction) {
|
||||
// readOnly = true;
|
||||
// transaction.setReadOnly(true);
|
||||
//}
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit this transaction if it was created for this request.
|
||||
*/
|
||||
public void commitTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
if (readOnly) {
|
||||
transaction.rollback();
|
||||
} else {
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Commit this transaction if it was created for this request.
|
||||
*/
|
||||
public void commitTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the transaction if it was created for this request.
|
||||
@@ -128,17 +116,17 @@ public abstract class BeanRequest {
|
||||
return transaction.getInternalConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if SQL should be logged for this transaction.
|
||||
*/
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if SUMMARY information should be logged for this transaction.
|
||||
*/
|
||||
public boolean isLogSummary() {
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
/**
|
||||
* Return true if SQL should be logged for this transaction.
|
||||
*/
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if SUMMARY information should be logged for this transaction.
|
||||
*/
|
||||
public boolean isLogSummary() {
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
@@ -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>();
|
||||
|
||||
@@ -65,7 +65,9 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
|
||||
public BootupClasses(List<Class<?>> list) {
|
||||
if (list != null) {
|
||||
process(list.iterator());
|
||||
for (Class<?> cls : list) {
|
||||
isMatch(cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +85,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
|
||||
}
|
||||
|
||||
private void process(Iterator<Class<?>> it) {
|
||||
while (it.hasNext()) {
|
||||
Class<?> cls = it.next();
|
||||
isMatch(cls);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this object so that classes can be added to it.
|
||||
*/
|
||||
@@ -297,13 +292,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
return beanListenerList;
|
||||
}
|
||||
|
||||
public void add(Iterator<Class<?>> it) {
|
||||
while (it.hasNext()) {
|
||||
Class<?> clazz = it.next();
|
||||
isMatch(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMatch(Class<?> cls) {
|
||||
|
||||
if (isEmbeddable(cls)) {
|
||||
@@ -311,7 +299,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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,16 +53,16 @@ public class DatabasePlatformFactory {
|
||||
private DatabasePlatform byDatabaseName(String dbName) throws SQLException {
|
||||
|
||||
dbName = dbName.toLowerCase();
|
||||
if (dbName.equals("postgres83")) {
|
||||
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
|
||||
return new PostgresPlatform();
|
||||
}
|
||||
if (dbName.equals("postgres8") || dbName.equals("postgres83")) {
|
||||
return new Postgres8Platform();
|
||||
}
|
||||
if (dbName.equals("oracle9")) {
|
||||
return new Oracle9Platform();
|
||||
}
|
||||
if (dbName.equals("oracle10")) {
|
||||
return new Oracle10Platform();
|
||||
}
|
||||
if (dbName.equals("oracle")) {
|
||||
if (dbName.equals("oracle") || dbName.equals("oracle10")) {
|
||||
return new Oracle10Platform();
|
||||
}
|
||||
if (dbName.equals("sqlserver2005")) {
|
||||
|
||||
@@ -23,6 +23,8 @@ 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.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
/**
|
||||
@@ -73,7 +75,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 +94,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);
|
||||
}
|
||||
@@ -107,7 +109,19 @@ public class DefaultBeanLoader {
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
query.orderBy(orderBy);
|
||||
}
|
||||
|
||||
String extraWhere = many.getExtraWhere();
|
||||
if (extraWhere != null) {
|
||||
// replace special ${ta} placeholder with the base table alias
|
||||
// which is always t0 and add the extra where clause
|
||||
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
|
||||
query.where().raw(ew);
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(idList, many);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
@@ -136,14 +150,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 +168,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 +196,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 +255,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 +284,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 +307,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 +329,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 +376,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 +390,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,14 +406,22 @@ 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);
|
||||
}
|
||||
|
||||
if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) {
|
||||
// explicitly state to load all properties on REFRESH.
|
||||
// Lobs default to fetch lazy so this forces lobs to be
|
||||
// included in a 'refresh' query
|
||||
query.select("*");
|
||||
}
|
||||
|
||||
Object dbBean = query.findUnique();
|
||||
if (dbBean == null) {
|
||||
|
||||
@@ -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)) {
|
||||
@@ -1126,14 +1112,14 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
// predicates on *ToMany properties
|
||||
if (query.initManyWhereJoins()) {
|
||||
// we need a sql distinct now
|
||||
query.setDistinct(true);
|
||||
query.setSqlDistinct(true);
|
||||
}
|
||||
|
||||
boolean allowOneManyFetch = true;
|
||||
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")
|
||||
@@ -1228,18 +1198,12 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(desc, spiQuery, t);
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (T) request.findId();
|
||||
|
||||
T bean = (T) request.findId();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return bean;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1255,17 +1219,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 +1229,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);
|
||||
}
|
||||
@@ -1293,15 +1250,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
Set<T> set = (Set<T>) request.findSet();
|
||||
return (Set<T>) request.findSet();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return set;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
// String stackTrace = throwablePrinter.print(ex);
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1317,15 +1269,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
Map<?, T> map = (Map<?, T>) request.findMap();
|
||||
return (Map<?, T>) request.findMap();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return map;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
// String stackTrace = throwablePrinter.print(ex);
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1340,14 +1287,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ROWCOUNT, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
int rowCount = request.findRowCount();
|
||||
return request.findRowCount();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return rowCount;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1363,14 +1306,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ID_LIST, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<Object> list = request.findIds();
|
||||
return request.findIds();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return list;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1458,6 +1397,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);
|
||||
@@ -1465,10 +1410,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.findVisit(visitor);
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
} finally {
|
||||
// do nothing - findVisit garuntee's cleanup of the transaction if required
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,10 +1422,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findIterate();
|
||||
// request.endTransIfRequired();
|
||||
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -1499,14 +1441,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<T> list = request.findList();
|
||||
return request.findList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return list;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1549,14 +1487,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<SqlRow> list = request.findList();
|
||||
return request.findList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return list;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1566,14 +1500,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
Set<SqlRow> set = request.findSet();
|
||||
return request.findSet();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return set;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1582,14 +1512,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
Map<?, SqlRow> map = request.findMap();
|
||||
return request.findMap();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
|
||||
return map;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1604,82 +1530,126 @@ 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(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void markAsDirty(Object bean) {
|
||||
if (bean instanceof EntityBean == false) {
|
||||
throw new IllegalArgumentException("This bean is not an EntityBean?");
|
||||
}
|
||||
persister.save(bean, t);
|
||||
// mark the bean as dirty (so that an update will not get skipped)
|
||||
((EntityBean)bean)._ebean_getIntercept().setDirty(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +1670,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 +1698,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 +1719,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 +1750,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 +1768,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 +1832,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 +1861,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 +1964,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 +2037,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.
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Robin Bygrave
|
||||
*
|
||||
* This file is part of Ebean.
|
||||
*
|
||||
* Ebean is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Ebean is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -22,6 +24,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
@@ -31,6 +34,7 @@ import com.avaje.ebeaninternal.server.resource.ResourceManager;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
|
||||
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
|
||||
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
|
||||
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
|
||||
@@ -42,8 +46,6 @@ import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
/**
|
||||
* Used to extend the ServerConfig with additional objects used to configure and
|
||||
* construct an EbeanServer.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class InternalConfiguration {
|
||||
|
||||
@@ -113,10 +115,9 @@ public class InternalConfiguration {
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
beanDescriptorManager.deploy();
|
||||
|
||||
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
|
||||
serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
this.transactionManager = createTransactionManager();
|
||||
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder, backgroundExecutor);
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
@@ -131,6 +132,34 @@ public class InternalConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
private TransactionManager createTransactionManager() {
|
||||
|
||||
if (isAutoCommitMode()) {
|
||||
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on.
|
||||
*/
|
||||
private boolean isAutoCommitMode() {
|
||||
if (serverConfig.isAutoCommitMode()) {
|
||||
// explicitly set
|
||||
return true;
|
||||
}
|
||||
DataSource dataSource = serverConfig.getDataSource();
|
||||
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
|
||||
// We know the DataSourcePool is using autoCommit
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public JsonContext createJsonContext(SpiEbeanServer server) {
|
||||
|
||||
|
||||
@@ -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,19 @@ 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);
|
||||
|
||||
/**
|
||||
* Return the database platform like clause.
|
||||
*/
|
||||
@Override
|
||||
public String getDBLikeClause() {
|
||||
return ebeanServer.getDatabasePlatform().getLikeClause();
|
||||
}
|
||||
|
||||
public void executeSecondaryQueries(int defaultQueryBatch) {
|
||||
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
|
||||
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +93,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 +114,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 +163,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 +172,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.
|
||||
@@ -197,24 +202,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
/**
|
||||
* Will end a locally created transaction.
|
||||
* <p>
|
||||
* It ends the transaction by using a rollback() as the transaction is known
|
||||
* to be readOnly.
|
||||
* It ends the query only transaction.
|
||||
* </p>
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction && !backgroundFetching) {
|
||||
// we can rollback as readOnly transaction
|
||||
transaction.rollback();
|
||||
if (createdTransaction) {
|
||||
transaction.endQueryOnly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +273,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 +351,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) {
|
||||
@@ -375,13 +369,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* Log the SQL if the logLevel is appropriate.
|
||||
*/
|
||||
public void logSql(String sql) {
|
||||
if (transaction.isLogSql()) {
|
||||
transaction.logSql(sql);
|
||||
}
|
||||
transaction.logSql(sql);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
@@ -40,15 +40,6 @@ public final class RelationalQueryRequest {
|
||||
this.trans = (SpiTransaction) t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the transaction if it was created for this request.
|
||||
*/
|
||||
public void rollbackTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
trans.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transaction if none currently exists.
|
||||
*/
|
||||
@@ -58,10 +49,6 @@ public final class RelationalQueryRequest {
|
||||
if (trans == null || !trans.isActive()) {
|
||||
// create a local readOnly transaction
|
||||
trans = ebeanServer.createServerTransaction(false, -1);
|
||||
|
||||
// commented out for performance reasons...
|
||||
// TODO: review performance of trans.setReadOnly(true)
|
||||
// trans.setReadOnly(true);
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
@@ -72,8 +59,7 @@ public final class RelationalQueryRequest {
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
// we can rollback as a readOnly transaction.
|
||||
trans.rollback();
|
||||
trans.endQueryOnly();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@ public interface SpiOrmQueryRequest<T> {
|
||||
*/
|
||||
public void endTransIfRequired();
|
||||
|
||||
public void rollbackTransIfRequired();
|
||||
|
||||
/**
|
||||
* Execute the query as findById.
|
||||
*/
|
||||
@@ -97,4 +95,9 @@ public interface SpiOrmQueryRequest<T> {
|
||||
*/
|
||||
public BeanCollection<T> getFromQueryCache();
|
||||
|
||||
/**
|
||||
* Return the Database platform like clause.
|
||||
*/
|
||||
public String getDBLikeClause();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.avaje.ebeaninternal.server.ddl;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
|
||||
/**
|
||||
* A visitor that creates indexes for columns annotated with ColumnIndex
|
||||
*
|
||||
* @author rvbiljouw
|
||||
* @see com.avaje.ebean.annotation.Index
|
||||
*/
|
||||
public class CreateIndexVisitor extends AbstractBeanVisitor {
|
||||
private final IndexPropertyVisitor iv;
|
||||
|
||||
public CreateIndexVisitor(DdlGenContext ctx) {
|
||||
this.iv = new IndexPropertyVisitor(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBegin() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitBean(BeanDescriptor<?> descriptor) {
|
||||
return descriptor.isInheritanceRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyVisitor visitProperty(BeanProperty p) {
|
||||
return iv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
|
||||
visitInheritanceProperties(descriptor, iv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
|
||||
}
|
||||
|
||||
private static final class IndexPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
private final DdlGenContext ctx;
|
||||
|
||||
public IndexPropertyVisitor(DdlGenContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
String baseTable = p.getBeanDescriptor().getBaseTable();
|
||||
if (p.isIndexed()) {
|
||||
String indexName = p.getIndexName();
|
||||
if (indexName.length() == 0) {
|
||||
indexName = ctx.getDdlSyntax().getIndexName(baseTable, p.getDbColumn(), ctx.incrementIxCount());
|
||||
}
|
||||
ctx.write("create index ")
|
||||
.write(indexName)
|
||||
.write(" on ")
|
||||
.write(baseTable)
|
||||
.write("(")
|
||||
.write(p.getDbColumn())
|
||||
.write(");")
|
||||
.writeNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@ package com.avaje.ebeaninternal.server.ddl;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -12,8 +16,6 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Used as part of CreateTableVisitor to generated the create table DDL script.
|
||||
@@ -28,6 +30,8 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
private final CreateTableVisitor parent;
|
||||
|
||||
private BeanPropertyAssocOne<?> embedded;
|
||||
|
||||
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
|
||||
this.parent = parent;
|
||||
this.ctx = ctx;
|
||||
@@ -66,12 +70,13 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
this.embedded = embedded;
|
||||
visitScalar(p);
|
||||
}
|
||||
|
||||
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
|
||||
|
||||
String uqConstraintName = "uq_" + table + "_" + column;
|
||||
String uqConstraintName = "uq_"+TableName.parse(table)+"_"+column;
|
||||
|
||||
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()) {
|
||||
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
|
||||
@@ -153,11 +158,12 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
parent.writeColumnName(p.getDbColumn(), p);
|
||||
|
||||
String columnDefn = ctx.getColumnDefn(p);
|
||||
ctx.write(columnDefn);
|
||||
|
||||
|
||||
boolean identity = isIdentity(p);
|
||||
if (identity) {
|
||||
writeIdentity();
|
||||
ctx.write(ddl.getIdentityColumnDefn(columnDefn));
|
||||
} else {
|
||||
ctx.write(columnDefn);
|
||||
}
|
||||
|
||||
if (p.isId() && ddl.isInlinePrimaryKeyConstraint()){
|
||||
@@ -181,18 +187,16 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
private String createUniqueConstraint(BeanProperty p) {
|
||||
|
||||
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
|
||||
String baseTable = p.getBeanDescriptor().getBaseTable();
|
||||
if (baseTable == null) {
|
||||
// Embedded bean property that has unique constraint on it
|
||||
baseTable = embedded.getBeanDescriptor().getBaseTable();
|
||||
}
|
||||
StringBuilder expr = createUniqueConstraintBuffer(baseTable, p.getDbColumn());
|
||||
|
||||
expr.append(p.getDbColumn()).append(")");
|
||||
return expr.toString();
|
||||
}
|
||||
|
||||
protected void writeIdentity() {
|
||||
String identity = ddl.getIdentity();
|
||||
if (identity != null && identity.length() > 0) {
|
||||
ctx.write(" ").write(identity);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeIdentitySuffix() {
|
||||
String identity = ddl.getIdentitySuffix();
|
||||
|
||||
@@ -4,6 +4,10 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -13,8 +17,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.SqlReservedWords;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Used to generated the create table DDL script.
|
||||
@@ -23,23 +25,22 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CreateTableVisitor.class);
|
||||
|
||||
final DdlGenContext ctx;
|
||||
private final DdlGenContext ctx;
|
||||
|
||||
final PropertyVisitor pv;
|
||||
private final PropertyVisitor pv;
|
||||
|
||||
final DbDdlSyntax ddl;
|
||||
private final DbDdlSyntax ddl;
|
||||
|
||||
final int columnNameWidth;
|
||||
private final int columnNameWidth;
|
||||
|
||||
// avoid writing columns twice, e.g. when used in associations with insertable=false and updateable=false
|
||||
private final Set<String> wroteColumns = new HashSet<String>();
|
||||
|
||||
private ArrayList<String> checkConstraints = new ArrayList<String>();
|
||||
private ArrayList<String> checkConstraints = new ArrayList<String>();
|
||||
|
||||
private ArrayList<String> uniqueConstraints = new ArrayList<String>();
|
||||
private ArrayList<String> uniqueConstraints = new ArrayList<String>();
|
||||
|
||||
private String table;
|
||||
private String schema;
|
||||
private String table;
|
||||
|
||||
public Set<String> getWroteColumns() {
|
||||
return wroteColumns;
|
||||
@@ -53,235 +54,211 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
|
||||
this.pv = new CreateTableColumnVisitor(this, ctx);
|
||||
}
|
||||
|
||||
public boolean isDbColumnWritten(String dbColumn) {
|
||||
// Columns are not case sensitive - user lower case
|
||||
// as e.g. @JoinColumn(s) may use a different case
|
||||
return wroteColumns.contains(dbColumn.toLowerCase());
|
||||
}
|
||||
public boolean isDbColumnWritten(String dbColumn) {
|
||||
// Columns are not case sensitive - user lower case
|
||||
// as e.g. @JoinColumn(s) may use a different case
|
||||
return wroteColumns.contains(dbColumn.toLowerCase());
|
||||
}
|
||||
|
||||
public void addDbColumnWritten(String dbColumn){
|
||||
// Column names are case insensitive
|
||||
wroteColumns.add(dbColumn.toLowerCase());
|
||||
}
|
||||
public void addDbColumnWritten(String dbColumn) {
|
||||
// Column names are case insensitive
|
||||
wroteColumns.add(dbColumn.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the table name including a check for SQL reserved words.
|
||||
*/
|
||||
protected void writeTableName(BeanDescriptor<?> descriptor) {
|
||||
|
||||
String tableName = descriptor.getBaseTable();
|
||||
int dotPos = tableName.lastIndexOf('.');
|
||||
if (dotPos > -1){
|
||||
schema = tableName.substring(0, dotPos);
|
||||
table = tableName.substring(dotPos+1);
|
||||
} else {
|
||||
table = tableName;
|
||||
}
|
||||
|
||||
if (SqlReservedWords.isKeyword(table)) {
|
||||
logger.warn("Table name ["+table+"] is a suspected SQL reserved word for bean "+descriptor.getFullName());
|
||||
}
|
||||
|
||||
ctx.write(tableName);
|
||||
}
|
||||
|
||||
protected String getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
protected String getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the column name including a check for the SQL reserved words.
|
||||
* @param p
|
||||
*/
|
||||
protected void writeColumnName(String columnName, BeanProperty p) {
|
||||
|
||||
addDbColumnWritten(columnName);
|
||||
|
||||
if (SqlReservedWords.isKeyword(columnName)) {
|
||||
String propName = p == null ? "(Unknown)" : p.getFullBeanName();
|
||||
logger.warn("Column name ["+columnName+"] is a suspected SQL reserved word for property "+propName);
|
||||
}
|
||||
|
||||
ctx.write(" ").write(columnName, columnNameWidth).write(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a check constraint for the property if required.
|
||||
* <p>
|
||||
* Typically check constraint based on Enum mapping values.
|
||||
* </p>
|
||||
*/
|
||||
protected void addCheckConstraint(BeanProperty p, String prefix, String constraintExpression) {
|
||||
|
||||
if (p != null && constraintExpression != null){
|
||||
|
||||
// build constraint clause
|
||||
String s = "constraint "+getConstraintName(prefix, p)+" "+constraintExpression;
|
||||
|
||||
// add to list as we render all check constraints just prior to primary key
|
||||
checkConstraints.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getConstraintName(String prefix, BeanProperty p) {
|
||||
return prefix + table + "_" + p.getDbColumn();
|
||||
}
|
||||
|
||||
protected void addUniqueConstraint(String constraintExpression) {
|
||||
uniqueConstraints.add(constraintExpression);
|
||||
}
|
||||
/**
|
||||
* Write the table name including a check for SQL reserved words.
|
||||
*/
|
||||
protected void writeTableName(BeanDescriptor<?> descriptor) {
|
||||
|
||||
protected void addCheckConstraint(String constraintExpression) {
|
||||
checkConstraints.add(constraintExpression);
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(BeanProperty p) {
|
||||
addCheckConstraint(p,"ck_", p.getDbConstraintExpression());
|
||||
}
|
||||
|
||||
public boolean visitBean(BeanDescriptor<?> descriptor) {
|
||||
|
||||
wroteColumns.clear();
|
||||
|
||||
if (!descriptor.isInheritanceRoot()){
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx.write("create table ");
|
||||
writeTableName(descriptor);
|
||||
ctx.write(" (").writeNewLine();
|
||||
|
||||
InheritInfo inheritInfo = descriptor.getInheritInfo();
|
||||
if (inheritInfo != null && inheritInfo.isRoot()){
|
||||
String discColumn = inheritInfo.getDiscriminatorColumn();
|
||||
int discType = inheritInfo.getDiscriminatorType();
|
||||
int discLength = inheritInfo.getDiscriminatorLength();
|
||||
DbType dbType = ctx.getDbTypeMap().get(discType);
|
||||
String discDbType = dbType.renderType(discLength, 0);
|
||||
|
||||
writeColumnName(discColumn, null);
|
||||
ctx.write(discDbType);
|
||||
ctx.write(" not null,");
|
||||
ctx.writeNewLine();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
|
||||
// parse to remove any catalog or schema prefix on the table
|
||||
table = TableName.parse(descriptor.getBaseTable());
|
||||
ctx.write(descriptor.getBaseTable());
|
||||
}
|
||||
|
||||
visitInheritanceProperties(descriptor, pv);
|
||||
|
||||
if (checkConstraints.size() > 0){
|
||||
for (String checkConstraint : checkConstraints) {
|
||||
ctx.write(" ").write(checkConstraint).write(",").writeNewLine();
|
||||
}
|
||||
checkConstraints = new ArrayList<String>();
|
||||
}
|
||||
|
||||
if (uniqueConstraints.size() > 0){
|
||||
for (String constraint : uniqueConstraints) {
|
||||
ctx.write(" ").write(constraint).write(",").writeNewLine();
|
||||
}
|
||||
uniqueConstraints = new ArrayList<String>();
|
||||
}
|
||||
|
||||
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
|
||||
if (compoundUniqueConstraints != null){
|
||||
String table = descriptor.getBaseTable();
|
||||
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
|
||||
String constraint = createUniqueConstraint(table, i, compoundUniqueConstraints[i]);
|
||||
ctx.write(" ").write(constraint).write(",").writeNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BeanProperty[] ids = descriptor.propertiesId();
|
||||
/**
|
||||
* Write the column name including a check for the SQL reserved words.
|
||||
*/
|
||||
protected void writeColumnName(String columnName, BeanProperty p) {
|
||||
|
||||
if (ids.length == 0){
|
||||
// No comma + new line
|
||||
ctx.removeLast().removeLast();
|
||||
} else if (ids.length > 1 || ddl.isInlinePrimaryKeyConstraint()) {
|
||||
// The Primary Key constraint was inlined with the column
|
||||
// ... No comma + new line
|
||||
ctx.removeLast().removeLast();
|
||||
|
||||
} else {
|
||||
// Add the primay key constraint
|
||||
String pkName = ddl.getPrimaryKeyName(table);
|
||||
ctx.write(" constraint ").write(pkName).write(" primary key (");
|
||||
|
||||
VisitorUtil.visit(ids, new AbstractPropertyVisitor() {
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
ctx.write(p.getDbColumn()).write(", ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
ctx.write(p.getDbColumn()).write(", ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
ctx.write(p.getDbColumn()).write(", ");
|
||||
}
|
||||
|
||||
});
|
||||
// remove the last comma, end of PK
|
||||
ctx.removeLast().write(")");
|
||||
}
|
||||
|
||||
// end of table
|
||||
ctx.write(")").writeNewLine();
|
||||
ctx.write(";").writeNewLine().writeNewLine();
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
private String createUniqueConstraint(String table, int idx, CompoundUniqueContraint uc) {
|
||||
|
||||
String uqConstraintName = "uq_"+table+"_"+(idx+1) ;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("constraint ")
|
||||
.append(uqConstraintName)
|
||||
.append(" unique (");
|
||||
|
||||
String[] columns = uc.getColumns();
|
||||
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0){
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(columns[i]);
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
addDbColumnWritten(columnName);
|
||||
|
||||
if (SqlReservedWords.isKeyword(columnName)) {
|
||||
String propName = p == null ? "(Unknown)" : p.getFullBeanName();
|
||||
logger.warn("Column name [" + columnName + "] is a suspected SQL reserved word for property " + propName);
|
||||
}
|
||||
|
||||
ctx.write(" ").write(columnName, columnNameWidth).write(" ");
|
||||
}
|
||||
|
||||
public void visitBeanDescriptorEnd() {
|
||||
ctx.write(");").writeNewLine().writeNewLine();
|
||||
}
|
||||
/**
|
||||
* Build a check constraint for the property if required.
|
||||
* <p>
|
||||
* Typically check constraint based on Enum mapping values.
|
||||
* </p>
|
||||
*/
|
||||
protected void addCheckConstraint(BeanProperty p, String prefix, String constraintExpression) {
|
||||
|
||||
if (p != null && constraintExpression != null) {
|
||||
|
||||
public PropertyVisitor visitProperty(BeanProperty p) {
|
||||
return pv;
|
||||
}
|
||||
// build constraint clause
|
||||
String s = "constraint " + getConstraintName(prefix, p) + " " + constraintExpression;
|
||||
|
||||
public void visitBegin() {
|
||||
|
||||
}
|
||||
// add to list as we render all check constraints just prior to primary key
|
||||
checkConstraints.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
ctx.addIntersectionCreateTables();
|
||||
ctx.flush();
|
||||
}
|
||||
protected String getConstraintName(String prefix, BeanProperty p) {
|
||||
return prefix + table + "_" + p.getDbColumn();
|
||||
}
|
||||
|
||||
protected void addUniqueConstraint(String constraintExpression) {
|
||||
uniqueConstraints.add(constraintExpression);
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(String constraintExpression) {
|
||||
checkConstraints.add(constraintExpression);
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(BeanProperty p) {
|
||||
addCheckConstraint(p, "ck_", p.getDbConstraintExpression());
|
||||
}
|
||||
|
||||
public boolean visitBean(BeanDescriptor<?> descriptor) {
|
||||
|
||||
wroteColumns.clear();
|
||||
|
||||
if (!descriptor.isInheritanceRoot()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx.write("create table ");
|
||||
writeTableName(descriptor);
|
||||
ctx.write(" (").writeNewLine();
|
||||
|
||||
InheritInfo inheritInfo = descriptor.getInheritInfo();
|
||||
if (inheritInfo != null && inheritInfo.isRoot()) {
|
||||
String discColumn = inheritInfo.getDiscriminatorColumn();
|
||||
int discType = inheritInfo.getDiscriminatorType();
|
||||
int discLength = inheritInfo.getDiscriminatorLength();
|
||||
DbType dbType = ctx.getDbTypeMap().get(discType);
|
||||
String discDbType = dbType.renderType(discLength, 0);
|
||||
|
||||
writeColumnName(discColumn, null);
|
||||
ctx.write(discDbType);
|
||||
ctx.write(" not null,");
|
||||
ctx.writeNewLine();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
|
||||
|
||||
visitInheritanceProperties(descriptor, pv);
|
||||
|
||||
if (checkConstraints.size() > 0) {
|
||||
for (String checkConstraint : checkConstraints) {
|
||||
ctx.write(" ").write(checkConstraint).write(",").writeNewLine();
|
||||
}
|
||||
checkConstraints = new ArrayList<String>();
|
||||
}
|
||||
|
||||
if (uniqueConstraints.size() > 0) {
|
||||
for (String constraint : uniqueConstraints) {
|
||||
ctx.write(" ").write(constraint).write(",").writeNewLine();
|
||||
}
|
||||
uniqueConstraints = new ArrayList<String>();
|
||||
}
|
||||
|
||||
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
|
||||
if (compoundUniqueConstraints != null) {
|
||||
String table = descriptor.getBaseTable();
|
||||
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
|
||||
String constraint = createUniqueConstraint(table, i, compoundUniqueConstraints[i]);
|
||||
ctx.write(" ").write(constraint).write(",").writeNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
BeanProperty idProp = descriptor.getIdProperty();
|
||||
|
||||
if (idProp == null) {
|
||||
// No comma + new line
|
||||
ctx.removeLast().removeLast();
|
||||
} else if (ddl.isInlinePrimaryKeyConstraint()) {
|
||||
// The Primary Key constraint was inlined with the column
|
||||
// ... No comma + new line
|
||||
ctx.removeLast().removeLast();
|
||||
|
||||
} else {
|
||||
// Add the primay key constraint
|
||||
String pkName = ddl.getPrimaryKeyName(table);
|
||||
ctx.write(" constraint ").write(pkName).write(" primary key (");
|
||||
|
||||
VisitorUtil.visit(idProp, new AbstractPropertyVisitor() {
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
ctx.write(p.getDbColumn()).write(", ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
ctx.write(p.getDbColumn()).write(", ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
ctx.write(p.getDbColumn()).write(", ");
|
||||
}
|
||||
|
||||
});
|
||||
// remove the last comma, end of PK
|
||||
ctx.removeLast().write(")");
|
||||
}
|
||||
|
||||
// end of table
|
||||
ctx.write(")").writeNewLine();
|
||||
ctx.write(";").writeNewLine().writeNewLine();
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
private String createUniqueConstraint(String table, int idx, CompoundUniqueContraint uc) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append("constraint ")
|
||||
.append("uq_").append(TableName.parse(table)).append("_").append(idx + 1)
|
||||
.append(" unique (");
|
||||
|
||||
String[] columns = uc.getColumns();
|
||||
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(columns[i]);
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void visitBeanDescriptorEnd() {
|
||||
ctx.write(");").writeNewLine().writeNewLine();
|
||||
}
|
||||
|
||||
public PropertyVisitor visitProperty(BeanProperty p) {
|
||||
return pv;
|
||||
}
|
||||
|
||||
public void visitBegin() {
|
||||
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
ctx.addIntersectionCreateTables();
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,17 +98,17 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeDrop(String dropFile) {
|
||||
protected void writeDrop(String dropFile) {
|
||||
|
||||
try {
|
||||
String c = generateDropDdl();
|
||||
writeFile(dropFile, c);
|
||||
try {
|
||||
String c = generateDropDdl();
|
||||
writeFile(dropFile, c);
|
||||
|
||||
} catch (IOException e) {
|
||||
String msg = "Error generating Drop DDL";
|
||||
throw new PersistenceException(msg, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
String msg = "Error generating Drop DDL";
|
||||
throw new PersistenceException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeCreate(String createFile) {
|
||||
|
||||
@@ -149,6 +149,9 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
|
||||
VisitorUtil.visit(server, fkeys);
|
||||
|
||||
CreateIndexVisitor indexes = new CreateIndexVisitor(ctx);
|
||||
VisitorUtil.visit(server, indexes);
|
||||
|
||||
ctx.flush();
|
||||
createContent = ctx.getContent();
|
||||
return createContent;
|
||||
|
||||
@@ -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,611 @@
|
||||
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);
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
ebi.setBeanLoader(desc.getEbeanServer());
|
||||
|
||||
if (Boolean.TRUE.equals(readOnly)) {
|
||||
ebi.setReadOnly(true);
|
||||
}
|
||||
|
||||
beanLoadData(bean, data);
|
||||
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, id);
|
||||
}
|
||||
return (T) bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a bean from the cache given its Id.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Transient;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Model;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.RawSqlBuilder;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
@@ -60,8 +64,7 @@ import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflectProperties;
|
||||
import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
@@ -148,6 +151,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final XmlConfig xmlConfig;
|
||||
|
||||
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
|
||||
|
||||
/**
|
||||
* Create for a given database dbConfig.
|
||||
*/
|
||||
@@ -176,6 +181,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 +227,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 +286,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,9 +511,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private void readEntityBeanTable() {
|
||||
|
||||
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
DeployBeanInfo<?> info = it.next();
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
BeanTable beanTable = createBeanTable(info);
|
||||
beanTableMap.put(beanTable.getBeanType(), beanTable);
|
||||
}
|
||||
@@ -521,18 +525,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private void readEntityDeploymentAssociations() {
|
||||
|
||||
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
DeployBeanInfo<?> info = it.next();
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
readDeployAssociations(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void readInheritedIdGenerators() {
|
||||
|
||||
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
DeployBeanInfo<?> info = it.next();
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
|
||||
InheritInfo inheritInfo = descriptor.getInheritInfo();
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
@@ -608,10 +608,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* @param info the new inheritance info
|
||||
*/
|
||||
private void setInheritanceInfo(DeployBeanInfo<?> info) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
|
||||
|
||||
if (assoc != null){
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
@@ -621,7 +621,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
|
||||
|
||||
if (assoc != null){
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
@@ -750,9 +749,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
String searchName = name.substring(0, p).toLowerCase();
|
||||
|
||||
// search for this in the possible matches
|
||||
Iterator<String> it = matchSet.iterator();
|
||||
while (it.hasNext()) {
|
||||
String possibleMappedBy = it.next();
|
||||
for (String possibleMappedBy : matchSet) {
|
||||
String possibleLower = possibleMappedBy.toLowerCase();
|
||||
if (possibleLower.indexOf(searchName) > -1) {
|
||||
// we have a match..
|
||||
@@ -872,7 +869,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
if (!tableJoin.hasJoinColumns()) {
|
||||
// define Join as the inverse of the mappedBy property
|
||||
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
|
||||
otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable());
|
||||
otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1009,9 +1006,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
desc.setUpdateChangesOnly(updateChangesOnly);
|
||||
|
||||
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
|
||||
|
||||
// set bean controller, finder and listener
|
||||
setBeanControllerFinderListener(desc);
|
||||
deplyInherit.process(desc);
|
||||
desc.checkInheritanceMapping();
|
||||
|
||||
createProperties.createProperties(desc);
|
||||
|
||||
@@ -1146,12 +1146,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private void setScalarType(DeployBeanDescriptor<?> deployDesc) {
|
||||
|
||||
Iterator<DeployBeanProperty> it = deployDesc.propertiesAll();
|
||||
while (it.hasNext()) {
|
||||
DeployBeanProperty prop = it.next();
|
||||
if (prop instanceof DeployBeanPropertyAssoc<?>) {
|
||||
|
||||
} else {
|
||||
for (DeployBeanProperty prop : deployDesc.propertiesAll()) {
|
||||
if (prop instanceof DeployBeanPropertyAssoc<?> == false) {
|
||||
deployUtil.setScalarType(prop);
|
||||
}
|
||||
}
|
||||
@@ -1298,46 +1294,46 @@ 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();
|
||||
|
||||
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);
|
||||
}
|
||||
for (DeployBeanProperty prop : desc.propertiesAll()) {
|
||||
String propName = prop.getName();
|
||||
Integer pos = reflectProps.getPropertyIndex(propName);
|
||||
if (pos == null) {
|
||||
if (isPersistentField(prop)) {
|
||||
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
int propertyIndex = pos.intValue();
|
||||
prop.setPropertyIndex(propertyIndex);
|
||||
prop.setGetter(beanReflect.getGetter(propName, propertyIndex));
|
||||
prop.setSetter(beanReflect.getSetter(propName, propertyIndex));
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a persistent field (not transient or static).
|
||||
*/
|
||||
private boolean isPersistentField(DeployBeanProperty prop) {
|
||||
|
||||
Field field = prop.getField();
|
||||
int modifiers = field.getModifiers();
|
||||
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
|
||||
return false;
|
||||
}
|
||||
if (field.isAnnotationPresent(Transient.class)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* DevNote: It is assumed that Embedded can contain version properties. It is
|
||||
* also assumed that Embedded beans do NOT themselves contain Embedded beans
|
||||
@@ -1345,13 +1341,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,93 +1388,68 @@ 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 (Model.class.equals(superclass)) {
|
||||
// top of the inheritance. Not enhancing Model at this stage
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityBean.class.isAssignableFrom(superclass)) {
|
||||
if (isMappedSuperWithNoProperties(superclass)) {
|
||||
// ok to stop and treat just the same as Object.class
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
|
||||
}
|
||||
|
||||
// recursively continue up the inheritance hierarchy
|
||||
checkInheritedClasses(ensureEnhanced, superclass);
|
||||
checkInheritedClasses(superclass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a MappedSuperclass bean with no persistent properties.
|
||||
* If so it is ok for it not to be enhanced.
|
||||
*/
|
||||
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
|
||||
|
||||
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
|
||||
if (annotation == null) {
|
||||
return false;
|
||||
}
|
||||
Field[] fields = beanClass.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) {
|
||||
// ignore this field
|
||||
} else if (field.isAnnotationPresent(Transient.class)) {
|
||||
// ignore this field
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort the BeanDescriptors by name.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user