mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
145
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
3b4836cad9 | ||
|
|
6048d97aab | ||
|
|
16d32b408e | ||
|
|
0435ab0991 | ||
|
|
95617d1bea | ||
|
|
96e4b4640f | ||
|
|
a3c8ed88ca | ||
|
|
38d0e7642e | ||
|
|
e4306ce1ab | ||
|
|
444b656780 | ||
|
|
fcbba23441 | ||
|
|
18961047a1 | ||
|
|
34347fc3e1 | ||
|
|
189e0a2ea9 | ||
|
|
182e027521 | ||
|
|
f85fa2c21d | ||
|
|
5a5f479568 | ||
|
|
9ca2486c9c | ||
|
|
656b239925 | ||
|
|
5d46904341 | ||
|
|
f0dd069ed2 | ||
|
|
5bd454bdc0 | ||
|
|
eeebaf9a08 | ||
|
|
636a148509 | ||
|
|
170cd6051e | ||
|
|
522fc2384c | ||
|
|
6f214590aa | ||
|
|
0014ea3501 | ||
|
|
db3140512f | ||
|
|
8bfa331c61 | ||
|
|
456a1d8608 | ||
|
|
695d3374a8 | ||
|
|
5488d054bf | ||
|
|
4d72273dc3 | ||
|
|
cb9a5d6709 | ||
|
|
1b8aa682c6 | ||
|
|
ef39bfcf14 | ||
|
|
749f8d0d45 | ||
|
|
2fd221bbb5 | ||
|
|
ee96c4afa6 | ||
|
|
87a948223c | ||
|
|
acca803a80 | ||
|
|
470b27efe1 | ||
|
|
f287205421 | ||
|
|
6bd8c2bd05 | ||
|
|
05dcdf162b | ||
|
|
e97e77e2e0 | ||
|
|
e685e2f4ac | ||
|
|
c6019bf3c5 | ||
|
|
1a9a4c957c | ||
|
|
bd57a3b9c8 | ||
|
|
21a71c3a4f | ||
|
|
f66439a62e | ||
|
|
73452a5f68 | ||
|
|
5f4789f9ba | ||
|
|
eaac874ac0 | ||
|
|
4900a460d1 | ||
|
|
d16f33d26b | ||
|
|
664f08fd6f | ||
|
|
d8391ba90c | ||
|
|
c4d41ecb00 | ||
|
|
39f5284a76 | ||
|
|
4ec62a5772 | ||
|
|
eb1eafe98c |
@@ -7,3 +7,10 @@ target/
|
||||
logs/
|
||||
log/
|
||||
/mydb.db
|
||||
|
||||
|
||||
# Intellij project files
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
avaje-ebeanorm-server
|
||||
=====================
|
||||
|
||||
The internal server implementation of EbeanORM API
|
||||
avaje-ebeanorm
|
||||
==============
|
||||
Release - 4.0.2 - May 19th: https://github.com/ebean-orm/avaje-ebeanorm/wiki/4.0.2-Release
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>4.0.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<version>4.0.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -94,7 +94,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>4.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -129,14 +129,28 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.15</version>
|
||||
<version>5.1.27</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<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>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -160,10 +174,19 @@
|
||||
<!-- Enhance the meta beans -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>4.1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>main</id>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -57,6 +56,9 @@ final class DRawSqlColumnsParser {
|
||||
split = tmp.toArray(new String[tmp.size()]);
|
||||
}
|
||||
|
||||
if (split.length == 0) {
|
||||
throw new PersistenceException("Huh? Not expecting length=0 when parsing column " + colInfo);
|
||||
}
|
||||
if (split.length == 1) {
|
||||
// default to column the same name as the property
|
||||
return new ColumnMapping.Column(indexPos++, split[0], null);
|
||||
@@ -64,17 +66,18 @@ final class DRawSqlColumnsParser {
|
||||
if (split.length == 2) {
|
||||
return new ColumnMapping.Column(indexPos++, split[0], split[1]);
|
||||
}
|
||||
if (split.length == 3) {
|
||||
if (!split[1].equalsIgnoreCase("as")) {
|
||||
String msg = "Expecting AS keyword parsing column " + colInfo;
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
return new ColumnMapping.Column(indexPos++, split[0], split[2]);
|
||||
// Ok, we now expect/require the AS keyword and it should be the
|
||||
// second to last word in the colInfo content
|
||||
if (!split[split.length - 2].equalsIgnoreCase("as")) {
|
||||
throw new PersistenceException("Expecting AS keyword as second to last word when parsing column " + colInfo);
|
||||
}
|
||||
|
||||
String msg = "Expecting Max 3 words parsing column " + colInfo + ". Got "
|
||||
+ Arrays.toString(split);
|
||||
throw new PersistenceException(msg);
|
||||
// build back the 'column formula' that precedes the AS keyword
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(split[0]);
|
||||
for (int i = 1; i < split.length-2; i++) {
|
||||
sb.append(" ").append(split[i]);
|
||||
}
|
||||
return new ColumnMapping.Column(indexPos++, sb.toString(), split[split.length - 1]);
|
||||
}
|
||||
|
||||
private int nextComma() {
|
||||
|
||||
@@ -5,20 +5,20 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This Ebean object is effectively a singleton that holds a map of registered
|
||||
@@ -214,14 +214,13 @@ public final class Ebean {
|
||||
* Register a server so we can get it by its name.
|
||||
*/
|
||||
private void register(EbeanServer server, boolean isPrimaryServer) {
|
||||
registerWithName(server.getName(), server, isPrimaryServer);
|
||||
}
|
||||
|
||||
private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) {
|
||||
synchronized (monitor) {
|
||||
concMap.put(server.getName(), server);
|
||||
EbeanServer existingServer = syncMap.put(server.getName(), server);
|
||||
if (existingServer != null) {
|
||||
String msg = "Existing EbeanServer [" + server.getName() + "] is being replaced?";
|
||||
logger.warn(msg);
|
||||
}
|
||||
|
||||
concMap.put(name, server);
|
||||
syncMap.put(name, server);
|
||||
if (isPrimaryServer) {
|
||||
primaryServer = server;
|
||||
}
|
||||
@@ -286,6 +285,15 @@ public final class Ebean {
|
||||
serverMgr.register(server, isPrimaryServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backdoor for registering a mock implementation of EbeanServer as the default server.
|
||||
*/
|
||||
protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) {
|
||||
EbeanServer originalPrimaryServer = serverMgr.primaryServer;
|
||||
serverMgr.registerWithName(name, server, isPrimaryServer);
|
||||
return originalPrimaryServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next identity value for a given bean type.
|
||||
* <p>
|
||||
@@ -454,69 +462,65 @@ public final class Ebean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean updating the non-null properties.
|
||||
* Insert the bean. This is useful when you set the Id property on a bean and
|
||||
* want to explicitly insert it.
|
||||
*/
|
||||
public static void insert(Object bean) {
|
||||
serverMgr.getPrimaryServer().insert(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a collection of beans.
|
||||
*/
|
||||
public static void insert(Collection<?> beans) {
|
||||
serverMgr.getPrimaryServer().insert(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
|
||||
* use this update() method rather than save().
|
||||
* <p>
|
||||
* You can use this method to FORCE an update to occur (even on a bean that
|
||||
* has not been fetched but say built from JSON or XML). When
|
||||
* {@link Ebean#save(Object)} is used Ebean determines whether to use an
|
||||
* insert or an update based on the state of the bean. Using this method will
|
||||
* force an update to occur.
|
||||
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
|
||||
* update().You can create a new instance and set some of its properties programmatically for via
|
||||
* JSON/XML marshalling etc. This is described as a 'stateless update'.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is expected that this method is most useful in stateless REST services
|
||||
* or web applications where you have the values you wish to update but no
|
||||
* existing bean.
|
||||
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
|
||||
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
|
||||
* </p>
|
||||
* <p>
|
||||
* For updates against beans that have not been fetched (say built from JSON
|
||||
* or XML) this will treat deleteMissingChildren=true and will delete any
|
||||
* 'missing children'. Refer to
|
||||
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
|
||||
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
|
||||
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
|
||||
* that are in the database but are not in the collection are deleted.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
|
||||
* controls if only the changed properties are included in the update or if all the loaded
|
||||
* properties are included instead.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Customer c = new Customer();
|
||||
* c.setId(7);
|
||||
* c.setName("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 +533,8 @@ public final class Ebean {
|
||||
/**
|
||||
* Save all the beans from a Collection.
|
||||
*/
|
||||
public static int save(Collection<?> c) throws OptimisticLockException {
|
||||
return save(c.iterator());
|
||||
public static int save(Collection<?> beans) throws OptimisticLockException {
|
||||
return serverMgr.getPrimaryServer().save(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.persistence.OptimisticLockException;
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
|
||||
@@ -84,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>
|
||||
@@ -117,6 +121,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
public ExpressionFactory getExpressionFactory();
|
||||
|
||||
/**
|
||||
* Return the MetaInfoManager which is used to get meta data from the EbeanServer
|
||||
* such as query execution statistics.
|
||||
*/
|
||||
public MetaInfoManager getMetaInfoManager();
|
||||
|
||||
|
||||
/**
|
||||
* Return the BeanState for a given entity bean.
|
||||
* <p>
|
||||
@@ -402,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);
|
||||
|
||||
@@ -420,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.
|
||||
@@ -469,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.
|
||||
@@ -485,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.
|
||||
@@ -500,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.
|
||||
@@ -516,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.
|
||||
@@ -669,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.
|
||||
@@ -696,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.
|
||||
@@ -707,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
|
||||
@@ -776,205 +825,107 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Insert or update a bean with an explicit transaction.
|
||||
*/
|
||||
public void save(Object bean, Transaction t) throws OptimisticLockException;
|
||||
public void save(Object bean, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Save all the beans in the iterator with an explicit transaction.
|
||||
*/
|
||||
public int save(Iterator<?> it, Transaction t) throws OptimisticLockException;
|
||||
public int save(Iterator<?> it, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Force an update using the bean.
|
||||
* Save all the beans in the collection with an explicit transaction.
|
||||
*/
|
||||
public int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
|
||||
* use this update() method rather than save().
|
||||
* <p>
|
||||
* You can use this method to FORCE an update to occur (even on a bean that
|
||||
* has not been fetched but say built from JSON or XML). When
|
||||
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
|
||||
* insert or an update based on the state of the bean. Using this method will
|
||||
* force an update to occur.
|
||||
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
|
||||
* update().You can create a new instance and set some of its properties programmatically for via
|
||||
* JSON/XML marshalling etc. This is described as a 'stateless update'.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is expected that this method is most useful in stateless REST services
|
||||
* or web applications where you have the values you wish to update but no
|
||||
* existing bean.
|
||||
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
|
||||
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
|
||||
* </p>
|
||||
* <p>
|
||||
* For updates against beans that have not been fetched (say built from JSON
|
||||
* or XML) this will treat deleteMissingChildren=true and will delete any
|
||||
* 'missing children'. Refer to
|
||||
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
|
||||
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
|
||||
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
|
||||
* that are in the database but are not in the collection are deleted.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
|
||||
* controls if only the changed properties are included in the update or if all the loaded
|
||||
* properties are included instead.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Customer c = new Customer();
|
||||
* c.setId(7);
|
||||
* c.setName("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.
|
||||
@@ -1150,4 +1101,5 @@ public interface EbeanServer {
|
||||
*/
|
||||
public JsonContext createJsonContext();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -73,6 +73,14 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - value between two given properties.
|
||||
*/
|
||||
public static Expression between(String lowProperty, String highProperty, Object value) {
|
||||
|
||||
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater Than - property greater than the given value.
|
||||
*/
|
||||
|
||||
@@ -36,11 +36,6 @@ import java.util.Map;
|
||||
*/
|
||||
public interface ExpressionFactory {
|
||||
|
||||
/**
|
||||
* Return the language for this expression factory.
|
||||
*/
|
||||
public String getLang();
|
||||
|
||||
/**
|
||||
* Equal To - property equal to the given value.
|
||||
*/
|
||||
|
||||
@@ -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,13 +275,6 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Set a QueryListener for bean by bean processing.
|
||||
*
|
||||
* @see Query#setListener(QueryListener)
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
/**
|
||||
* Set to true to use the query for executing this query.
|
||||
*
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* <pre class="code">
|
||||
* // Normal fetch join results in a single SQL query
|
||||
* List<Order> list = Ebean.find(Order.class).join("details").findList();
|
||||
* List<Order> list = Ebean.find(Order.class).fetch("details").findList();
|
||||
*
|
||||
* // Find Orders join details using a single SQL query
|
||||
* </pre>
|
||||
@@ -51,8 +51,8 @@ import java.io.Serializable;
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("details", new JoinConfig().query())
|
||||
* .fetch("customer", new JoinConfig().query(5))
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .fetch("customer", new FetchConfig().queryFirst(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
@@ -70,7 +70,7 @@ import java.io.Serializable;
|
||||
* .select("status, shipDate")
|
||||
* .fetch("details", "quantity, price", new FetchConfig().query())
|
||||
* .fetch("details.product", "sku, name")
|
||||
* .fetch("customer", "name", new FetchConfig().query(10))
|
||||
* .fetch("customer", "name", new FetchConfig().queryFirst(5))
|
||||
* .fetch("customer.contacts")
|
||||
* .fetch("customer.shippingAddress")
|
||||
* .findList();
|
||||
@@ -96,13 +96,13 @@ import java.io.Serializable;
|
||||
* <pre class="code">
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("customer", new FetchConfig().query(3).lazy(10))
|
||||
* .fetch("customer", new FetchConfig().query(10).lazy(5))
|
||||
* .findList();
|
||||
*
|
||||
* // query 1) find order
|
||||
* // query 2) find customer where id in (?,?,?) // first 3 customers
|
||||
* // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
|
||||
* // .. then if lazy loading of customers is invoked
|
||||
* // .. use a batch size of 10 to load the customers
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
@@ -128,8 +128,8 @@ import java.io.Serializable;
|
||||
* // .. use a batch size of 5 to load the customers
|
||||
*
|
||||
* find customer (name)
|
||||
* fetch contact (contactName, phone, email)
|
||||
* fetch shippingAddress (*)
|
||||
* fetch customer.contacts (contactName, phone, email)
|
||||
* fetch customer.shippingAddress (*)
|
||||
* where id in (?,?,?,?,?)
|
||||
*
|
||||
* </pre>
|
||||
@@ -159,6 +159,7 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig lazy() {
|
||||
this.lazyBatchSize = 0;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -170,11 +171,12 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig lazy(int lazyBatchSize) {
|
||||
this.lazyBatchSize = lazyBatchSize;
|
||||
this.queryAll = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be loaded as a separate query (rather than as
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* This will use the default batch size for separate query which is 100.
|
||||
@@ -187,14 +189,15 @@ public class FetchConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that this path should be loaded as a separate query (rather than as
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
* <p>
|
||||
* The queryBatchSize is the number of parent id's that this separate query
|
||||
* will load per batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will load all beans on this path eagerly.
|
||||
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
|
||||
* is also used.
|
||||
* </p>
|
||||
*
|
||||
* @param queryBatchSize
|
||||
@@ -202,12 +205,14 @@ public class FetchConfig implements Serializable {
|
||||
*/
|
||||
public FetchConfig query(int queryBatchSize) {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
this.queryAll = true;
|
||||
// queryAll true as long as a lazy batch size has not already been set
|
||||
this.queryAll = (lazyBatchSize == -1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #query(int)} but only fetches the first batch.
|
||||
* Eagerly fetch the first batch of beans on this path.
|
||||
* This is similar to {@link #query(int)} but only fetches the first batch.
|
||||
* <p>
|
||||
* If there are more parent beans than the batch size then they will not be
|
||||
* loaded eagerly but instead use lazy loading.
|
||||
@@ -228,7 +233,7 @@ public class FetchConfig implements Serializable {
|
||||
public int getLazyBatchSize() {
|
||||
return lazyBatchSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the batch size for separate query load.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,543 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
|
||||
* deleting beans.
|
||||
*
|
||||
* <p>
|
||||
* By having your entity beans extend this it provides a 'Active Record' style programming model for
|
||||
* Ebean users.
|
||||
*
|
||||
* <p>
|
||||
* Note that there is a avaje-ebeanorm-mocker project that enables you to use Mockito or similar
|
||||
* tools to still mock out the underlying 'default EbeanServer' for testing purposes.
|
||||
*
|
||||
* <p>
|
||||
* You may choose not use this Model mapped superclass if you don't like the 'Active Record' style
|
||||
* or if you believe it 'pollutes' your entity beans.
|
||||
*
|
||||
* <p>
|
||||
* If you choose to use the Model mapped superclass you will probably also chose to additionally add
|
||||
* a {@link Finder} as a public static field to complete the active record pattern and provide a
|
||||
* relatively nice clean way to write queries.
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public class Model {
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Transaction example
|
||||
*/
|
||||
public EbeanServer db() {
|
||||
return Ebean.getServer(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a named EbeanServer that is typically different to the default server.
|
||||
*
|
||||
* <p>
|
||||
* If you are using multiple databases then each database has a name and maps to a single
|
||||
* EbeanServer. You can use this method to get an EbeanServer for another database.
|
||||
*
|
||||
* @param server
|
||||
* The name of the EbeanServer. If this is null then the default EbeanServer is returned.
|
||||
*/
|
||||
public EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update this entity depending on its state.
|
||||
*
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*/
|
||||
public void save() {
|
||||
db().save(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this entity.
|
||||
*/
|
||||
public void update() {
|
||||
db().update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert this entity.
|
||||
*/
|
||||
public void insert() {
|
||||
db().insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this entity.
|
||||
*/
|
||||
public void delete() {
|
||||
db().delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an update using this entity against the specified server.
|
||||
*/
|
||||
public void update(String server) {
|
||||
db(server).update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an insert using this entity against the specified server.
|
||||
*/
|
||||
public void insert(String server) {
|
||||
db(server).insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a delete using this entity against the specified server.
|
||||
*/
|
||||
public void delete(String server) {
|
||||
db(server).delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*/
|
||||
public void refresh() {
|
||||
db().refresh(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper object for performing queries.
|
||||
*
|
||||
* <p>
|
||||
* Typically a Finder is defined as a public static field on an entity bean class to provide a
|
||||
* nice way to write queries.
|
||||
*
|
||||
* @param I
|
||||
* The Id type. This is most often a {@link Long} but is also often a {@link UUID} or
|
||||
* {@link String}.
|
||||
*
|
||||
* @param T
|
||||
* The bean type
|
||||
*/
|
||||
public static class Finder<I, T> {
|
||||
|
||||
private final Class<I> idType;
|
||||
private final Class<T> type;
|
||||
private final String serverName;
|
||||
|
||||
/**
|
||||
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>.
|
||||
*
|
||||
* <p>
|
||||
* Typically you use this constructor to have a static "find" field on each entity bean.
|
||||
*/
|
||||
public Finder(Class<I> idType, Class<T> type) {
|
||||
this(null, idType, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>, using a
|
||||
* specific EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* Typically you don't need to use this method.
|
||||
*/
|
||||
public Finder(String serverName, Class<I> idType, Class<T> type) {
|
||||
this.type = type;
|
||||
this.idType = idType;
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
*
|
||||
*/
|
||||
public EbeanServer db() {
|
||||
return Ebean.getServer(serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return typically a different EbeanServer to the default.
|
||||
* <p>
|
||||
* This is equivilent to {@link Ebean#getServer(String)}
|
||||
*
|
||||
* @param server
|
||||
* The name of the EbeanServer. If this is null then the default EbeanServer is
|
||||
* returned.
|
||||
*/
|
||||
public EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Finder for the named EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* Create and return a new Finder for a different server.
|
||||
*/
|
||||
public Finder<I, T> on(String server) {
|
||||
return new Finder<I, T>(server, idType, type);
|
||||
}
|
||||
|
||||
// Does not exist yet but I think should
|
||||
// public void deleteById(I id) {
|
||||
// return db().deleteById(type, id);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type.
|
||||
*
|
||||
* <p>
|
||||
* This is the same as (synonym for) {@link #findList()}
|
||||
*/
|
||||
public List<T> all() {
|
||||
return findList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an entity by ID.
|
||||
*
|
||||
* <p>
|
||||
* Equivilent to {@link EbeanServer#find(Class, Object)}
|
||||
*/
|
||||
public T byId(I id) {
|
||||
return db().find(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity reference for this ID.
|
||||
*
|
||||
* <p>
|
||||
* Equivilent to {@link EbeanServer#getReference(Class, Object)}
|
||||
*/
|
||||
public T ref(I id) {
|
||||
return db().getReference(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a filter for sorting and filtering lists of entities locally without going back to
|
||||
* the database.
|
||||
* <p>
|
||||
* Equivilent to {@link EbeanServer#filter(Class)}
|
||||
*/
|
||||
public Filter<T> filter() {
|
||||
return db().filter(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query.
|
||||
* <p>
|
||||
* Equivilent to {@link EbeanServer#find(Class)}
|
||||
*/
|
||||
public Query<T> query() {
|
||||
return db().find(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next identity value.
|
||||
*
|
||||
* @see EbeanServer#nextId(Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public I nextId() {
|
||||
return (I) db().nextId(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a query and returns the results as a list of IDs.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findIds()}
|
||||
*/
|
||||
public List<Object> findIds() {
|
||||
return query().findIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type.
|
||||
* <p>
|
||||
* The same as {@link #all()}
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findList()}
|
||||
*/
|
||||
public List<T> findList() {
|
||||
return query().findList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the entities of the given type as a set.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findSet()}
|
||||
*/
|
||||
public Set<T> findSet() {
|
||||
return query().findSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type as a map of objects.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findMap()}
|
||||
*/
|
||||
public Map<?, T> findMap() {
|
||||
return query().findMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns the results as a map of the objects specifying the map key
|
||||
* property.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findMap(String, Class)}
|
||||
*/
|
||||
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
|
||||
return query().findMap(keyProperty, keyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PagedList of all entities of the given type (use where() to specify predicates as
|
||||
* needed).
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findPagedList(int, int)}
|
||||
*/
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
|
||||
return query().findPagedList(pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a find row count query in a background thread.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findFutureRowCount()}
|
||||
*/
|
||||
public FutureRowCount<T> findFutureRowCount() {
|
||||
return query().findFutureRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of entities for this type. *
|
||||
* <p>
|
||||
* Equivilent to {@link Query#findRowCount()}
|
||||
*/
|
||||
public int findRowCount() {
|
||||
return query().findRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>ExpressionFactory</code> used by this query.
|
||||
*/
|
||||
public ExpressionFactory getExpressionFactory() {
|
||||
return query().getExpressionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean,
|
||||
* to load a partial object.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#select(String)}
|
||||
*/
|
||||
public Query<T> select(String fetchProperties) {
|
||||
return query().select(fetchProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a path to load including all its properties.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#fetch(String)}
|
||||
*/
|
||||
public Query<T> fetch(String path) {
|
||||
return query().fetch(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally specifies a <code>FetchConfig</code> to specify a 'query join' and/or define the
|
||||
* lazy loading query.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#fetch(String, FetchConfig)}
|
||||
*/
|
||||
public Query<T> fetch(String path, FetchConfig joinConfig) {
|
||||
return query().fetch(path, joinConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a path to fetch with a specific list properties to include, to load a partial
|
||||
* object.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#fetch(String, String)}
|
||||
*/
|
||||
public Query<T> fetch(String path, String fetchProperties) {
|
||||
return query().fetch(path, fetchProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally specifies a <code>FetchConfig</code> to use a separate query or lazy loading to
|
||||
* load this path.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#fetch(String, String, FetchConfig)}
|
||||
*/
|
||||
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) {
|
||||
return query().fetch(assocProperty, fetchProperties, fetchConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds expressions to the <code>where</code> clause with the ability to chain on the
|
||||
* <code>ExpressionList</code>.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#where()}
|
||||
*/
|
||||
public ExpressionList<T> where() {
|
||||
return query().where();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>order by</code> clause so that you can append an ascending or descending
|
||||
* property to the <code>order by</code> clause.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #orderBy}.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#order()}
|
||||
*/
|
||||
public OrderBy<T> order() {
|
||||
return query().order();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>order by</code> clause, replacing the existing <code>order by</code> clause if
|
||||
* there is one.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #orderBy(String)}.
|
||||
*/
|
||||
public Query<T> order(String orderByClause) {
|
||||
return query().order(orderByClause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>order by</code> clause so that you can append an ascending or descending
|
||||
* property to the <code>order by</code> clause.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #order}.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#orderBy()}
|
||||
*/
|
||||
public OrderBy<T> orderBy() {
|
||||
return query().orderBy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the <code>order by</code> clause replacing the existing <code>order by</code> clause if
|
||||
* there is one.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #order(String)}.
|
||||
*/
|
||||
public Query<T> orderBy(String orderByClause) {
|
||||
return query().orderBy(orderByClause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the first row to return for this query.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#setFirstRow(int)}
|
||||
*/
|
||||
public Query<T> setFirstRow(int firstRow) {
|
||||
return query().setFirstRow(firstRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of rows to return in the query.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#setMaxRows(int)}
|
||||
*/
|
||||
public Query<T> setMaxRows(int maxRows) {
|
||||
return query().setMaxRows(maxRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID value to query.
|
||||
*
|
||||
* <p>
|
||||
* Use this to perform a find byId query but with additional control over the query such as
|
||||
* using select and fetch to control what parts of the object graph are returned.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#setId(Object)}
|
||||
*/
|
||||
public Query<T> setId(Object id) {
|
||||
return query().setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new query using the OQL.
|
||||
* <p>
|
||||
* Equivilent to {@link EbeanServer#createQuery(Class, String)}
|
||||
*/
|
||||
public Query<T> setQuery(String oql) {
|
||||
return db().createQuery(type, oql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new query based on the <code>RawSql</code>.
|
||||
* <p>
|
||||
* Equivilent to {@link Query#setRawSql(RawSql)}
|
||||
*/
|
||||
public Query<T> setRawSql(RawSql rawSql) {
|
||||
return query().setRawSql(rawSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query with explicit 'Autofetch' use.
|
||||
*/
|
||||
public Query<T> setAutofetch(boolean autofetch) {
|
||||
return query().setAutofetch(autofetch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query with the select with "for update" specified.
|
||||
*
|
||||
* <p>
|
||||
* This will typically create row level database locks on the selected rows.
|
||||
*/
|
||||
public Query<T> setForUpdate(boolean forUpdate) {
|
||||
return query().setForUpdate(forUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying whether the returned beans will be read-only.
|
||||
*/
|
||||
public Query<T> setReadOnly(boolean readOnly) {
|
||||
return query().setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying if the beans should be loaded into the L2 cache.
|
||||
*/
|
||||
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
|
||||
return query().setLoadBeanCache(loadBeanCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying if the L2 bean cache should be used.
|
||||
*/
|
||||
public Query<T> setUseCache(boolean useBeanCache) {
|
||||
return query().setUseCache(useBeanCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying if the L2 query cache should be used.
|
||||
*/
|
||||
public Query<T> setUseQueryCache(boolean useQueryCache) {
|
||||
return query().setUseQueryCache(useQueryCache);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import java.util.List;
|
||||
* Typically you will not construct an OrderBy yourself but use one that exists
|
||||
* on the Query object.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public final class OrderBy<T> implements Serializable {
|
||||
|
||||
@@ -24,7 +22,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
private transient Query<T> query;
|
||||
|
||||
private List<Property> list;
|
||||
private final List<Property> list;
|
||||
|
||||
/**
|
||||
* Create an empty OrderBy with no associated query.
|
||||
@@ -32,6 +30,10 @@ public final class OrderBy<T> implements Serializable {
|
||||
public OrderBy() {
|
||||
this.list = new ArrayList<Property>(2);
|
||||
}
|
||||
|
||||
private OrderBy(List<Property> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an orderBy parsing the order by clause.
|
||||
@@ -81,6 +83,17 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this OrderBy with the path trimmed.
|
||||
*/
|
||||
public OrderBy<T> copyWithTrim(String path) {
|
||||
List<Property> newList = new ArrayList<Property>(list.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
newList.add(list.get(i).copyWithTrim(path));
|
||||
}
|
||||
return new OrderBy<T>(newList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties for this OrderBy.
|
||||
*/
|
||||
@@ -153,31 +166,23 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof OrderBy<?>) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
OrderBy<?> other = (OrderBy<?>) obj;
|
||||
return hashCode() == other.hashCode();
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash();
|
||||
if (!(obj instanceof OrderBy<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderBy<?> e = (OrderBy<?>) obj;
|
||||
return e.list.equals(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash value for this OrderBy. This can be to determine logical
|
||||
* equality for OrderBy clauses.
|
||||
*/
|
||||
public int hash() {
|
||||
int hc = OrderBy.class.getName().hashCode();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
hc = hc * 31 + list.get(i).hash();
|
||||
}
|
||||
return hc;
|
||||
public int hashCode() {
|
||||
return list.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,11 +201,31 @@ public final class OrderBy<T> implements Serializable {
|
||||
this.ascending = ascending;
|
||||
}
|
||||
|
||||
protected int hash() {
|
||||
/**
|
||||
* Return a copy of this Property with the path trimmed.
|
||||
*/
|
||||
public Property copyWithTrim(String path) {
|
||||
return new Property(property.substring(path.length() + 1), ascending);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = property.hashCode();
|
||||
hc = hc * 31 + (ascending ? 0 : 1);
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Property e = (Property) obj;
|
||||
return e.ascending == ascending
|
||||
&& e.property.equals(property);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toStringFormat();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -269,26 +269,6 @@ import java.util.Set;
|
||||
*/
|
||||
public interface Query<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* How this query should use (or not) the Lucene Index if one is defined for
|
||||
* the bean type.
|
||||
*/
|
||||
public enum UseIndex {
|
||||
NO, DEFAULT, YES_IDS, YES_OBJECTS
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly specify how this query should use a Lucene Index if one is
|
||||
* defined for this bean type.
|
||||
*/
|
||||
public Query<T> setUseIndex(UseIndex useIndex);
|
||||
|
||||
/**
|
||||
* Return the setting for how this query should use a Lucene Index if one is
|
||||
* defined for this bean type.
|
||||
*/
|
||||
public UseIndex getUseIndex();
|
||||
|
||||
/**
|
||||
* Return the RawSql that was set to use for this query.
|
||||
*/
|
||||
@@ -609,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.
|
||||
@@ -683,32 +671,6 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setParameter(int position, Object value);
|
||||
|
||||
/**
|
||||
* Set a listener to process the query on a row by row basis.
|
||||
* <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>
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
/**
|
||||
* Set the Id value to query. This is used with findUnique().
|
||||
* <p>
|
||||
@@ -982,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>
|
||||
@@ -1068,16 +1023,14 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public String getGeneratedSql();
|
||||
|
||||
/**
|
||||
* Return the total hits matched for a lucene text search query.
|
||||
*/
|
||||
public int getTotalHits();
|
||||
|
||||
/**
|
||||
* executed the select with "for update" which should lock the record
|
||||
* "on read"
|
||||
*/
|
||||
public Query<T> setForUpdate(boolean forUpdate);
|
||||
|
||||
/**
|
||||
* Return true if this query has forUpdate set.
|
||||
*/
|
||||
public boolean isForUpdate();
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for processing a query one bean at a time.
|
||||
* <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
|
||||
*/
|
||||
public interface QueryListener<T> {
|
||||
|
||||
/**
|
||||
* Process the bean that has just been read.
|
||||
* <p>
|
||||
* This bean will not be added to the List Set or Map and nor will it be put
|
||||
* into the PersistenceContext. This is what makes this a good way to process
|
||||
* a large result set (which could normally use a lot of memory).
|
||||
* </p>
|
||||
*/
|
||||
public void process(T bean);
|
||||
}
|
||||
@@ -79,6 +79,7 @@ public interface SqlQuery extends Serializable {
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
* @deprecated
|
||||
*/
|
||||
public SqlFutureList findFutureList();
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ package com.avaje.ebean;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import java.io.Closeable;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* The Transaction object. Typically representing a JDBC or JTA transaction.
|
||||
*/
|
||||
public interface Transaction {
|
||||
public interface Transaction extends Closeable {
|
||||
|
||||
/**
|
||||
* Read Committed transaction isolation. Same as
|
||||
@@ -34,13 +35,6 @@ public interface Transaction {
|
||||
*/
|
||||
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
|
||||
|
||||
// /**
|
||||
// * You can call this after the transaction commit to wait for any changes to
|
||||
// * Lucene indexes to be made and committed. Note that generally this could
|
||||
// * be some time so it is not expected that you call this method generally.
|
||||
// */
|
||||
// public void waitForIndexUpdates();
|
||||
|
||||
/**
|
||||
* Return true if this transaction is read only.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.Query.UseIndex;
|
||||
|
||||
/**
|
||||
* Specify the default cache use specific entity type.
|
||||
@@ -56,9 +55,4 @@ public @interface CacheStrategy {
|
||||
*/
|
||||
String warmingQuery() default "";
|
||||
|
||||
/**
|
||||
* Default setting for using a text index if it has been defined on this bean
|
||||
* type.
|
||||
*/
|
||||
UseIndex useIndex() default UseIndex.DEFAULT;
|
||||
};
|
||||
|
||||
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
|
||||
/**
|
||||
* Use a version column.
|
||||
*/
|
||||
VERSION,
|
||||
|
||||
/**
|
||||
* Use all the columns (except Lobs).
|
||||
*/
|
||||
ALL
|
||||
VERSION
|
||||
}
|
||||
|
||||
@@ -3,11 +3,8 @@ package com.avaje.ebean.bean;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* Lazy loading capable Maps, Lists and Sets.
|
||||
@@ -34,10 +31,16 @@ public interface BeanCollection<E> extends Serializable {
|
||||
ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the collection is empty and untouched. Used to detect if a
|
||||
* collection was 'cleared' deliberately or just un-initialised.
|
||||
*/
|
||||
public boolean isEmptyAndUntouched();
|
||||
|
||||
/**
|
||||
* Return the bean that owns this collection.
|
||||
*/
|
||||
public Object getOwnerBean();
|
||||
public EntityBean getOwnerBean();
|
||||
|
||||
/**
|
||||
* Return the bean property name this collection represents.
|
||||
@@ -75,30 +78,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
public void setFilterMany(ExpressionList<?> filterMany);
|
||||
|
||||
/**
|
||||
* Set when this collection is being loaded via a background thread.
|
||||
* <p>
|
||||
* Refer to {@link Query#setBackgroundFetchAfter(int)}
|
||||
* </p>
|
||||
*/
|
||||
public void setBackgroundFetch(Future<Integer> future);
|
||||
|
||||
/**
|
||||
* Wait for the fetch to complete with a given timeout.
|
||||
* <p>
|
||||
* Refer to {@link Query#setBackgroundFetchAfter(int)}
|
||||
* </p>
|
||||
*/
|
||||
public void backgroundFetchWait(long wait, TimeUnit timeUnit);
|
||||
|
||||
/**
|
||||
* Wait for the fetch to complete.
|
||||
* <p>
|
||||
* Refer to {@link Query#setBackgroundFetchAfter(int)}
|
||||
* </p>
|
||||
*/
|
||||
public void backgroundFetchWait();
|
||||
|
||||
/**
|
||||
* Set a listener to be notified when the BeanCollection is first touched.
|
||||
*/
|
||||
@@ -145,14 +124,19 @@ public interface BeanCollection<E> extends Serializable {
|
||||
public boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns the underlying details as an iterator.
|
||||
* <p>
|
||||
* Note that for maps this returns the entrySet as we need the keys of the
|
||||
* map.
|
||||
* </p>
|
||||
* Returns the underlying collection of beans from the Set, Map or List.
|
||||
*/
|
||||
public Collection<E> getActualDetails();
|
||||
|
||||
/**
|
||||
* Returns the underlying entries so for Maps this is a collection of
|
||||
* Map.Entry.
|
||||
* <p>
|
||||
* For maps this returns the entrySet as we need the keys of the map.
|
||||
* </p>
|
||||
*/
|
||||
public Collection<?> getActualEntries();
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
@@ -169,18 +153,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
public void setHasMoreRows(boolean hasMoreRows);
|
||||
|
||||
/**
|
||||
* Returns true if the fetch has finished. False if the fetch is continuing in
|
||||
* a background thread.
|
||||
*/
|
||||
public boolean isFinishedFetch();
|
||||
|
||||
/**
|
||||
* Set to true when a fetch has finished. Used when a fetch continues in the
|
||||
* background.
|
||||
*/
|
||||
public void setFinishedFetch(boolean finishedFetch);
|
||||
|
||||
/**
|
||||
* return true if there are real rows held. Return false is this is using
|
||||
* Deferred fetch to lazy load the rows and the rows have not yet been
|
||||
|
||||
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
|
||||
/**
|
||||
* Add a loaded bean to the collection.
|
||||
*/
|
||||
public void addBean(Object bean);
|
||||
public void addBean(EntityBean bean);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Represent the call stack (stack trace elements).
|
||||
@@ -34,6 +35,25 @@ public final class CallStack implements Serializable {
|
||||
}
|
||||
this.pathHash = enc(hc);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 0;
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
hc = 31 * hc + callStack[i].hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CallStack)) {
|
||||
return false;
|
||||
}
|
||||
CallStack e = (CallStack) obj;
|
||||
return Arrays.equals(callStack, e.callStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first element of the call stack.
|
||||
|
||||
@@ -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
@@ -64,6 +64,25 @@ public final class ObjectGraphNode implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "origin:" + originQueryPoint + " " + ":" + path + ":" + path;
|
||||
return "origin:" + originQueryPoint + " path[" + path+"]";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * originQueryPoint.hashCode();
|
||||
hc = 31 * hc + (path == null ? 0 : path.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ObjectGraphNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGraphNode e = (ObjectGraphNode) obj;
|
||||
return ((e.path == path) || (e.path != null && e.path.equals(path)))
|
||||
&& e.originQueryPoint.equals(originQueryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public final class ObjectGraphOrigin implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 410937765287968707L;
|
||||
private static final long serialVersionUID = 410937765287968708L;
|
||||
|
||||
private final CallStack callStack;
|
||||
|
||||
private final String key;
|
||||
|
||||
private final String beanType;
|
||||
|
||||
private final int queryHash;
|
||||
|
||||
private final String key;
|
||||
|
||||
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
|
||||
this.callStack = callStack;
|
||||
this.beanType = beanType;
|
||||
this.queryHash = queryHash;
|
||||
this.key = callStack.getOriginKey(queryHash);
|
||||
}
|
||||
|
||||
@@ -55,7 +58,27 @@ public final class ObjectGraphOrigin implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return key + " " + beanType + " " + callStack.getFirstStackTraceElement();
|
||||
return "key["+ key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement()+" ";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * callStack.hashCode();
|
||||
hc = 31 * hc + beanType.hashCode();
|
||||
hc = 31 * hc + queryHash;
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ObjectGraphOrigin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
|
||||
return e.queryHash == queryHash
|
||||
&& e.beanType.equals(beanType)
|
||||
&& e.callStack.equals(callStack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ public interface PersistenceContext {
|
||||
*/
|
||||
public Object get(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Get the bean from the persistence context also checked to see if it had
|
||||
* been previously deleted (if so then you also can't hit the L2 cache to
|
||||
* fetch the bean for this particular persistence context).
|
||||
*/
|
||||
public WithOption getWithOption(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Clear all the references.
|
||||
*/
|
||||
@@ -44,9 +51,60 @@ public interface PersistenceContext {
|
||||
*/
|
||||
public void clear(Class<?> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Clear the reference as a result of an entity being deleted.
|
||||
*/
|
||||
public void deleted(Class<?> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Return the number of beans of the given type in the persistence context.
|
||||
*/
|
||||
public int size(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Wrapper on a bean to also indicate if a bean has been deleted.
|
||||
* <p>
|
||||
* If a bean has been deleted then for the same persistence context is should
|
||||
* not be able to be fetched from persistence context or L2 cache.
|
||||
* </p>
|
||||
*/
|
||||
public static class WithOption {
|
||||
|
||||
/**
|
||||
* The bean was previously deleted from this persistence context (can't hit
|
||||
* L2 cache).
|
||||
*/
|
||||
public static WithOption DELETED = new WithOption(true);
|
||||
|
||||
private final boolean deleted;
|
||||
private final Object bean;
|
||||
|
||||
private WithOption(boolean deleted) {
|
||||
this.deleted = true;
|
||||
this.bean = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The bean exists in the persistence context (and not been previously deleted).
|
||||
*/
|
||||
public WithOption(Object bean) {
|
||||
this.deleted = false;
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean was deleted. This means you can't hit the L2
|
||||
* cache.
|
||||
*/
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean (from the persistence context).
|
||||
*/
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -12,7 +10,6 @@ import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanCollectionTouched;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
/**
|
||||
* Base class for List Set and Map implementations of BeanCollection.
|
||||
@@ -38,25 +35,16 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
|
||||
protected transient BeanCollectionTouched beanCollectionTouched;
|
||||
|
||||
protected transient Future<Integer> fetchFuture;
|
||||
|
||||
/**
|
||||
* The owning bean (used for lazy fetch).
|
||||
*/
|
||||
protected final Object ownerBean;
|
||||
protected final EntityBean ownerBean;
|
||||
|
||||
/**
|
||||
* The name of this property in the owning bean (used for lazy fetch).
|
||||
*/
|
||||
protected final String propertyName;
|
||||
|
||||
/**
|
||||
* Can be false when a background thread is used to continue the fetch the
|
||||
* rows. It will set this to true when it is finished. If no background thread
|
||||
* is used then this should already be true.
|
||||
*/
|
||||
protected boolean finishedFetch = true;
|
||||
|
||||
/**
|
||||
* Flag set to true if rows are limited by firstRow maxRows and more rows
|
||||
* exist. For use by client to enable 'next' for paging.
|
||||
@@ -70,6 +58,12 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
protected boolean modifyRemoveListening;
|
||||
protected boolean modifyListening;
|
||||
|
||||
/**
|
||||
* Flag used to tell if empty collections have been cleared etc or just
|
||||
* uninitialised.
|
||||
*/
|
||||
protected boolean touched;
|
||||
|
||||
/**
|
||||
* Constructor not non-lazy loading collection.
|
||||
*/
|
||||
@@ -81,19 +75,15 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
/**
|
||||
* Used to create deferred fetch proxy.
|
||||
*/
|
||||
public AbstractBeanCollection(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
|
||||
public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
|
||||
this.loader = loader;
|
||||
this.ebeanServerName = loader.getName();
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
|
||||
if (ownerBean instanceof EntityBean) {
|
||||
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
|
||||
this.readOnly = ebi.isReadOnly();
|
||||
}
|
||||
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
|
||||
}
|
||||
|
||||
public Object getOwnerBean() {
|
||||
public EntityBean getOwnerBean() {
|
||||
return ownerBean;
|
||||
}
|
||||
|
||||
@@ -128,7 +118,14 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
checkEmptyLazyLoad();
|
||||
}
|
||||
|
||||
protected void touched() {
|
||||
/**
|
||||
* Set touched. If setFlag is false then typically an isEmpty() call and still
|
||||
* considering that to be untouched.
|
||||
*/
|
||||
protected void touched(boolean setFlag) {
|
||||
if (setFlag) {
|
||||
touched = true;
|
||||
}
|
||||
if (beanCollectionTouched != null) {
|
||||
// only call this once
|
||||
beanCollectionTouched.notifyTouched(this);
|
||||
@@ -174,46 +171,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
this.hasMoreRows = hasMoreRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the fetch has finished. False if the fetch is continuing in
|
||||
* a background thread.
|
||||
*/
|
||||
public boolean isFinishedFetch() {
|
||||
return finishedFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true when a fetch has finished. Used when a fetch continues in the
|
||||
* background.
|
||||
*/
|
||||
public void setFinishedFetch(boolean finishedFetch) {
|
||||
this.finishedFetch = finishedFetch;
|
||||
}
|
||||
|
||||
public void setBackgroundFetch(Future<Integer> fetchFuture) {
|
||||
this.fetchFuture = fetchFuture;
|
||||
}
|
||||
|
||||
public void backgroundFetchWait(long wait, TimeUnit timeUnit) {
|
||||
if (fetchFuture != null) {
|
||||
try {
|
||||
fetchFuture.get(wait, timeUnit);
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void backgroundFetchWait() {
|
||||
if (fetchFuture != null) {
|
||||
try {
|
||||
fetchFuture.get();
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkReadOnly() {
|
||||
if (readOnly) {
|
||||
String msg = "This collection is in ReadOnly mode";
|
||||
|
||||
@@ -10,13 +10,15 @@ import java.util.ListIterator;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* List capable of lazy loading.
|
||||
*/
|
||||
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>,
|
||||
BeanCollectionAdd {
|
||||
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The underlying List implementation.
|
||||
*/
|
||||
@@ -40,17 +42,25 @@ 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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (list == null) {
|
||||
list = new ArrayList<E>();
|
||||
}
|
||||
list.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -72,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +124,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
public Collection<E> getActualDetails() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying list.
|
||||
@@ -249,7 +272,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,15 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Map capable of lazy loading.
|
||||
*/
|
||||
public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Map<K, E> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The underlying map implementation.
|
||||
*/
|
||||
@@ -33,10 +36,22 @@ 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) {
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<K, E>();
|
||||
}
|
||||
map.put((K)key, (E)bean);
|
||||
}
|
||||
|
||||
public void internalAdd(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
@@ -75,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,14 +127,23 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map entrySet iterator.
|
||||
* Returns the collection of beans (map values).
|
||||
*/
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the map entrySet.
|
||||
* <p>
|
||||
* This is because the key values may need to be set against the details (so
|
||||
* they don't need to be set twice).
|
||||
* </p>
|
||||
*/
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
public Collection<?> getActualEntries() {
|
||||
return map.entrySet();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +224,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ 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 {
|
||||
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The underlying Set implementation.
|
||||
@@ -34,17 +36,24 @@ 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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet<E>();
|
||||
}
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -81,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +130,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying set.
|
||||
*/
|
||||
@@ -210,7 +232,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,25 +36,31 @@ public class DataSourceConfig {
|
||||
|
||||
private int heartbeatFreqSecs = 30;
|
||||
|
||||
private int heartbeatTimeoutSeconds = 3;
|
||||
|
||||
private boolean captureStackTrace;
|
||||
|
||||
private int maxStackTraceSize = 5;
|
||||
|
||||
private int leakTimeMinutes = 30;
|
||||
|
||||
private int maxInactiveTimeSecs = 900;
|
||||
private int maxInactiveTimeSecs = 720;
|
||||
|
||||
private int maxAgeMinutes = 0;
|
||||
|
||||
private int trimPoolFreqSecs = 59;
|
||||
|
||||
private int pstmtCacheSize = 20;
|
||||
|
||||
private int cstmtCacheSize = 20;
|
||||
|
||||
private int waitTimeoutMillis = 1000;
|
||||
|
||||
|
||||
private String poolListener;
|
||||
|
||||
private boolean offline;
|
||||
|
||||
Map<String, String> customProperties;
|
||||
|
||||
protected Map<String, String> customProperties;
|
||||
|
||||
/**
|
||||
* Return the connection URL.
|
||||
@@ -194,6 +200,20 @@ public class DataSourceConfig {
|
||||
public void setHeartbeatFreqSecs(int heartbeatFreqSecs) {
|
||||
this.heartbeatFreqSecs = heartbeatFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the heart beat timeout in seconds.
|
||||
*/
|
||||
public int getHeartbeatTimeoutSeconds() {
|
||||
return heartbeatTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the heart beat timeout in seconds.
|
||||
*/
|
||||
public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) {
|
||||
this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a stack trace should be captured when obtaining a connection
|
||||
@@ -309,6 +329,23 @@ public class DataSourceConfig {
|
||||
return maxInactiveTimeSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum age a connection is allowed to be before it is closed.
|
||||
* <p>
|
||||
* This can be used to close really old connections.
|
||||
* </p>
|
||||
*/
|
||||
public int getMaxAgeMinutes() {
|
||||
return maxAgeMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum age a connection can be in minutes.
|
||||
*/
|
||||
public void setMaxAgeMinutes(int maxAgeMinutes) {
|
||||
this.maxAgeMinutes = maxAgeMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in seconds a connection can be idle after which it can be
|
||||
* trimmed from the pool.
|
||||
@@ -321,6 +358,25 @@ public class DataSourceConfig {
|
||||
this.maxInactiveTimeSecs = maxInactiveTimeSecs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the minimum time gap between pool trim checks.
|
||||
* <p>
|
||||
* This defaults to 59 seconds meaning that the pool trim check will run every
|
||||
* minute assuming the heart beat check runs every 30 seconds.
|
||||
* </p>
|
||||
*/
|
||||
public int getTrimPoolFreqSecs() {
|
||||
return trimPoolFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum trim gap between pool trim checks.
|
||||
*/
|
||||
public void setTrimPoolFreqSecs(int trimPoolFreqSecs) {
|
||||
this.trimPoolFreqSecs = trimPoolFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pool listener.
|
||||
*/
|
||||
@@ -359,7 +415,7 @@ public class DataSourceConfig {
|
||||
public void setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a map of custom properties for the jdbc driver connection.
|
||||
*/
|
||||
@@ -388,18 +444,18 @@ public class DataSourceConfig {
|
||||
this.username = properties.get(prefix + "username", null);
|
||||
this.password = properties.get(prefix + "password", null);
|
||||
|
||||
String v;
|
||||
String dbDriver = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", dbDriver);
|
||||
|
||||
v = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", v);
|
||||
|
||||
v = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", v);
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
|
||||
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
|
||||
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
|
||||
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 900);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
|
||||
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
|
||||
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
|
||||
|
||||
this.minConnections = properties.getInt(prefix + "minConnections", 0);
|
||||
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
|
||||
@@ -409,6 +465,7 @@ public class DataSourceConfig {
|
||||
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
|
||||
|
||||
this.heartbeatSql = properties.get(prefix + "heartbeatSql", null);
|
||||
this.heartbeatTimeoutSeconds = properties.getInt(prefix + "heartbeatTimeoutSeconds", 3);
|
||||
this.poolListener = properties.get(prefix + "poolListener", null);
|
||||
this.offline = properties.getBoolean(prefix + "offline", false);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.BulkTableEventListener;
|
||||
import com.avaje.ebean.event.ServerConfigStartup;
|
||||
import com.avaje.ebean.event.TransactionEventListener;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
|
||||
/**
|
||||
@@ -65,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;
|
||||
|
||||
/**
|
||||
@@ -111,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;
|
||||
|
||||
/**
|
||||
@@ -129,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;
|
||||
@@ -151,26 +165,51 @@ public class ServerConfig {
|
||||
*/
|
||||
private PstmtDelegate pstmtDelegate;
|
||||
|
||||
/** The data source. */
|
||||
/**
|
||||
* The data source (if programmatically provided).
|
||||
*/
|
||||
private DataSource dataSource;
|
||||
|
||||
/** The data source config. */
|
||||
/**
|
||||
* The data source config.
|
||||
*/
|
||||
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
|
||||
|
||||
/** The data source jndi name. */
|
||||
/**
|
||||
* The data source JNDI name if using a JNDI DataSource.
|
||||
*/
|
||||
private String dataSourceJndiName;
|
||||
|
||||
/** The database boolean true. */
|
||||
/**
|
||||
* The database boolean true value (typically either 1, T, or Y).
|
||||
*/
|
||||
private String databaseBooleanTrue;
|
||||
|
||||
/** The database boolean false. */
|
||||
/**
|
||||
* The database boolean false value (typically either 0, F or N).
|
||||
*/
|
||||
private String databaseBooleanFalse;
|
||||
|
||||
/** The naming convention. */
|
||||
/**
|
||||
* The naming convention.
|
||||
*/
|
||||
private NamingConvention namingConvention;
|
||||
|
||||
/** The update changes only. */
|
||||
/**
|
||||
* Behaviour of update to include on the change properties.
|
||||
*/
|
||||
private boolean updateChangesOnly = true;
|
||||
|
||||
/**
|
||||
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
|
||||
*/
|
||||
private boolean updatesDeleteMissingChildren = true;
|
||||
|
||||
/**
|
||||
* Setting to indicate if UUID should be stored as binary(16) or varchar(40).
|
||||
*/
|
||||
private boolean uuidStoreAsBinary;
|
||||
|
||||
|
||||
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
|
||||
private List<BeanPersistListener<?>> persistListeners = new ArrayList<BeanPersistListener<?>>();
|
||||
@@ -191,6 +230,10 @@ public class ServerConfig {
|
||||
|
||||
private ServerCacheManager serverCacheManager;
|
||||
|
||||
private boolean collectQueryStatsByNode;
|
||||
|
||||
private boolean collectQueryOrigins;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an
|
||||
* EbeanServer.
|
||||
@@ -759,6 +802,21 @@ public class ServerConfig {
|
||||
this.dbEncrypt = dbEncrypt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
|
||||
*/
|
||||
public boolean isUuidStoreAsBinary() {
|
||||
return uuidStoreAsBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
|
||||
*/
|
||||
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
|
||||
this.uuidStoreAsBinary = uuidStoreAsBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to run the DDL generation on startup.
|
||||
*/
|
||||
@@ -926,6 +984,66 @@ 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.
|
||||
*/
|
||||
public boolean isCollectQueryStatsByNode() {
|
||||
return collectQueryStatsByNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to collection query execution statistics by ObjectGraphNode.
|
||||
* <p>
|
||||
* These statistics can be used to highlight code/query 'origin points' that result in lots of lazy loading.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is considered safe/fine to have this set to true for production.
|
||||
* </p>
|
||||
* <p>
|
||||
* This information can be later retrieved via {@link MetaInfoManager}.
|
||||
* </p>
|
||||
* @see MetaInfoManager
|
||||
*/
|
||||
public void setCollectQueryStatsByNode(boolean collectQueryStatsByNode) {
|
||||
this.collectQueryStatsByNode = collectQueryStatsByNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if query plans should also collect their 'origins'. This means for a given query plan you
|
||||
* can identify the code/origin points where this query resulted from including lazy loading origins.
|
||||
*/
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if query plans should collect their 'origin' points. This means for a given query plan you
|
||||
* can identify the code/origin points where this query resulted from including lazy loading origins.
|
||||
* <p>
|
||||
* This information can be later retrieved via {@link MetaInfoManager}.
|
||||
* </p>
|
||||
* @see MetaInfoManager
|
||||
*/
|
||||
public void setCollectQueryOrigins(boolean collectQueryOrigins) {
|
||||
this.collectQueryOrigins = collectQueryOrigins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource directory.
|
||||
@@ -1183,7 +1301,13 @@ public class ServerConfig {
|
||||
packages = getSearchJarsPackages(packagesProp);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1196,6 +1320,7 @@ public class ServerConfig {
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
|
||||
databasePlatformName = p.get("databasePlatformName", null);
|
||||
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
|
||||
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
|
||||
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Used to enhance or override the default bean persistence mechanism.
|
||||
* <p>
|
||||
* Note that if want to totally change the finding, you need to use a BeanFinder
|
||||
* Note that if want to totally change the finding, you need to use a BeanQueryAdapter
|
||||
* rather than using postLoad().
|
||||
* </p>
|
||||
* <p>
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.avaje.ebean.event;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Listens for committed bean events.
|
||||
* <p>
|
||||
@@ -31,8 +33,9 @@ import java.util.Set;
|
||||
* </p>
|
||||
* <p>
|
||||
* A BeanPersistListener is either found automatically via class path search or
|
||||
* can be added programmatically via ServerConfiguration.addEntity().
|
||||
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}.
|
||||
* </p>
|
||||
* @see ServerConfig#add(BeanPersistListener)
|
||||
*/
|
||||
public interface BeanPersistListener<T> {
|
||||
|
||||
@@ -52,7 +55,7 @@ public interface BeanPersistListener<T> {
|
||||
* @param bean
|
||||
* The bean that was updated.
|
||||
* @param updatedProperties
|
||||
* the properties on the bean that where updated
|
||||
* The properties that were modified by this update.
|
||||
*/
|
||||
public boolean updated(T bean, Set<String> updatedProperties);
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.avaje.ebean.event;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
/**
|
||||
* Holds the information available for a bean persist (insert, update or
|
||||
@@ -41,12 +43,8 @@ public interface BeanPersistRequest<T> {
|
||||
public T getBean();
|
||||
|
||||
/**
|
||||
* Returns a bean containing the original values prior to the bean being
|
||||
* modified.
|
||||
* <p>
|
||||
* This is for updates only.
|
||||
* </p>
|
||||
* Returns a map of the properties that have changed and their new and old values.
|
||||
*/
|
||||
public T getOldValues();
|
||||
public Map<String,ValuePair> getUpdatedValues();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
|
||||
/**
|
||||
* Statistics collected by AutoFetch profiling.
|
||||
*/
|
||||
@Entity
|
||||
public class MetaAutoFetchStatistic implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6640406753257176803L;
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private ObjectGraphOrigin origin;
|
||||
|
||||
private String beanType;
|
||||
|
||||
private int counter;
|
||||
|
||||
@Transient
|
||||
private List<QueryStats> queryStats;
|
||||
|
||||
@Transient
|
||||
private List<NodeUsageStats> nodeUsageStats;
|
||||
|
||||
public MetaAutoFetchStatistic() {
|
||||
}
|
||||
|
||||
public MetaAutoFetchStatistic(ObjectGraphOrigin origin, int counter, List<QueryStats> queryStats,
|
||||
List<NodeUsageStats> nodeUsageStats) {
|
||||
|
||||
this.origin = origin;
|
||||
this.beanType = origin == null ? null : origin.getBeanType();
|
||||
this.id = origin == null ? null : origin.getKey();
|
||||
this.counter = counter;
|
||||
this.queryStats = queryStats;
|
||||
this.nodeUsageStats = nodeUsageStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the query point key.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean type.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query point.
|
||||
*/
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of profiled queries the statistics is based on.
|
||||
*/
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query execution statistics.
|
||||
*/
|
||||
public List<QueryStats> getQueryStats() {
|
||||
return queryStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the node usage statistics.
|
||||
*/
|
||||
public List<NodeUsageStats> getNodeUsageStats() {
|
||||
return nodeUsageStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: This will likely be deprecated in favour of a separate object graph
|
||||
* cost.
|
||||
*/
|
||||
public static class QueryStats implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5517935732867671387L;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final int exeCount;
|
||||
|
||||
private final int totalBeanLoaded;
|
||||
|
||||
private final int totalMicros;
|
||||
|
||||
public QueryStats(String path, int exeCount, int totalBeanLoaded, int totalMicros) {
|
||||
this.path = path;
|
||||
this.exeCount = exeCount;
|
||||
this.totalBeanLoaded = totalBeanLoaded;
|
||||
this.totalMicros = totalMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path. This is empty string for the origin query and otherwise
|
||||
* the path for the associated lazy loading queries.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of queries executed.
|
||||
*/
|
||||
public int getExeCount() {
|
||||
return exeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of beans loaded by the query.
|
||||
*/
|
||||
public int getTotalBeanLoaded() {
|
||||
return totalBeanLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total time in microseconds of the queries.
|
||||
*/
|
||||
public int getTotalMicros() {
|
||||
return totalMicros;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount;
|
||||
|
||||
return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded["
|
||||
+ totalBeanLoaded + "] avgMicros[" + avgMicros + "] totalMicros[" + totalMicros
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects usages statistics for a given node in the object graph.
|
||||
*/
|
||||
public static class NodeUsageStats implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1786787832374844739L;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final int profileCount;
|
||||
|
||||
private final int profileUsedCount;
|
||||
|
||||
private final String[] usedProperties;
|
||||
|
||||
public NodeUsageStats(String path, int profileCount, int profileUsedCount,
|
||||
String[] usedProperties) {
|
||||
this.path = path == null ? "" : path;
|
||||
this.profileCount = profileCount;
|
||||
this.profileUsedCount = profileUsedCount;
|
||||
this.usedProperties = usedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path. This is empty string for the origin and otherwise the
|
||||
* path for the associated nodes.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of profiled beans for this node.
|
||||
*/
|
||||
public int getProfileCount() {
|
||||
return profileCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of profiled beans that where actually used for this node.
|
||||
* <p>
|
||||
* The difference between profiled and used could show uneven traversal of
|
||||
* the object graph. UI paging through results means the traversal for the
|
||||
* first x beans can be much higher than the last x beans.
|
||||
* </p>
|
||||
*/
|
||||
public int getProfileUsedCount() {
|
||||
return profileUsedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The properties used at this node.
|
||||
*/
|
||||
public String[] getUsedProperties() {
|
||||
return usedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties as a Set rather than an Array.
|
||||
*/
|
||||
public Set<String> getUsedPropertiesSet() {
|
||||
LinkedHashSet<String> s = new LinkedHashSet<String>();
|
||||
for (int i = 0; i < usedProperties.length; i++) {
|
||||
s.add(usedProperties[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "path[" + path + "] profileCount[" + profileCount + "] used[" + profileUsedCount
|
||||
+ "] props" + Arrays.toString(usedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
|
||||
/**
|
||||
* "Tuned fetch" information used by AutoFetch.
|
||||
* <p>
|
||||
* Note that the queryPoint is effectively the Id field for this bean.
|
||||
* </p>
|
||||
* <p>
|
||||
* The queryPoint identifies both the query and call stack.
|
||||
* </p>
|
||||
*/
|
||||
@Entity
|
||||
public class MetaAutoFetchTunedQueryInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3119991928889170215L;
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String beanType;
|
||||
|
||||
/**
|
||||
* The profile query point (call stack and query).
|
||||
*/
|
||||
private ObjectGraphOrigin origin;
|
||||
|
||||
/**
|
||||
* The tuned query details with joins and properties.
|
||||
*/
|
||||
private String tunedDetail;
|
||||
|
||||
/**
|
||||
* The number of times profiling has been collected for this query point.
|
||||
*/
|
||||
private int profileCount;
|
||||
|
||||
/**
|
||||
* The number of queries tuned by this info.
|
||||
*/
|
||||
private int tunedCount;
|
||||
|
||||
private long lastTuneTime;
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo() {
|
||||
|
||||
}
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo(final ObjectGraphOrigin origin, String tunedDetail,
|
||||
int profileCount, int tunedCount, long lastTuneTime) {
|
||||
|
||||
this.origin = origin;
|
||||
this.beanType = origin == null ? null : origin.getBeanType();
|
||||
this.id = origin == null ? null : origin.getKey();
|
||||
this.tunedDetail = tunedDetail;
|
||||
this.profileCount = profileCount;
|
||||
this.tunedCount = tunedCount;
|
||||
this.lastTuneTime = lastTuneTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query point key.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of bean this is tuned for.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query point.
|
||||
*/
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tuned query detail in string form.
|
||||
*/
|
||||
public String getTunedDetail() {
|
||||
return tunedDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of profiled queries the tuned query is based on.
|
||||
*/
|
||||
public int getProfileCount() {
|
||||
return profileCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of queries tuned.
|
||||
*/
|
||||
public int getTunedCount() {
|
||||
return tunedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time of the last tune (that changed the query).
|
||||
*/
|
||||
public long getLastTuneTime() {
|
||||
return lastTuneTime;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "origin[" + origin + "] query[" + tunedDetail + "] profileCount[" + profileCount + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MetaBeanInfo {
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return the non-empty statistics.
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect the current query plan statistics return all the statistics (include query plans that haven't had query executions).
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the meta data in EbeanServer such as query execution statistics.
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Return the MetaBeanInfo for a bean type.
|
||||
*/
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass);
|
||||
|
||||
/**
|
||||
* Return all the MetaBeanInfo.
|
||||
*/
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList();
|
||||
|
||||
/**
|
||||
* Collect and return the query plan statistics for all the beans.
|
||||
* <p>
|
||||
* Note that this excludes the query plan statistics where there has been no
|
||||
* executions (since the last collection with reset).
|
||||
* </p>
|
||||
*/
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect and return the ObjectGraphNode statistics.
|
||||
* <p>
|
||||
* These show query executions based on an origin point and relative path.
|
||||
* This is used to look at the amount of lazy loading occurring for a given
|
||||
* query origin point and highlight potential for tuning a query.
|
||||
* </p>
|
||||
*
|
||||
* @param reset
|
||||
* Set to true to reset the underlying statistics after collection.
|
||||
*/
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
|
||||
/**
|
||||
* Statistics for query execution based on object graph origin and paths.
|
||||
* <p>
|
||||
* These statistics can be used to identify origin queries that result in lots
|
||||
* of lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaInfoManager#collectNodeStatistics(boolean)
|
||||
*/
|
||||
public interface MetaObjectGraphNodeStats {
|
||||
|
||||
/**
|
||||
* Return the ObjectGraphNode which has the origin point and relative path.
|
||||
*/
|
||||
public ObjectGraphNode getNode();
|
||||
|
||||
/**
|
||||
* Return the startTime of statistics collection.
|
||||
*/
|
||||
public long getStartTime();
|
||||
|
||||
/**
|
||||
* Return the total count of queries executed for this node.
|
||||
*/
|
||||
public long getCount();
|
||||
|
||||
/**
|
||||
* Return the total time of queries executed for this node.
|
||||
*/
|
||||
public long getTotalTime();
|
||||
|
||||
/**
|
||||
* Return the total beans loaded by queries for this node.
|
||||
*/
|
||||
public long getTotalBeans();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
|
||||
/**
|
||||
* Holds a query 'origin' point and count for the number of queries executed for
|
||||
* this 'origin'.
|
||||
* <p>
|
||||
* This basically points to the bit of original code and query that results in
|
||||
* this query directly or via lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaQueryPlanStatistic
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanOriginCount {
|
||||
|
||||
/**
|
||||
* The 'origin' and path which this query belongs to.
|
||||
* <p>
|
||||
* For lazy loading queries this points to the original query and associated
|
||||
* navigation path that resulted in this query being executed.
|
||||
* </p>
|
||||
*/
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
/**
|
||||
* The number of times a query was fired for this node since the counter was
|
||||
* last reset.
|
||||
*/
|
||||
public long getCount();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanStatistic {
|
||||
|
||||
/**
|
||||
* Return the bean type this query plan is for.
|
||||
*/
|
||||
public Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by Autofetch.
|
||||
*/
|
||||
public boolean isAutofetchTuned();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
*/
|
||||
public String getQueryPlanHash();
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
public String getSql();
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
public long getExecutionCount();
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getTotalLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the total time taken by executions of this query.
|
||||
*/
|
||||
public long getTotalTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the max execution time for this query.
|
||||
*/
|
||||
public long getMaxTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the time collection started (or was last reset).
|
||||
*/
|
||||
public long getCollectionStart();
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
public long getLastQueryTime();
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getAvgTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public long getAvgLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the 'origin' points and paths that resulted in the query being
|
||||
* executed and the associated number of times the query was executed via that
|
||||
* path.
|
||||
* <p>
|
||||
* This includes direct and lazy loading paths.
|
||||
* </p>
|
||||
*/
|
||||
public List<MetaQueryPlanOriginCount> getOrigins();
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package com.avaje.ebean.meta;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*/
|
||||
@Entity
|
||||
public class MetaQueryStatistic implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8746524372894472583L;
|
||||
|
||||
boolean autofetchTuned;
|
||||
|
||||
String beanType;
|
||||
|
||||
/**
|
||||
* The original query plan hash (calculated prior to autofetch tuning).
|
||||
*/
|
||||
int origQueryPlanHash;
|
||||
|
||||
/**
|
||||
* The final query plan hash (calculated after to autofetch tuning).
|
||||
*/
|
||||
int finalQueryPlanHash;
|
||||
|
||||
String sql;
|
||||
|
||||
int executionCount;
|
||||
|
||||
int totalLoadedBeans;
|
||||
|
||||
int totalTimeMicros;
|
||||
|
||||
long collectionStart;
|
||||
|
||||
long lastQueryTime;
|
||||
|
||||
int avgTimeMicros;
|
||||
|
||||
int avgLoadedBeans;
|
||||
|
||||
public MetaQueryStatistic() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MetaQueryStatistic.
|
||||
*/
|
||||
public MetaQueryStatistic(boolean autofetchTuned, String beanType, int plan, String sql,
|
||||
int executionCount, int totalLoadedBeans, int totalTimeMicros, long collectionStart,
|
||||
long lastQueryTime) {
|
||||
|
||||
this.autofetchTuned = autofetchTuned;
|
||||
this.beanType = beanType;
|
||||
this.finalQueryPlanHash = plan;
|
||||
this.sql = sql;
|
||||
this.executionCount = executionCount;
|
||||
this.totalLoadedBeans = totalLoadedBeans;
|
||||
this.totalTimeMicros = totalTimeMicros;
|
||||
this.collectionStart = collectionStart;
|
||||
|
||||
this.lastQueryTime = lastQueryTime;
|
||||
this.avgTimeMicros = executionCount == 0 ? 0 : totalTimeMicros / executionCount;
|
||||
this.avgLoadedBeans = executionCount == 0 ? 0 : totalLoadedBeans / executionCount;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "type=" + beanType + " tuned:" + autofetchTuned + " origHash=" + origQueryPlanHash
|
||||
+ " count=" + executionCount + " avgMicros=" + getAvgTimeMicros();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this query plan was built for Autofetch tuned queries.
|
||||
*/
|
||||
public boolean isAutofetchTuned() {
|
||||
return autofetchTuned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original query plan hash (calculated prior to autofetch tuning).
|
||||
* <p>
|
||||
* This will return 0 if there is no autofetch profiling or tuning on this
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public int getOrigQueryPlanHash() {
|
||||
return origQueryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the queryPlanHash value. This is unique for a given query plan.
|
||||
*/
|
||||
public int getFinalQueryPlanHash() {
|
||||
return finalQueryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean type.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
public int getExecutionCount() {
|
||||
return executionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalLoadedBeans() {
|
||||
return totalLoadedBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times this query was executed.
|
||||
*/
|
||||
public int getTotalTimeMicros() {
|
||||
return totalTimeMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time collection started.
|
||||
*/
|
||||
public long getCollectionStart() {
|
||||
return collectionStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
public long getLastQueryTime() {
|
||||
return lastQueryTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getAvgTimeMicros() {
|
||||
return avgTimeMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
public int getAvgLoadedBeans() {
|
||||
return avgLoadedBeans;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Meta data that can be retrieved for the EbeanServer.
|
||||
*/
|
||||
package com.avaje.ebean.meta;
|
||||
@@ -1,17 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Entity Beans for getting "Meta" data from Ebean</title>
|
||||
</head>
|
||||
<body>
|
||||
Entity Beans for getting "Meta" data from Ebean
|
||||
<p>
|
||||
You can query these entity beans to get "meta" data from Ebean.
|
||||
This includes things like query execution statistics.
|
||||
</p>
|
||||
<pre class="code">
|
||||
// fetch the meta data that controls autoFetch query tuning
|
||||
Query<MetaAutoFetchTunedFetch> query = Ebean.createQuery(MetaAutoFetchTunedFetch.class);
|
||||
List<MetaAutoFetchTunedFetch> list = query.findList();
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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.
|
||||
|
||||
@@ -3,8 +3,7 @@ package com.avaje.ebeaninternal.api;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -14,22 +13,16 @@ import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
/**
|
||||
* Parameters used for binding to a statement.
|
||||
* <p>
|
||||
* Used by FindByNativeSql and UpdateSql to support ordered and named
|
||||
* parameters. Note that you can use either ordered OR named parameters.
|
||||
* Supports ordered or named parameters.
|
||||
* </p>
|
||||
*/
|
||||
public class BindParams implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4541081933302086285L;
|
||||
|
||||
private ArrayList<Param> positionedParameters = new ArrayList<Param>();
|
||||
private List<Param> positionedParameters = new ArrayList<Param>();
|
||||
|
||||
private HashMap<String, Param> namedParameters = new HashMap<String, Param>();
|
||||
|
||||
/**
|
||||
* Need to create a hash when binding collection values (for in clauses).
|
||||
*/
|
||||
private int queryPlanHash = 1;
|
||||
private Map<String, Param> namedParameters = new LinkedHashMap<String, Param>();
|
||||
|
||||
/**
|
||||
* This is the sql. For named parameters this is the sql after the named
|
||||
@@ -38,6 +31,40 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
private String preparedSql;
|
||||
|
||||
public BindParams() {
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash that should be included with the query plan.
|
||||
* <p>
|
||||
* This is to handle binding collections to in clauses. The number of values
|
||||
* in the collection effects the query (number of bind values) and so must be
|
||||
* taken into account when calculating the query hash.
|
||||
* </p>
|
||||
*/
|
||||
public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
|
||||
int hc = 31;
|
||||
for (Param param : positionedParameters) {
|
||||
hc = hc * 31 + param.queryBindCount();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
hc = hc * 31 + entry.getKey().hashCode();
|
||||
hc = hc * 31 + entry.getValue().queryBindCount();
|
||||
}
|
||||
|
||||
int bindCount = positionedParameters.size() + namedParameters.size();
|
||||
builder.add(hc).bind(bindCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a deep copy of the BindParams.
|
||||
*/
|
||||
@@ -46,45 +73,12 @@ public class BindParams implements Serializable {
|
||||
for (Param p : positionedParameters) {
|
||||
copy.positionedParameters.add(p.copy());
|
||||
}
|
||||
Iterator<Entry<String, Param>> it = namedParameters.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, Param> entry = (Map.Entry<String, Param>) it.next();
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
for (Entry<String, Param> entry : namedParameters.entrySet()) {
|
||||
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = getClass().hashCode();
|
||||
hc = hc * 31 + namedParameters.hashCode();
|
||||
for (int i = 0; i < positionedParameters.size(); i++) {
|
||||
hc = hc * 31 + positionedParameters.get(i).hashCode();
|
||||
}
|
||||
hc = hc * 31 + (preparedSql == null ? 0 : preparedSql.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof BindParams) {
|
||||
return hashCode() == o.hashCode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no bind parameters.
|
||||
*/
|
||||
@@ -131,10 +125,8 @@ public class BindParams implements Serializable {
|
||||
* Set an In Out parameter using position.
|
||||
*/
|
||||
public void setParameter(int position, Object value, int outType) {
|
||||
|
||||
addToQueryPlanHash(String.valueOf(position), value);
|
||||
|
||||
Param p = getParam(position);
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
@@ -144,10 +136,8 @@ public class BindParams implements Serializable {
|
||||
* must use setNullParameter.
|
||||
*/
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
addToQueryPlanHash(String.valueOf(position), value);
|
||||
|
||||
Param p = getParam(position);
|
||||
Param p = getParam(position);
|
||||
p.setInValue(value);
|
||||
}
|
||||
|
||||
@@ -182,10 +172,8 @@ public class BindParams implements Serializable {
|
||||
* Set a named In Out parameter.
|
||||
*/
|
||||
public void setParameter(String name, Object value, int outType) {
|
||||
|
||||
addToQueryPlanHash(name, value);
|
||||
|
||||
Param p = getParam(name);
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
p.setOutType(outType);
|
||||
}
|
||||
@@ -203,48 +191,22 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
public Param setParameter(String name, Object value) {
|
||||
|
||||
addToQueryPlanHash(name, value);
|
||||
|
||||
Param p = getParam(name);
|
||||
Param p = getParam(name);
|
||||
p.setInValue(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* For binding collections calculate a hash to be used for the query plan.
|
||||
*/
|
||||
private void addToQueryPlanHash(String name, Object value){
|
||||
if (value != null){
|
||||
if (value instanceof Collection<?>){
|
||||
queryPlanHash = queryPlanHash * 31 + name.hashCode();
|
||||
queryPlanHash = queryPlanHash * 31 + ((Collection<?>)value).size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash that should be included with the query plan.
|
||||
* <p>
|
||||
* This is to handle binding collections to in clauses. The number
|
||||
* of values in the collection effects the query (number of bind values)
|
||||
* and so must be taken into account when calculating the query hash.
|
||||
* </p>
|
||||
*/
|
||||
public int getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an encryption key as a bind value.
|
||||
* <p>
|
||||
* Needs special treatment as the value should not be included in a log.
|
||||
* </p>
|
||||
*/
|
||||
public Param setEncryptionKey(String name, Object value) {
|
||||
Param p = getParam(name);
|
||||
p.setEncryptionKey(value);
|
||||
return p;
|
||||
}
|
||||
/**
|
||||
* Set an encryption key as a bind value.
|
||||
* <p>
|
||||
* Needs special treatment as the value should not be included in a log.
|
||||
* </p>
|
||||
*/
|
||||
public Param setEncryptionKey(String name, Object value) {
|
||||
Param p = getParam(name);
|
||||
p.setEncryptionKey(value);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the named parameter as an Out parameter.
|
||||
@@ -300,9 +262,9 @@ public class BindParams implements Serializable {
|
||||
*/
|
||||
public static final class OrderedList {
|
||||
|
||||
final List<Param> paramList;
|
||||
private final List<Param> paramList;
|
||||
|
||||
final StringBuilder preparedSql;
|
||||
private final StringBuilder preparedSql;
|
||||
|
||||
public OrderedList() {
|
||||
this(new ArrayList<Param>());
|
||||
@@ -373,6 +335,16 @@ public class BindParams implements Serializable {
|
||||
public Param() {
|
||||
}
|
||||
|
||||
public int queryBindCount() {
|
||||
if (inValue == null) {
|
||||
return 0;
|
||||
}
|
||||
if (inValue instanceof Collection<?>){
|
||||
return ((Collection<?>)inValue).size();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy of the Param.
|
||||
*/
|
||||
@@ -448,14 +420,14 @@ public class BindParams implements Serializable {
|
||||
this.isInParam = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an encryption key (which can not be logged).
|
||||
*/
|
||||
public void setEncryptionKey(Object in) {
|
||||
this.inValue = in;
|
||||
this.isInParam = true;
|
||||
this.encryptionKey = true;
|
||||
}
|
||||
/**
|
||||
* Set an encryption key (which can not be logged).
|
||||
*/
|
||||
public void setEncryptionKey(Object in) {
|
||||
this.inValue = in;
|
||||
this.isInParam = true;
|
||||
this.encryptionKey = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the In parameter is NULL and the specific type that it
|
||||
@@ -506,12 +478,12 @@ public class BindParams implements Serializable {
|
||||
this.textLocation = textLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true do not include this value in a transaction log.
|
||||
*/
|
||||
public boolean isEncryptionKey() {
|
||||
return encryptionKey;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash key for a query including both the query plan and bind values.
|
||||
*/
|
||||
public class HashQuery {
|
||||
|
||||
private final HashQueryPlan planHash;
|
||||
|
||||
private final int bindHash;
|
||||
|
||||
/**
|
||||
* Create the HashQuery.
|
||||
*/
|
||||
public HashQuery(HashQueryPlan planHash, int bindHash) {
|
||||
this.planHash = planHash;
|
||||
this.bindHash = bindHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan hash.
|
||||
*/
|
||||
public HashQueryPlan getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind values hash.
|
||||
*/
|
||||
public int getBindHash() {
|
||||
return bindHash;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * planHash.hashCode();
|
||||
hc = 31 * hc + bindHash;
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQuery)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQuery e = (HashQuery) obj;
|
||||
return e.bindHash == bindHash && e.planHash.equals(planHash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* A hash for a query plan.
|
||||
*/
|
||||
public class HashQueryPlan {
|
||||
|
||||
private final String rawSql;
|
||||
|
||||
private final int planHash;
|
||||
|
||||
private final int bindCount;
|
||||
|
||||
public HashQueryPlan(String rawSql, int planHash, int bindCount) {
|
||||
this.rawSql = rawSql;
|
||||
this.planHash = planHash;
|
||||
this.bindCount = bindCount;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = planHash;
|
||||
hc = hc * 31 + bindCount;
|
||||
hc = hc * 31 + (rawSql == null ? 0 : rawSql.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HashQueryPlan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HashQueryPlan e = (HashQueryPlan) obj;
|
||||
return e.planHash == planHash
|
||||
&& e.bindCount == bindCount
|
||||
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
public class HashQueryPlanBuilder {
|
||||
|
||||
private int planHash;
|
||||
|
||||
private int bindCount;
|
||||
|
||||
private String rawSql;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 31;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 31 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 31 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 31 + (booleanValue ? 31 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a number to the bind count for the hash.
|
||||
*/
|
||||
public HashQueryPlanBuilder bind(int extraBindCount) {
|
||||
bindCount += extraBindCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add raw sql to the hash.
|
||||
*/
|
||||
public HashQueryPlanBuilder addRawSql(String rawSql) {
|
||||
this.rawSql = rawSql;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public HashQueryPlan build() {
|
||||
return new HashQueryPlan(rawSql, planHash, bindCount);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* A buffer of beans for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadBeanBuffer {
|
||||
|
||||
public List<EntityBeanIntercept> getBatch();
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
public String getFullPath();
|
||||
|
||||
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
}
|
||||
@@ -1,39 +1,10 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Controls the loading of ManyToOne and OneToOne relationships.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LoadBeanContext extends LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Configure the query to load beans for this node/path.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
|
||||
|
||||
/**
|
||||
* Return the full path of this node from the root object.
|
||||
*/
|
||||
public String getFullPath();
|
||||
|
||||
/**
|
||||
* Return the persistence context used for all queries
|
||||
* related to this object graph.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for beans for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the batchSize used for lazy loading beans.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,62 +2,61 @@ 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.
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanContext loadContext;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanContext loadContext, List<EntityBeanIntercept> batch,
|
||||
Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
|
||||
super(transaction, batchSize, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = batch;
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
public String getDescription() {
|
||||
String fullPath = loadContext.getFullPath();
|
||||
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
|
||||
+ batch.size();
|
||||
return s;
|
||||
}
|
||||
private final LoadBeanBuffer LoadBuffer;
|
||||
|
||||
/**
|
||||
* Return the batch of beans to actually load.
|
||||
*/
|
||||
public List<EntityBeanIntercept> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanContext getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, 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;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + LoadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch of beans to actually load.
|
||||
*/
|
||||
public List<EntityBeanIntercept> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanBuffer getLoadContext() {
|
||||
return LoadBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that invoked the lazy loading.
|
||||
*/
|
||||
public String getLazyLoadProperty() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
/**
|
||||
* A buffer of bean collections for batch lazy loading and secondary query loading.
|
||||
*/
|
||||
public interface LoadManyBuffer {
|
||||
|
||||
public List<BeanCollection<?>> getBatch();
|
||||
|
||||
public BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
public String getFullPath();
|
||||
|
||||
public void configureQuery(SpiQuery<?> query);
|
||||
|
||||
}
|
||||
@@ -1,54 +1,9 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
/**
|
||||
* Controls the loading of OneToMany and ManyToMany relationships.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface LoadManyContext extends LoadSecondaryQuery {
|
||||
|
||||
/**
|
||||
* Configure the query to load beans for this node/path.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Return the full path of this node from the root object.
|
||||
*/
|
||||
public String getFullPath();
|
||||
|
||||
/**
|
||||
* Return the node location for this node/path.
|
||||
*/
|
||||
public ObjectGraphNode getObjectGraphNode();
|
||||
|
||||
|
||||
/**
|
||||
* Return the persistence context used for all queries
|
||||
* related to this object graph.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext();
|
||||
|
||||
/**
|
||||
* Return the batchSize used for lazy loading beans.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for beans for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return the associated Many bean property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getBeanProperty();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,73 +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 LoadManyContext loadContext;
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadManyRequest(LoadManyContext loadContext,
|
||||
List<BeanCollection<?>> batch, Transaction transaction,
|
||||
int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) {
|
||||
private final boolean loadCache;
|
||||
|
||||
super(transaction, batchSize, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = batch;
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = 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) {
|
||||
|
||||
public String getDescription() {
|
||||
String fullPath = loadContext.getFullPath();
|
||||
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
|
||||
+ batch.size();
|
||||
return s;
|
||||
}
|
||||
super(parentRequest, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = loadContext.getBatch();
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch of collections to actually load.
|
||||
*/
|
||||
public List<BeanCollection<?>> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
public String getDescription() {
|
||||
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyContext getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
/**
|
||||
* Return the batch of collections to actually load.
|
||||
*/
|
||||
public List<BeanCollection<?>> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 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,26 +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 int batchSize;
|
||||
protected final Transaction transaction;
|
||||
|
||||
protected final Transaction transaction;
|
||||
protected final boolean lazy;
|
||||
|
||||
public LoadRequest(Transaction transaction, int batchSize, boolean lazy) {
|
||||
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
|
||||
|
||||
this.transaction = transaction;
|
||||
this.batchSize = batchSize;
|
||||
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.
|
||||
*/
|
||||
@@ -28,13 +38,6 @@ public abstract class LoadRequest {
|
||||
return lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the requested batch size.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction to use if this is a secondary query.
|
||||
* <p>
|
||||
|
||||
@@ -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,7 +19,31 @@ 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.
|
||||
@@ -30,10 +56,10 @@ public class ManyWhereJoins implements Serializable {
|
||||
join = addManyToJoin(join, p.getName());
|
||||
}
|
||||
if (join != null){
|
||||
joins.add(join);
|
||||
addJoin(join);
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
joins.add(join+"."+secondaryTableJoinPrefix);
|
||||
addJoin(join+"."+secondaryTableJoinPrefix);
|
||||
}
|
||||
addParentJoins(join);
|
||||
}
|
||||
@@ -54,11 +80,16 @@ public class ManyWhereJoins implements Serializable {
|
||||
private void addParentJoins(String join) {
|
||||
String[] split = SplitName.split(join);
|
||||
if (split[0] != null){
|
||||
joins.add(split[0]);
|
||||
addJoin(split[0]);
|
||||
addParentJoins(split[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void addJoin(String property) {
|
||||
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
|
||||
joins.put(property, new PropertyJoin(property, joinType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no extra many where joins.
|
||||
*/
|
||||
@@ -69,8 +100,52 @@ 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.
|
||||
*/
|
||||
public void addFormulaWithJoin(String propertyName) {
|
||||
if (formulaWithJoin) {
|
||||
formulaProperties.append(",");
|
||||
} else {
|
||||
formulaProperties = new StringBuilder();
|
||||
formulaWithJoin = true;
|
||||
}
|
||||
formulaProperties.append(propertyName);
|
||||
}
|
||||
|
||||
public boolean isHasMany() {
|
||||
return formulaWithJoin || !joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the findRowCount query just needs the id property in the select clause.
|
||||
*/
|
||||
public boolean isSelectId() {
|
||||
return !formulaWithJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formula properties to build the select clause for a findRowCount query.
|
||||
*/
|
||||
public String getFormulaProperties() {
|
||||
return formulaProperties.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import com.avaje.ebean.TxScope;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanLoader;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
@@ -30,15 +32,14 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
public void shutdownManaged();
|
||||
|
||||
/**
|
||||
* Return true if DeleteMissingChildren defaults to true for stateless
|
||||
* updates.
|
||||
* Return true if query origins should be collected.
|
||||
*/
|
||||
public boolean isDefaultDeleteMissingChildren();
|
||||
|
||||
public boolean isCollectQueryOrigins();
|
||||
|
||||
/**
|
||||
* Return true if UpdateNullProperties defaults to true for stateless updates.
|
||||
* Return the server configuration.
|
||||
*/
|
||||
public boolean isDefaultUpdateNullProperties();
|
||||
public ServerConfig getServerConfig();
|
||||
|
||||
/**
|
||||
* Return the DatabasePlatform for this server.
|
||||
@@ -187,4 +188,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
public boolean isSupportedType(java.lang.reflect.Type genericType);
|
||||
|
||||
/**
|
||||
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
|
||||
*/
|
||||
public void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
@@ -26,7 +25,7 @@ public interface SpiExpression extends Expression {
|
||||
* from an AutoFetch perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutoFetchHash();
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Calculate a hash value for the expression.
|
||||
@@ -37,7 +36,7 @@ public interface SpiExpression extends Expression {
|
||||
* case the query execution plan can be reused.
|
||||
* </p>
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Return the hash value for the values that will be bound.
|
||||
|
||||
@@ -8,6 +8,6 @@ public interface SpiExpressionFactory extends ExpressionFactory {
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
public ExpressionFactory createExpressionFactory(FilterExprPath prefix);
|
||||
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,24 +18,23 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
public List<SpiExpression> getUnderlyingList();
|
||||
|
||||
/**
|
||||
* Trim the path for filterMany() expressions.
|
||||
*/
|
||||
public void trimPath(int prefixTrim);
|
||||
/**
|
||||
* Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
|
||||
*/
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim);
|
||||
|
||||
/**
|
||||
* Restore the ExpressionFactory after deserialisation.
|
||||
*/
|
||||
public void setExpressionFactory(ExpressionFactory expr);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent
|
||||
* join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
|
||||
/**
|
||||
* Return true if this list is empty.
|
||||
@@ -62,10 +61,10 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
public ArrayList<Object> buildBindValues(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -15,6 +14,7 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
@@ -79,11 +79,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
SUBQUERY
|
||||
}
|
||||
|
||||
/**
|
||||
* Set total hits when querying against lucene.
|
||||
*/
|
||||
public void setTotalHits(int totalHits);
|
||||
|
||||
/**
|
||||
* Return true if select all properties was used to ensure the property
|
||||
@@ -153,11 +148,23 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
public String getLoadMode();
|
||||
|
||||
/**
|
||||
* This becomes a lazy loading query for a many relationship.
|
||||
*/
|
||||
public void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many);
|
||||
|
||||
/**
|
||||
* Return the lazy loading 'many' property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
|
||||
|
||||
/**
|
||||
* Return the list of parent Id's for lazy loading.
|
||||
*/
|
||||
public List<Object> getLazyLoadForParentIds();
|
||||
|
||||
/**
|
||||
* Set the load mode (+lazy or +query) and the load description.
|
||||
*
|
||||
* @param loadMode
|
||||
* @param loadDescription
|
||||
*/
|
||||
public void setLoadDescription(String loadMode, String loadDescription);
|
||||
|
||||
@@ -253,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
|
||||
@@ -341,7 +363,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* tuning/modifying the query.
|
||||
* </p>
|
||||
*/
|
||||
public int queryAutofetchHash();
|
||||
public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Identifies queries that are the same bar the bind variables.
|
||||
@@ -354,7 +376,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* Excludes the actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
public int queryPlanHash(BeanQueryRequest<?> request);
|
||||
public HashQueryPlan queryPlanHash(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
@@ -368,7 +390,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Identifies queries that are exactly the same including bind variables.
|
||||
*/
|
||||
public int queryHash();
|
||||
public HashQuery queryHash();
|
||||
|
||||
/**
|
||||
* Return true if this is a query based on a SqlSelect rather than
|
||||
@@ -501,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.
|
||||
*/
|
||||
@@ -537,19 +553,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
public Object getId();
|
||||
|
||||
/**
|
||||
* Return the queryListener.
|
||||
*/
|
||||
public QueryListener<T> getListener();
|
||||
|
||||
/**
|
||||
* Return true if this query should use its own transaction.
|
||||
* <p>
|
||||
* This is true for background fetching and when using QueryListener.
|
||||
* </p>
|
||||
*/
|
||||
public boolean createOwnTransaction();
|
||||
|
||||
/**
|
||||
* Set the generated sql for debug purposes.
|
||||
*
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.persist.dml.DmlHandler;
|
||||
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
|
||||
|
||||
@@ -21,20 +21,20 @@ import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
|
||||
*/
|
||||
public interface SpiUpdatePlan {
|
||||
|
||||
/**
|
||||
* Return true if the set clause has no columns.
|
||||
* <p>
|
||||
* Can occur when the only columns updated have a updatable=false in their
|
||||
* deployment.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isEmptySetClause();
|
||||
/**
|
||||
* Return true if the set clause has no columns.
|
||||
* <p>
|
||||
* Can occur when the only columns updated have a updatable=false in their
|
||||
* deployment.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isEmptySetClause();
|
||||
|
||||
/**
|
||||
* Bind given the request and bean. The bean could be the oldValues bean
|
||||
* when binding a update or delete where clause with ALL concurrency mode.
|
||||
*/
|
||||
public void bindSet(DmlHandler bind, Object bean) throws SQLException;
|
||||
public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return the time this plan was created.
|
||||
@@ -66,10 +66,10 @@ public interface SpiUpdatePlan {
|
||||
*/
|
||||
public Bindable getSet();
|
||||
|
||||
/**
|
||||
* Return the properties that where changed and should be included in the
|
||||
* update statement.
|
||||
*/
|
||||
public Set<String> getProperties();
|
||||
// /**
|
||||
// * Return the properties that where changed and should be included in the
|
||||
// * update statement.
|
||||
// */
|
||||
// public Set<String> getProperties();
|
||||
|
||||
}
|
||||
@@ -224,7 +224,7 @@ public interface AutoFetchManager extends NodeUsageListener {
|
||||
* @param micros
|
||||
* the query executing time in microseconds
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,6 +61,7 @@ public class AutoFetchManagerFactory {
|
||||
FileInputStream fi = new FileInputStream(autoFetchFile);
|
||||
ObjectInputStream ois = new ObjectInputStream(fi);
|
||||
AutoFetchManager profListener = (AutoFetchManager) ois.readObject();
|
||||
ois.close();
|
||||
|
||||
logger.info("AutoFetch deserialized from file ["+autoFetchFile.getAbsolutePath()+"]");
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
|
||||
* query in which case the parentNode will be null, or a lazy loading query
|
||||
* resulting from traversal of the object graph.
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros) {
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
|
||||
|
||||
if (node != null){
|
||||
ObjectGraphOrigin origin = node.getOriginQueryPoint();
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.text.PathProperties.Props;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -53,30 +48,6 @@ public class Statistics implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public MetaAutoFetchStatistic createPublicMeta() {
|
||||
|
||||
synchronized (monitor) {
|
||||
|
||||
StatisticsQuery[] sourceQueryStats = queryStatsMap.values().toArray(new StatisticsQuery[queryStatsMap.size()]);
|
||||
List<QueryStats> destQueryStats = new ArrayList<QueryStats>(sourceQueryStats.length);
|
||||
|
||||
// copy the query statistics
|
||||
for (int i = 0; i < sourceQueryStats.length; i++) {
|
||||
destQueryStats.add(sourceQueryStats[i].createPublicMeta());
|
||||
}
|
||||
|
||||
StatisticsNodeUsage[] sourceNodeUsage = nodeUsageMap.values().toArray(new StatisticsNodeUsage[nodeUsageMap.size()]);
|
||||
List<NodeUsageStats> destNodeUsage = new ArrayList<NodeUsageStats>(sourceNodeUsage.length);
|
||||
|
||||
// copy the node usage statistics
|
||||
for (int i = 0; i < sourceNodeUsage.length; i++) {
|
||||
destNodeUsage.add(sourceNodeUsage[i].createPublicMeta());
|
||||
}
|
||||
|
||||
return new MetaAutoFetchStatistic(origin, counter, destQueryStats, destNodeUsage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times the root query has executed.
|
||||
* <p>
|
||||
@@ -127,7 +98,7 @@ public class Statistics implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
public void collectQueryInfo(ObjectGraphNode node, int beansLoaded, int micros) {
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
|
||||
|
||||
synchronized (monitor) {
|
||||
String key = node.getPath();
|
||||
|
||||
@@ -5,16 +5,16 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Collects usages statistics for a given node in the object graph.
|
||||
@@ -44,13 +44,6 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
public NodeUsageStats createPublicMeta() {
|
||||
synchronized(monitor){
|
||||
String[] usedProps = aggregateUsed.toArray(new String[aggregateUsed.size()]);
|
||||
return new NodeUsageStats(path, profileCount, profileUsedCount, usedProps);
|
||||
}
|
||||
}
|
||||
|
||||
public void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc) {
|
||||
|
||||
synchronized(monitor){
|
||||
@@ -93,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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
|
||||
|
||||
/**
|
||||
* Used to accumulate query execution statistics.
|
||||
*/
|
||||
@@ -13,21 +11,17 @@ public class StatisticsQuery implements Serializable {
|
||||
|
||||
private final String path;
|
||||
|
||||
private int exeCount;
|
||||
private long exeCount;
|
||||
|
||||
private int totalBeanLoaded;
|
||||
private long totalBeanLoaded;
|
||||
|
||||
private int totalMicros;
|
||||
private long totalMicros;
|
||||
|
||||
public StatisticsQuery(String path){
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public QueryStats createPublicMeta() {
|
||||
return new QueryStats(path, exeCount, totalBeanLoaded, totalMicros);
|
||||
}
|
||||
|
||||
public void add(int beansLoaded, int micros) {
|
||||
public void add(long beansLoaded, long micros) {
|
||||
exeCount++;
|
||||
totalBeanLoaded += beansLoaded;
|
||||
totalMicros += micros;
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.autofetch;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchTunedQueryInfo;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
@@ -73,13 +72,6 @@ public class TunedQueryInfo implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this tuned fetch data for public consumption.
|
||||
*/
|
||||
public MetaAutoFetchTunedQueryInfo createPublicMeta() {
|
||||
return new MetaAutoFetchTunedQueryInfo(origin, tunedDetail.toString(), profileCount, tunedCount, lastTuneTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of times profiling has been collected for this query
|
||||
* point.
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.Statistics;
|
||||
|
||||
/**
|
||||
* Bean Finder for MetaAutoFetchStatistic.
|
||||
* <p>
|
||||
* This gets the meta data from the AutoFetchManager and creates a copy of that
|
||||
* data to give back to the caller in the form of MetaAutoFetchStatistic beans.
|
||||
* </p>
|
||||
*/
|
||||
public class BFAutoFetchStatisticFinder implements BeanFinder<MetaAutoFetchStatistic> {
|
||||
|
||||
|
||||
public MetaAutoFetchStatistic find(BeanQueryRequest<MetaAutoFetchStatistic> request) {
|
||||
SpiQuery<MetaAutoFetchStatistic> query = (SpiQuery<MetaAutoFetchStatistic>)request.getQuery();
|
||||
try {
|
||||
String queryPointKey = (String) query.getId();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
Statistics stats = manager.getStatistics(queryPointKey);
|
||||
if (stats != null) {
|
||||
return stats.createPublicMeta();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaAutoFetchStatistic> findMany(BeanQueryRequest<MetaAutoFetchStatistic> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)) {
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
BeanList<MetaAutoFetchStatistic> list = new BeanList<MetaAutoFetchStatistic>();
|
||||
|
||||
Iterator<Statistics> it = manager.iterateStatistics();
|
||||
while (it.hasNext()) {
|
||||
Statistics stats = it.next();
|
||||
// create a copy for public use
|
||||
list.add(stats.createPublicMeta());
|
||||
}
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaAutoFetchTunedQueryInfo;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo;
|
||||
|
||||
/**
|
||||
* BeanFinder for MetaAutoFetchTunedFetch.
|
||||
*/
|
||||
public class BFAutoFetchTunedFetchFinder implements BeanFinder<MetaAutoFetchTunedQueryInfo> {
|
||||
|
||||
|
||||
public MetaAutoFetchTunedQueryInfo find(BeanQueryRequest<MetaAutoFetchTunedQueryInfo> request) {
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>)request.getQuery();
|
||||
try {
|
||||
String queryPointKey = (String)query.getId();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
TunedQueryInfo tunedFetch = manager.getTunedQueryInfo(queryPointKey);
|
||||
if (tunedFetch != null){
|
||||
return tunedFetch.createPublicMeta();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e){
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaAutoFetchTunedQueryInfo> findMany(BeanQueryRequest<MetaAutoFetchTunedQueryInfo> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)){
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
AutoFetchManager manager = server.getAutoFetchManager();
|
||||
|
||||
BeanList<MetaAutoFetchTunedQueryInfo> list = new BeanList<MetaAutoFetchTunedQueryInfo>();
|
||||
|
||||
Iterator<TunedQueryInfo> it = manager.iterateTunedQueryInfo();
|
||||
while (it.hasNext()) {
|
||||
TunedQueryInfo tunedFetch = it.next();
|
||||
// create a copy for public use
|
||||
list.add(tunedFetch.createPublicMeta());
|
||||
}
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType, origQueryPlanHash";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.bean;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.common.BeanList;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.meta.MetaQueryStatistic;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
|
||||
/**
|
||||
* BeanFinder for MetaQueryStatistic.
|
||||
*/
|
||||
public class BFQueryStatisticFinder implements BeanFinder<MetaQueryStatistic> {
|
||||
|
||||
|
||||
public MetaQueryStatistic find(BeanQueryRequest<MetaQueryStatistic> request) {
|
||||
throw new RuntimeException("Not Supported yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Only returns Lists at this stage.
|
||||
*/
|
||||
public BeanCollection<MetaQueryStatistic> findMany(BeanQueryRequest<MetaQueryStatistic> request) {
|
||||
|
||||
SpiQuery.Type queryType = ((SpiQuery<?>)request.getQuery()).getType();
|
||||
if (!queryType.equals(SpiQuery.Type.LIST)){
|
||||
throw new PersistenceException("Only findList() supported at this stage.");
|
||||
}
|
||||
|
||||
BeanList<MetaQueryStatistic> list = new BeanList<MetaQueryStatistic>();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) request.getEbeanServer();
|
||||
build(list, server);
|
||||
|
||||
String orderBy = request.getQuery().order().toStringFormat();
|
||||
if (orderBy == null){
|
||||
orderBy = "beanType, origQueryPlanHash, autofetchTuned";
|
||||
}
|
||||
server.sort(list, orderBy);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void build(List<MetaQueryStatistic> list, SpiEbeanServer server) {
|
||||
|
||||
for (BeanDescriptor<?> desc : server.getBeanDescriptors()) {
|
||||
desc.clearQueryStatistics();
|
||||
build(list, desc);
|
||||
}
|
||||
}
|
||||
|
||||
private void build(List<MetaQueryStatistic> list, BeanDescriptor<?> desc) {
|
||||
|
||||
Iterator<CQueryPlan> it = desc.queryPlans();
|
||||
while (it.hasNext()) {
|
||||
CQueryPlan queryPlan = (CQueryPlan) it.next();
|
||||
list.add(queryPlan.createMetaQueryStatistic(desc.getFullName()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>BeanFinders, BeanControllers etc for "meta" beans</title>
|
||||
</head>
|
||||
<body>
|
||||
BeanFinders, BeanControllers etc for "meta" beans
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -11,21 +16,23 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
*/
|
||||
public abstract class BeanRequest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BeanRequest.class);
|
||||
|
||||
/**
|
||||
* The server processing the request.
|
||||
*/
|
||||
final SpiEbeanServer ebeanServer;
|
||||
protected final SpiEbeanServer ebeanServer;
|
||||
|
||||
final String serverName;
|
||||
protected final String serverName;
|
||||
|
||||
/**
|
||||
* The transaction this is part of.
|
||||
*/
|
||||
SpiTransaction transaction;
|
||||
protected SpiTransaction transaction;
|
||||
|
||||
boolean createdTransaction;
|
||||
protected boolean createdTransaction;
|
||||
|
||||
boolean readOnly;
|
||||
protected boolean readOnly;
|
||||
|
||||
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
@@ -84,7 +91,14 @@ public abstract class BeanRequest {
|
||||
*/
|
||||
public void rollbackTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
transaction.rollback();
|
||||
try {
|
||||
transaction.rollback();
|
||||
} catch (PersistenceException e) {
|
||||
// Just log this and carry on. A previous exception has been
|
||||
// thrown and if this rollback throws exception it likely means
|
||||
// that the connection is broken (and the datasource and db will cleanup)
|
||||
log.error("Error trying to rollack a transaction (after a prior exception thrown)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +110,10 @@ public abstract class BeanRequest {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
public SpiEbeanServer getServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Transaction associated with this request.
|
||||
*/
|
||||
|
||||
@@ -82,16 +82,22 @@ public class BootupClassPathSearch {
|
||||
ClassPathSearchFilter filter = new ClassPathSearchFilter();
|
||||
filter.addDefaultExcludePackages();
|
||||
|
||||
if (packages != null) {
|
||||
if (packages != null && packages.size() > 0) {
|
||||
for (String packageName : packages) {
|
||||
filter.includePackage(packageName);
|
||||
}
|
||||
|
||||
// if they specified include packages, they don't want by default to include everything
|
||||
filter.setDefaultPackageMatch(false);
|
||||
}
|
||||
|
||||
if (jars != null) {
|
||||
if (jars != null && jars.size() > 0) {
|
||||
for (String jarName : jars) {
|
||||
filter.includeJar(jarName);
|
||||
}
|
||||
|
||||
// if they specified jars to specifically include, they don't want everything included
|
||||
filter.setDefaultJarMatch(false);
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
@@ -8,9 +8,9 @@ import java.util.List;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
@@ -22,8 +22,6 @@ import com.avaje.ebean.event.ServerConfigStartup;
|
||||
import com.avaje.ebean.event.TransactionEventListener;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Interesting classes for a EbeanServer such as Embeddable, Entity,
|
||||
@@ -33,8 +31,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
|
||||
|
||||
private ArrayList<Class<?>> xmlBeanList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> embeddableList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> entityList = new ArrayList<Class<?>>();
|
||||
@@ -55,7 +51,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
|
||||
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> luceneIndexList = new ArrayList<Class<?>>();
|
||||
|
||||
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
|
||||
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
|
||||
@@ -75,7 +70,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
}
|
||||
|
||||
private BootupClasses(BootupClasses parent) {
|
||||
this.xmlBeanList.addAll(parent.xmlBeanList);
|
||||
this.embeddableList.addAll(parent.embeddableList);
|
||||
this.entityList.addAll(parent.entityList);
|
||||
this.scalarTypeList.addAll(parent.scalarTypeList);
|
||||
@@ -86,7 +80,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
this.beanFinderList.addAll(parent.beanFinderList);
|
||||
this.beanListenerList.addAll(parent.beanListenerList);
|
||||
this.beanQueryAdapterList.addAll(parent.beanQueryAdapterList);
|
||||
this.luceneIndexList.addAll(parent.luceneIndexList);
|
||||
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
|
||||
}
|
||||
|
||||
@@ -304,13 +297,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
return beanListenerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of XML Beans.
|
||||
*/
|
||||
public ArrayList<Class<?>> getXmlBeanList() {
|
||||
return xmlBeanList;
|
||||
}
|
||||
|
||||
public void add(Iterator<Class<?>> it) {
|
||||
while (it.hasNext()) {
|
||||
Class<?> clazz = it.next();
|
||||
@@ -326,10 +312,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
} else if (isEntity(cls)) {
|
||||
entityList.add(cls);
|
||||
|
||||
} else if (isXmlBean(cls)){
|
||||
entityList.add(cls);
|
||||
//xmlBeanList.add(cls);
|
||||
|
||||
} else if (isInterestingInterface(cls)) {
|
||||
return true;
|
||||
|
||||
@@ -419,18 +401,4 @@ public class BootupClasses implements ClassPathSearchMatcher {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXmlBean(Class<?> cls) {
|
||||
|
||||
Annotation ann = cls.getAnnotation(XmlRootElement.class);
|
||||
if (ann != null) {
|
||||
return true;
|
||||
}
|
||||
ann = cls.getAnnotation(XmlType.class);
|
||||
if (ann != null) {
|
||||
// Only looking for Beans and not Enums
|
||||
return !cls.isEnum();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.avaje.ebean.meta.*;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebeaninternal.server.util.LongAdder;
|
||||
|
||||
/**
|
||||
* Helper to collect the query execution statistics for a given node.
|
||||
*/
|
||||
public class CObjectGraphNodeStatistics {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
|
||||
private final LongAdder count = new LongAdder();
|
||||
|
||||
private final LongAdder totalTime = new LongAdder();
|
||||
|
||||
private final LongAdder totalBeans = new LongAdder();
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
public CObjectGraphNodeStatistics(ObjectGraphNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public void add(long beanCount, long exeMicros) {
|
||||
count.increment();
|
||||
totalTime.add(exeMicros);
|
||||
totalBeans.add(beanCount);
|
||||
}
|
||||
|
||||
public MetaObjectGraphNodeStats get(boolean reset) {
|
||||
if (reset) {
|
||||
return new Snapshot(node, startTime.getAndSet(System.currentTimeMillis()), count.sumThenReset(),
|
||||
totalTime.sumThenReset(), totalBeans.sumThenReset());
|
||||
} else {
|
||||
return new Snapshot(node, startTime.get(), count.sum(), totalTime.sum(), totalBeans.sum());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Snapshot implements MetaObjectGraphNodeStats {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
private final long startTime;
|
||||
private final long count;
|
||||
private final long totalTime;
|
||||
private final long totalBeans;
|
||||
|
||||
public Snapshot(ObjectGraphNode node, long startTime, long count, long totalTime, long totalBeans) {
|
||||
this.node = node;
|
||||
this.startTime = startTime;
|
||||
this.count = count;
|
||||
this.totalTime = totalTime;
|
||||
this.totalBeans = totalBeans;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return node + " count[" + count + "] time[" + totalTime + "] beans[" + totalBeans + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTime() {
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalBeans() {
|
||||
return totalBeans;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,20 +15,19 @@ import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyContext;
|
||||
import com.avaje.ebeaninternal.api.LoadManyRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyBuffer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
/**
|
||||
* Helper to handle lazy loading and refreshing of beans.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class DefaultBeanLoader {
|
||||
|
||||
@@ -50,35 +49,32 @@ public class DefaultBeanLoader {
|
||||
* re-use the query plan cache and get DB statement re-use.
|
||||
* </p>
|
||||
*/
|
||||
private int getBatchSize(int batchListSize, int requestedBatchSize) {
|
||||
if (batchListSize == requestedBatchSize) {
|
||||
return batchListSize;
|
||||
}
|
||||
if (batchListSize == 1) {
|
||||
private int getBatchSize(int batchSize) {
|
||||
|
||||
if (batchSize == 1) {
|
||||
// there is only one bean/collection to load
|
||||
return 1;
|
||||
}
|
||||
if (requestedBatchSize <= 5) {
|
||||
if (batchSize <= 5) {
|
||||
// anything less than 5 becomes 5
|
||||
return 5;
|
||||
}
|
||||
if (batchListSize <= 10 || requestedBatchSize <= 10) {
|
||||
// 10 or less to load
|
||||
// ... or we wanted a batch size between 6 and 10
|
||||
if (batchSize <= 10) {
|
||||
return 10;
|
||||
}
|
||||
if (batchListSize <= 20 || requestedBatchSize <= 20) {
|
||||
// 20 or less to load
|
||||
// ... or we wanted a batch size between 11 and 20
|
||||
if (batchSize <= 20) {
|
||||
return 20;
|
||||
}
|
||||
if (batchListSize <= 50) {
|
||||
if (batchSize <= 50) {
|
||||
return 50;
|
||||
}
|
||||
return requestedBatchSize;
|
||||
if (batchSize <= 100) {
|
||||
return 100;
|
||||
}
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName) {
|
||||
public void refreshMany(EntityBean parentBean, String propertyName) {
|
||||
refreshMany(parentBean, propertyName, null);
|
||||
}
|
||||
|
||||
@@ -86,9 +82,9 @@ public class DefaultBeanLoader {
|
||||
|
||||
List<BeanCollection<?>> batch = loadRequest.getBatch();
|
||||
|
||||
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadManyContext ctx = loadRequest.getLoadContext();
|
||||
LoadManyBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanPropertyAssocMany<?> many = ctx.getBeanProperty();
|
||||
|
||||
PersistenceContext pc = ctx.getPersistenceContext();
|
||||
@@ -97,7 +93,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
BeanCollection<?> bc = batch.get(i);
|
||||
Object ownerBean = bc.getOwnerBean();
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
Object id = many.getParentId(ownerBean);
|
||||
idList.add(id);
|
||||
}
|
||||
@@ -111,20 +107,12 @@ public class DefaultBeanLoader {
|
||||
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
String idProperty = desc.getIdBinder().getIdProperty();
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
|
||||
query.setLazyLoadForParents(idList, many);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
|
||||
query.setMode(Mode.LAZYLOAD_MANY);
|
||||
query.setLazyLoadManyPath(many.getName());
|
||||
query.setPersistenceContext(pc);
|
||||
query.select(idProperty);
|
||||
query.fetch(many.getName());
|
||||
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
@@ -134,7 +122,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
if (loadRequest.isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.fetch(many.getName(), many.getTargetIdProperty());
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
server.findList(query, loadRequest.getTransaction());
|
||||
@@ -149,38 +137,39 @@ 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, LoadManyContext ctx, boolean onlyIds) {
|
||||
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();
|
||||
//ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
|
||||
|
||||
loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
|
||||
loadManyInternal(parentBean, propertyName, null, false, null, onlyIds);
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
|
||||
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 = null;
|
||||
PersistenceContext pc = null;
|
||||
BeanCollection<?> beanCollection = null;
|
||||
ExpressionList<?> filterMany = null;
|
||||
|
||||
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
|
||||
pc = ebi.getPersistenceContext();
|
||||
EntityBeanIntercept ebi = ((EntityBean) parentBean)._ebean_getIntercept();
|
||||
PersistenceContext pc = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
|
||||
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) parentDesc.getBeanProperty(propertyName);
|
||||
|
||||
BeanCollection<?> beanCollection = null;
|
||||
ExpressionList<?> filterMany = null;
|
||||
|
||||
Object currentValue = many.getValue(parentBean);
|
||||
if (currentValue instanceof BeanCollection<?>) {
|
||||
beanCollection = (BeanCollection<?>) currentValue;
|
||||
@@ -194,13 +183,13 @@ public class DefaultBeanLoader {
|
||||
pc.put(parentId, parentBean);
|
||||
}
|
||||
|
||||
boolean useManyIdCache = beanCollection != null && parentDesc.cacheIsUseManyId();
|
||||
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
|
||||
if (useManyIdCache) {
|
||||
Boolean readOnly = null;
|
||||
if (ebi != null && ebi.isReadOnly()) {
|
||||
readOnly = Boolean.TRUE;
|
||||
}
|
||||
if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly)) {
|
||||
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -253,7 +242,7 @@ public class DefaultBeanLoader {
|
||||
logger.debug("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean());
|
||||
}
|
||||
} else if (useManyIdCache) {
|
||||
parentDesc.cachePutMany(many, beanCollection, parentId);
|
||||
parentDesc.cacheManyPropPut(many, beanCollection, parentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,9 +259,9 @@ public class DefaultBeanLoader {
|
||||
throw new RuntimeException("Nothing in batch?");
|
||||
}
|
||||
|
||||
int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadBeanContext ctx = loadRequest.getLoadContext();
|
||||
LoadBeanBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
@@ -282,7 +271,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
Object bean = ebi.getOwner();
|
||||
EntityBean bean = ebi.getOwner();
|
||||
Object id = desc.getId(bean);
|
||||
idList.add(id);
|
||||
}
|
||||
@@ -305,17 +294,6 @@ public class DefaultBeanLoader {
|
||||
|
||||
PersistenceContext persistenceContext = ctx.getPersistenceContext();
|
||||
|
||||
// query the database
|
||||
for (int i = 0; i < ebis.length; i++) {
|
||||
Object parentBean = ebis[i].getParentBean();
|
||||
if (parentBean != null) {
|
||||
// Special case for OneToOne
|
||||
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
|
||||
Object parentId = parentDesc.getId(parentBean);
|
||||
persistenceContext.put(parentId, parentBean);
|
||||
}
|
||||
}
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(beanType);
|
||||
|
||||
query.setMode(Mode.LAZYLOAD_BEAN);
|
||||
@@ -338,35 +316,42 @@ public class DefaultBeanLoader {
|
||||
|
||||
if (loadRequest.isLoadCache()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
desc.cachePutBeanData(list.get(i));
|
||||
desc.cacheBeanPutData((EntityBean)list.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ebis.length; i++) {
|
||||
if (ebis[i].isReference()) {
|
||||
// The underlying row in DB was deleted. Mark this bean as 'failed'
|
||||
// but allow processing to continue until it is accessed by client code
|
||||
ebis[i].setLazyLoadFailure();
|
||||
}
|
||||
// Check if the underlying row in DB was deleted. Mark this bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
ebis[i].checkLazyLoadFailure();
|
||||
}
|
||||
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
public void refresh(Object bean) {
|
||||
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN);
|
||||
public void refresh(EntityBean bean) {
|
||||
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
|
||||
}
|
||||
|
||||
public void loadBean(EntityBeanIntercept ebi) {
|
||||
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN);
|
||||
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
|
||||
}
|
||||
|
||||
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
|
||||
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) {
|
||||
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
|
||||
PersistenceContext pc = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
|
||||
if (EntityType.EMBEDDED == desc.getEntityType()) {
|
||||
// lazy loading on an embedded bean property
|
||||
EntityBean embeddedOwner = (EntityBean)ebi.getEmbeddedOwner();
|
||||
int ownerIndex = ebi.getEmbeddedOwnerIndex();
|
||||
|
||||
refreshBeanInternal(embeddedOwner, mode, ownerIndex);
|
||||
}
|
||||
|
||||
Object id = desc.getId(bean);
|
||||
|
||||
if (pc == null) {
|
||||
@@ -378,10 +363,10 @@ public class DefaultBeanLoader {
|
||||
}
|
||||
}
|
||||
|
||||
if (ebi != null) {
|
||||
if (embeddedOwnerIndex == -1) {
|
||||
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
|
||||
// lazy loading and the bean cache is active
|
||||
if (desc.loadFromCache(bean, ebi, id)) {
|
||||
if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -392,17 +377,14 @@ public class DefaultBeanLoader {
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
|
||||
if (ebi != null) {
|
||||
Object parentBean = ebi.getParentBean();
|
||||
if (parentBean != null) {
|
||||
// Special case for OneToOne
|
||||
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
|
||||
Object parentId = parentDesc.getId(parentBean);
|
||||
pc.putIfAbsent(parentId, parentBean);
|
||||
}
|
||||
|
||||
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
|
||||
}
|
||||
|
||||
if (embeddedOwnerIndex > -1) {
|
||||
String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex);
|
||||
query.select("id,"+embeddedBeanPropertyName);
|
||||
}
|
||||
|
||||
// don't collect autoFetch usage profiling information
|
||||
// as we just copy the data out of these fetched beans
|
||||
// and put the data into the original bean
|
||||
@@ -411,12 +393,13 @@ public class DefaultBeanLoader {
|
||||
|
||||
query.setMode(mode);
|
||||
query.setId(id);
|
||||
// make sure the query doesn't use the cache
|
||||
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
|
||||
|
||||
if (embeddedOwnerIndex > -1 || mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
|
||||
// make sure the query doesn't use the cache
|
||||
query.setUseCache(false);
|
||||
}
|
||||
|
||||
if (ebi != null && ebi.isReadOnly()) {
|
||||
if (ebi.isReadOnly()) {
|
||||
query.setReadOnly(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.BeanState;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
@@ -13,9 +14,9 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
*/
|
||||
public class DefaultBeanState implements BeanState {
|
||||
|
||||
final EntityBean entityBean;
|
||||
private final EntityBean entityBean;
|
||||
|
||||
final EntityBeanIntercept intercept;
|
||||
private final EntityBeanIntercept intercept;
|
||||
|
||||
public DefaultBeanState(EntityBean entityBean){
|
||||
this.entityBean = entityBean;
|
||||
@@ -39,14 +40,16 @@ public class DefaultBeanState implements BeanState {
|
||||
}
|
||||
|
||||
public Set<String> getLoadedProps() {
|
||||
Set<String> props = intercept.getLoadedProps();
|
||||
return props == null ? null : Collections.unmodifiableSet(props);
|
||||
return intercept.getLoadedPropertyNames();
|
||||
}
|
||||
|
||||
public Set<String> getChangedProps() {
|
||||
Set<String> props = intercept.getChangedProps();
|
||||
return props == null ? null : Collections.unmodifiableSet(props);
|
||||
}
|
||||
return intercept.getDirtyPropertyNames();
|
||||
}
|
||||
|
||||
public Map<String,ValuePair> getDirtyValues() {
|
||||
return intercept.getDirtyValues();
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return intercept.isReadOnly();
|
||||
@@ -64,14 +67,8 @@ public class DefaultBeanState implements BeanState {
|
||||
entityBean.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void setLoaded(Set<String> loadedProperties) {
|
||||
intercept.setLoadedProps(loadedProperties);
|
||||
intercept.setLoaded();
|
||||
public void setLoaded() {
|
||||
intercept.setLoaded();
|
||||
}
|
||||
|
||||
public void setReference() {
|
||||
intercept.setReference();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.meta.MetaObjectGraphNodeStats;
|
||||
|
||||
/**
|
||||
* DefaultServer based implementation of MetaInfoManager.
|
||||
*/
|
||||
public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
|
||||
private final DefaultServer server;
|
||||
|
||||
public DefaultMetaInfoManager(DefaultServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass) {
|
||||
return server.getBeanDescriptor(beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
return new ArrayList<MetaBeanInfo>(server.getBeanDescriptors());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<MetaQueryPlanStatistic>();
|
||||
|
||||
for (MetaBeanInfo metaBeanInfo : getMetaBeanInfoList()) {
|
||||
list.addAll(metaBeanInfo.collectQueryPlanStatistics(reset));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset) {
|
||||
|
||||
List<MetaObjectGraphNodeStats> list = new ArrayList<MetaObjectGraphNodeStats>();
|
||||
|
||||
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
|
||||
MetaObjectGraphNodeStats nodeStats = nodeStatistics.get(reset);
|
||||
if (nodeStats.getCount() > 0) {
|
||||
// Only collection non-empty statistics
|
||||
list.add(nodeStats);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
@@ -29,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;
|
||||
@@ -49,13 +51,18 @@ import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.bean.PersistenceContext.WithOption;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.EncryptKeyManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebean.text.json.JsonElement;
|
||||
@@ -84,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;
|
||||
@@ -99,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;
|
||||
@@ -115,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;
|
||||
@@ -132,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.
|
||||
*/
|
||||
@@ -154,7 +164,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private final CQueryEngine cqueryEngine;
|
||||
|
||||
@Deprecated
|
||||
//@Deprecated
|
||||
private DdlGenerator ddlGenerator;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
@@ -167,6 +177,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private final JsonContext jsonContext;
|
||||
|
||||
private final MetaInfoManager metaInfoManager;
|
||||
|
||||
/**
|
||||
* The MBean name used to register Ebean.
|
||||
*/
|
||||
@@ -187,45 +199,57 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
private int lazyLoadBatchSize;
|
||||
|
||||
/** The query batch size */
|
||||
/**
|
||||
* The query batch size
|
||||
*/
|
||||
private int queryBatchSize;
|
||||
|
||||
/**
|
||||
* JDBC driver specific handling for JDBC batch execution.
|
||||
*/
|
||||
private PstmtBatch pstmtBatch;
|
||||
|
||||
|
||||
/**
|
||||
* holds plugins (e.g. ddl generator) detected by the service loader
|
||||
*/
|
||||
private List<SpiEbeanPlugin> ebeanPlugins;
|
||||
|
||||
private final boolean collectQueryOrigins;
|
||||
|
||||
private final boolean collectQueryStatsByNode;
|
||||
|
||||
/**
|
||||
* Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points).
|
||||
*/
|
||||
protected final ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics> objectGraphStats;
|
||||
|
||||
/**
|
||||
* Create the DefaultServer.
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
this.serverConfig = config.getServerConfig();
|
||||
this.objectGraphStats = new ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics>();
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
this.serverCacheManager = cache;
|
||||
this.pstmtBatch = config.getPstmtBatch();
|
||||
this.databasePlatform = config.getDatabasePlatform();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.serverName = config.getServerConfig().getName();
|
||||
this.lazyLoadBatchSize = config.getServerConfig().getLazyLoadBatchSize();
|
||||
this.queryBatchSize = config.getServerConfig().getQueryBatchSize();
|
||||
|
||||
this.serverName = serverConfig.getName();
|
||||
this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize();
|
||||
this.queryBatchSize = serverConfig.getQueryBatchSize();
|
||||
this.cqueryEngine = config.getCQueryEngine();
|
||||
this.expressionFactory = config.getExpressionFactory();
|
||||
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
|
||||
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
|
||||
|
||||
this.beanDescriptorManager = config.getBeanDescriptorManager();
|
||||
beanDescriptorManager.setEbeanServer(this);
|
||||
|
||||
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
|
||||
|
||||
this.defaultUpdateNullProperties = "true"
|
||||
.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();
|
||||
@@ -276,12 +300,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDefaultDeleteMissingChildren() {
|
||||
return defaultDeleteMissingChildren;
|
||||
}
|
||||
|
||||
public boolean isDefaultUpdateNullProperties() {
|
||||
return defaultUpdateNullProperties;
|
||||
@Override
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
public int getLazyLoadBatchSize() {
|
||||
@@ -292,9 +313,18 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return pstmtBatch;
|
||||
}
|
||||
|
||||
public ServerConfig getServerConfig() {
|
||||
return serverConfig;
|
||||
}
|
||||
|
||||
public DatabasePlatform getDatabasePlatform() {
|
||||
return databasePlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaInfoManager getMetaInfoManager() {
|
||||
return metaInfoManager;
|
||||
}
|
||||
|
||||
public BackgroundExecutor getBackgroundExecutor() {
|
||||
return backgroundExecutor;
|
||||
@@ -454,7 +484,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a query. Only valid for ORM queries (not LDAP)
|
||||
* Compile a query. Only valid for ORM queries.
|
||||
*/
|
||||
public <T> CQuery<T> compileQuery(Query<T> query, Transaction t) {
|
||||
SpiOrmQueryRequest<T> qr = createQueryRequest(Type.SUBQUERY, query, t);
|
||||
@@ -486,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) {
|
||||
@@ -501,12 +531,12 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
beanLoader.loadMany(bc, null, onlyIds);
|
||||
beanLoader.loadMany(bc, onlyIds);
|
||||
}
|
||||
|
||||
public void refresh(Object bean) {
|
||||
|
||||
beanLoader.refresh(bean);
|
||||
beanLoader.refresh(checkEntityBean(bean));
|
||||
}
|
||||
|
||||
public void loadBean(LoadBeanRequest loadRequest) {
|
||||
@@ -616,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)) {
|
||||
@@ -1097,7 +1119,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
if (Mode.LAZYLOAD_MANY.equals(query.getMode())) {
|
||||
allowOneManyFetch = false;
|
||||
|
||||
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect() && query.getBackgroundFetchAfter() == 0) {
|
||||
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) {
|
||||
// convert ALL fetch joins to Many's to be query joins
|
||||
// so that limit offset type SQL clauses work
|
||||
allowOneManyFetch = false;
|
||||
@@ -1136,9 +1158,14 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
// first look in the persistence context
|
||||
context = t.getPersistenceContext();
|
||||
if (context != null) {
|
||||
Object o = context.get(beanDescriptor.getBeanType(), query.getId());
|
||||
WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId());
|
||||
if (o != null) {
|
||||
return (T) o;
|
||||
if (o.isDeleted()) {
|
||||
// Bean was previously deleted in the same transaction / persistence context
|
||||
return null;
|
||||
}
|
||||
// Return the entity bean instance from the persistence context
|
||||
return (T) o.getBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1148,23 +1175,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
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(), false, null, false);
|
||||
loadContext.setPersistenceContext(context);
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
|
||||
ebi.setPersistenceContext(context);
|
||||
loadContext.register(null, ebi);
|
||||
}
|
||||
|
||||
return (T) cachedBean;
|
||||
// Hit the L2 bean cache
|
||||
return beanDescriptor.cacheBeanGet(query, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -1213,17 +1225,9 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType());
|
||||
|
||||
if (desc.calculateUseNaturalKeyCache(q.isUseBeanCache())) {
|
||||
// check if it is a find by unique id
|
||||
NaturalKeyBindParam keyBindParam = q.getNaturalKeyBindParam();
|
||||
if (keyBindParam != null && desc.cacheIsNaturalKey(keyBindParam.getName())) {
|
||||
Object id2 = desc.cacheGetNaturalKeyId(keyBindParam.getValue());
|
||||
if (id2 != null) {
|
||||
SpiQuery<T> copy = q.copy();
|
||||
copy.convertWhereNaturalKeyToId(id2);
|
||||
return findId(copy, t);
|
||||
}
|
||||
}
|
||||
T bean = desc.cacheNaturalKeyLookup(q, (SpiTransaction)t);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
// a query that is expected to return either 0 or 1 rows
|
||||
@@ -1231,9 +1235,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
if (list.size() == 0) {
|
||||
return null;
|
||||
|
||||
} else if (list.size() > 1) {
|
||||
String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]";
|
||||
throw new PersistenceException(m);
|
||||
throw new PersistenceException("Unique expecting 0 or 1 rows but got [" + list.size() + "]");
|
||||
|
||||
} else {
|
||||
return list.get(0);
|
||||
}
|
||||
@@ -1416,6 +1421,12 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return new LimitOffsetPagingQuery<T>(this, spiQuery, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
|
||||
|
||||
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
|
||||
@@ -1562,82 +1573,116 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
* Save the bean with an explicit transaction.
|
||||
*/
|
||||
public void save(Object bean, Transaction t) {
|
||||
if (bean == null) {
|
||||
throw new NullPointerException(Message.msg("bean.isnull"));
|
||||
}
|
||||
persister.save(bean, t);
|
||||
persister.save(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean updating non-null properties.
|
||||
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
|
||||
*/
|
||||
public void update(Object bean) {
|
||||
update(bean, null, null);
|
||||
update(bean, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean explicitly stating which properties to
|
||||
* include in the update.
|
||||
*/
|
||||
public void update(Object bean, Set<String> updateProps) {
|
||||
update(bean, updateProps, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean updating non-null properties.
|
||||
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
|
||||
*/
|
||||
public void update(Object bean, Transaction t) {
|
||||
update(bean, null, t);
|
||||
persister.update(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean explicitly stating which properties to
|
||||
* include in the update.
|
||||
* Update the bean specifying the deleteMissingChildren option.
|
||||
*/
|
||||
public void update(Object bean, Set<String> updateProps, Transaction t) {
|
||||
update(bean, updateProps, t, defaultDeleteMissingChildren, defaultUpdateNullProperties);
|
||||
public void update(Object bean, Transaction t, boolean deleteMissingChildren) {
|
||||
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean explicitly stating which properties to
|
||||
* include in the update.
|
||||
* Update all beans in the collection.
|
||||
*/
|
||||
public void update(Object bean, Set<String> updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) {
|
||||
if (bean == null) {
|
||||
throw new NullPointerException(Message.msg("bean.isnull"));
|
||||
public void update(Collection<?> beans) {
|
||||
update(beans, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all beans in the collection with an explicit transaction.
|
||||
*/
|
||||
public void update(Collection<?> beans, Transaction t) {
|
||||
|
||||
if (beans == null || beans.isEmpty()) {
|
||||
// Nothing to update?
|
||||
return;
|
||||
}
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
for (Object bean : beans) {
|
||||
update(checkEntityBean(bean), trans);
|
||||
}
|
||||
wrap.commitIfCreated();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.rollbackIfCreated();
|
||||
throw e;
|
||||
}
|
||||
persister.forceUpdate(bean, updateProps, t, deleteMissingChildren, updateNullProperties);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Force the bean to be saved with an explicit insert.
|
||||
* <p>
|
||||
* Typically you would use save() and let Ebean determine if the bean should
|
||||
* be inserted or updated. This can be useful when you are transferring data
|
||||
* between databases and want to explicitly insert a bean into a different
|
||||
* database that it came from.
|
||||
* </p>
|
||||
* Insert the bean.
|
||||
*/
|
||||
public void insert(Object bean) {
|
||||
insert(bean, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the bean to be saved with an explicit insert.
|
||||
* <p>
|
||||
* Typically you would use save() and let Ebean determine if the bean should
|
||||
* be inserted or updated. This can be useful when you are transferring data
|
||||
* between databases and want to explicitly insert a bean into a different
|
||||
* database that it came from.
|
||||
* </p>
|
||||
* Insert the bean with a transaction.
|
||||
*/
|
||||
public void insert(Object bean, Transaction t) {
|
||||
if (bean == null) {
|
||||
throw new NullPointerException(Message.msg("bean.isnull"));
|
||||
}
|
||||
persister.forceInsert(bean, t);
|
||||
persister.insert(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert all beans in the collection.
|
||||
*/
|
||||
public void insert(Collection<?> beans) {
|
||||
insert(beans, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert all beans in the collection with a transaction.
|
||||
*/
|
||||
public void insert(Collection<?> beans, Transaction t) {
|
||||
|
||||
if (beans == null || beans.isEmpty()) {
|
||||
// Nothing to insert?
|
||||
return;
|
||||
}
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
for (Object bean : beans) {
|
||||
persister.insert(checkEntityBean(bean), trans);
|
||||
}
|
||||
wrap.commitIfCreated();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.rollbackIfCreated();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private EntityBean checkEntityBean(Object bean) {
|
||||
if (bean == null) {
|
||||
throw new IllegalArgumentException(Message.msg("bean.isnull"));
|
||||
}
|
||||
if (bean instanceof EntityBean == false) {
|
||||
throw new IllegalArgumentException("Was expecting an EntityBean but got a "+bean.getClass());
|
||||
}
|
||||
return (EntityBean)bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associations (from the intersection table) of a ManyToMany given
|
||||
* the owner bean and the propertyName of the ManyToMany collection.
|
||||
@@ -1658,10 +1703,11 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
|
||||
|
||||
EntityBean owner = checkEntityBean(ownerBean);
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int rc = persister.deleteManyToManyAssociations(ownerBean, propertyName, trans);
|
||||
int rc = persister.deleteManyToManyAssociations(owner, propertyName, trans);
|
||||
wrap.commitIfCreated();
|
||||
return rc;
|
||||
|
||||
@@ -1685,11 +1731,12 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
|
||||
|
||||
EntityBean owner = checkEntityBean(ownerBean);
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
|
||||
persister.saveManyToManyAssociations(ownerBean, propertyName, trans);
|
||||
persister.saveManyToManyAssociations(owner, propertyName, trans);
|
||||
|
||||
wrap.commitIfCreated();
|
||||
|
||||
@@ -1705,21 +1752,12 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
public void saveAssociation(Object ownerBean, String propertyName, Transaction t) {
|
||||
|
||||
if (ownerBean instanceof EntityBean) {
|
||||
Set<String> loadedProps = ((EntityBean) ownerBean)._ebean_getIntercept().getLoadedProps();
|
||||
if (loadedProps != null && !loadedProps.contains(propertyName)) {
|
||||
// skip as property is not actually loaded in this partially
|
||||
// loaded bean
|
||||
logger.debug("Skip saveAssociation as property " + propertyName + " is not loaded");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EntityBean owner = checkEntityBean(ownerBean);
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
|
||||
persister.saveAssociation(ownerBean, propertyName, trans);
|
||||
persister.saveAssociation(owner, propertyName, trans);
|
||||
|
||||
wrap.commitIfCreated();
|
||||
|
||||
@@ -1745,6 +1783,14 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return save(c.iterator(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an update or insert on each bean in the collection. Returns the
|
||||
* number of beans that where saved.
|
||||
*/
|
||||
public int save(Collection<?> c, Transaction t) {
|
||||
return save(c.iterator(), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all beans in the iterator with an explicit transaction.
|
||||
*/
|
||||
@@ -1755,7 +1801,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int saveCount = 0;
|
||||
while (it.hasNext()) {
|
||||
Object bean = it.next();
|
||||
EntityBean bean = checkEntityBean(it.next());
|
||||
persister.save(bean, trans);
|
||||
saveCount++;
|
||||
}
|
||||
@@ -1819,10 +1865,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
* Delete the bean with the explicit transaction.
|
||||
*/
|
||||
public void delete(Object bean, Transaction t) {
|
||||
if (bean == null) {
|
||||
throw new NullPointerException(Message.msg("bean.isnull"));
|
||||
}
|
||||
persister.delete(bean, t);
|
||||
|
||||
persister.delete(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1850,7 +1894,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int deleteCount = 0;
|
||||
while (it.hasNext()) {
|
||||
Object bean = it.next();
|
||||
EntityBean bean = checkEntityBean(it.next());
|
||||
persister.delete(bean, trans);
|
||||
deleteCount++;
|
||||
}
|
||||
@@ -1918,6 +1962,13 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return beanDescriptorManager.getBeanDescriptorList();
|
||||
}
|
||||
|
||||
public List<MetaBeanInfo> getMetaBeanInfoList() {
|
||||
|
||||
List<MetaBeanInfo> list = new ArrayList<MetaBeanInfo>();
|
||||
list.addAll(getBeanDescriptors());
|
||||
return list;
|
||||
}
|
||||
|
||||
public void register(BeanPersistController c) {
|
||||
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -1946,13 +1997,14 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
public Object getBeanId(Object bean) {
|
||||
EntityBean eb = checkEntityBean(bean);
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(bean.getClass());
|
||||
if (desc == null) {
|
||||
String m = bean.getClass().getName() + " is NOT an Entity Bean registered with this server?";
|
||||
throw new PersistenceException(m);
|
||||
}
|
||||
|
||||
return desc.getId(bean);
|
||||
return desc.getId(eb);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2018,8 +2070,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return transactionManager.createQueryTransaction();
|
||||
}
|
||||
|
||||
private static final int IGNORE_LEADING_ELEMENTS = 5;
|
||||
private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15);
|
||||
|
||||
/**
|
||||
* Create a CallStack object.
|
||||
@@ -2067,4 +2117,20 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return jsonContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
|
||||
|
||||
if (collectQueryStatsByNode) {
|
||||
CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node);
|
||||
if (nodeStatistics == null) {
|
||||
// race condition here but I actually don't care too much if we miss a
|
||||
// few early statistics - especially when the server is warming up etc
|
||||
nodeStatistics = new CObjectGraphNodeStatistics(node);
|
||||
objectGraphStats.put(node, nodeStatistics);
|
||||
}
|
||||
nodeStatistics.add(loadedBeanCount, timeMicros);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -353,6 +353,11 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
if (sequenceFormat != null) {
|
||||
nc.setSequenceFormat(sequenceFormat);
|
||||
}
|
||||
|
||||
String schema = config.getProperty("namingConvention.schema");
|
||||
if (schema != null) {
|
||||
nc.setSchema(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,32 +415,10 @@ public class DefaultServerFactory implements BootupEbeanManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dsConfig.getHeartbeatSql() == null) {
|
||||
// use default heartbeatSql from the DatabasePlatform
|
||||
String heartbeatSql = getHeartbeatSql(dsConfig.getDriver());
|
||||
dsConfig.setHeartbeatSql(heartbeatSql);
|
||||
}
|
||||
|
||||
DataSourceAlert notify = new SimpleDataSourceAlert();
|
||||
return new DataSourcePool(notify, config.getName(), dsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a heartbeatSql depending on the jdbc driver name.
|
||||
*/
|
||||
private String getHeartbeatSql(String driver) {
|
||||
if (driver != null) {
|
||||
String d = driver.toLowerCase();
|
||||
if (d.contains("oracle")) {
|
||||
return "select 'x' from dual";
|
||||
}
|
||||
if (d.contains(".h2.") || d.contains(".mysql.") || d.contains("postgre")) {
|
||||
return "select 1";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the autoCommit and Transaction Isolation levels of the DataSource.
|
||||
* <p>
|
||||
|
||||
@@ -19,50 +19,55 @@ import com.avaje.ebeaninternal.util.ValueUtil;
|
||||
public class DiffHelp {
|
||||
|
||||
|
||||
/**
|
||||
* Return a map of the differences between a and b.
|
||||
* <p>
|
||||
* A and B must be of the same type. B can be null, in which case the
|
||||
* 'OldValues' of a is used to compare with (as B).
|
||||
* </p>
|
||||
* <p>
|
||||
* This intentionally does not include as OneToMany or ManyToMany
|
||||
* properties.
|
||||
* </p>
|
||||
*/
|
||||
/**
|
||||
* Return a map of the differences between a and b.
|
||||
* <p>
|
||||
* A and B must be of the same type. B can be null, in which case the 'dirty
|
||||
* values' of a is returned.
|
||||
* </p>
|
||||
* <p>
|
||||
* This intentionally does not include as OneToMany or ManyToMany properties.
|
||||
* </p>
|
||||
*/
|
||||
public Map<String, ValuePair> diff(Object a, Object b, BeanDescriptor<?> desc) {
|
||||
|
||||
boolean oldValues = false;
|
||||
if (a instanceof EntityBean == false) {
|
||||
throw new IllegalArgumentException("First bean expected to be an enhanced EntityBean? bean:"+a);
|
||||
}
|
||||
|
||||
if (b != null) {
|
||||
if (b instanceof EntityBean == false) {
|
||||
throw new IllegalArgumentException("Second bean expected to be an enhanced EntityBean? bean:"+b);
|
||||
}
|
||||
if (!a.getClass().isAssignableFrom(b.getClass())) {
|
||||
throw new IllegalArgumentException("Second bean not assignable to the first bean?");
|
||||
}
|
||||
}
|
||||
|
||||
if (b == null) {
|
||||
// get the old values from a
|
||||
if (a instanceof EntityBean) {
|
||||
EntityBean eb = (EntityBean) a;
|
||||
b = eb._ebean_getIntercept().getOldValues();
|
||||
oldValues = true;
|
||||
}
|
||||
return ((EntityBean) a)._ebean_getIntercept().getDirtyValues();
|
||||
}
|
||||
|
||||
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
|
||||
|
||||
if (b == null) {
|
||||
return map;
|
||||
}
|
||||
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
|
||||
diff(null, map, (EntityBean)a, (EntityBean)b, desc);
|
||||
return map;
|
||||
}
|
||||
|
||||
public void diff(String prefix, Map<String, ValuePair> map, EntityBean first, EntityBean sec, BeanDescriptor<?> desc) {
|
||||
|
||||
// check the simple properties
|
||||
BeanProperty[] base = desc.propertiesBaseScalar();
|
||||
for (int i = 0; i < base.length; i++) {
|
||||
|
||||
Object aval = base[i].getValue(a);
|
||||
Object bval = base[i].getValue(b);
|
||||
Object aval = base[i].getValue(first);
|
||||
Object bval = base[i].getValue(sec);
|
||||
if (!ValueUtil.areEqual(aval, bval)) {
|
||||
map.put(base[i].getName(), new ValuePair(aval, bval));
|
||||
String propName = (prefix == null) ? base[i].getName() : prefix + base[i].getName();
|
||||
map.put(propName, new ValuePair(aval, bval));
|
||||
}
|
||||
}
|
||||
|
||||
diffAssocOne(a, b, desc, map);
|
||||
diffEmbedded(a, b, desc, map, oldValues);
|
||||
|
||||
return map;
|
||||
diffAssocOne(prefix, first, sec, desc, map);
|
||||
diffEmbedded(prefix, first, sec, desc, map);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,40 +77,24 @@ public class DiffHelp {
|
||||
* determined to be different as is added to the map.
|
||||
* </p>
|
||||
*/
|
||||
private void diffEmbedded(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> map,
|
||||
boolean oldValues) {
|
||||
private void diffEmbedded(String prefix, EntityBean a, EntityBean b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
|
||||
|
||||
BeanPropertyAssocOne<?>[] emb = desc.propertiesEmbedded();
|
||||
|
||||
for (int i = 0; i < emb.length; i++) {
|
||||
Object aval = emb[i].getValue(a);
|
||||
Object bval = emb[i].getValue(b);
|
||||
if (oldValues) {
|
||||
bval = ((EntityBean) bval)._ebean_getIntercept().getOldValues();
|
||||
if (bval == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
EntityBean aval = (EntityBean)emb[i].getValue(a);
|
||||
EntityBean bval = (EntityBean)emb[i].getValue(b);
|
||||
|
||||
if (!isBothNull(aval, bval)) {
|
||||
String propName = (prefix == null) ? emb[i].getName() : prefix + emb[i].getName();
|
||||
if (isDiffNull(aval, bval)) {
|
||||
// one of the embedded beans is null
|
||||
map.put(emb[i].getName(), new ValuePair(aval, bval));
|
||||
map.put(propName, new ValuePair(aval, bval));
|
||||
|
||||
} else {
|
||||
// if ANY of the properties in an Embedded bean is
|
||||
// different, treat the whole bean as being different
|
||||
BeanProperty[] props = emb[i].getProperties();
|
||||
for (int j = 0; j < props.length; j++) {
|
||||
Object aEmbPropVal = props[j].getValue(aval);
|
||||
Object bEmbPropVal = props[j].getValue(bval);
|
||||
if (!ValueUtil.areEqual(aEmbPropVal, bEmbPropVal)) {
|
||||
|
||||
// if one prop is different put the
|
||||
// embedded bean in the map
|
||||
map.put(emb[i].getName(), new ValuePair(aval, bval));
|
||||
}
|
||||
}
|
||||
// recursively diff into the embedded bean
|
||||
BeanDescriptor<?> embDesc = emb[i].getTargetDescriptor();
|
||||
diff(emb[i].getName()+".", map, aval, bval, embDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +104,7 @@ public class DiffHelp {
|
||||
* If the properties are different by null OR if the id value is different,
|
||||
* then add the Assoc One bean to the map.
|
||||
*/
|
||||
private void diffAssocOne(Object a, Object b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
|
||||
private void diffAssocOne(String prefix, EntityBean a, EntityBean b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
|
||||
|
||||
BeanPropertyAssocOne<?>[] ones = desc.propertiesOne();
|
||||
|
||||
@@ -124,20 +113,21 @@ public class DiffHelp {
|
||||
Object bval = ones[i].getValue(b);
|
||||
|
||||
if (!isBothNull(aval, bval)) {
|
||||
String propName = (prefix == null) ? ones[i].getName() : prefix + ones[i].getName();
|
||||
if (isDiffNull(aval, bval)) {
|
||||
// one of them is/was null
|
||||
map.put(ones[i].getName(), new ValuePair(aval, bval));
|
||||
map.put(propName, new ValuePair(aval, bval));
|
||||
|
||||
} else {
|
||||
// check to see if the Id properties
|
||||
// are different
|
||||
BeanDescriptor<?> oneDesc = ones[i].getTargetDescriptor();
|
||||
Object aOneId = oneDesc.getId(aval);
|
||||
Object bOneId = oneDesc.getId(bval);
|
||||
Object aOneId = oneDesc.getId((EntityBean)aval);
|
||||
Object bOneId = oneDesc.getId((EntityBean)bval);
|
||||
|
||||
if (!ValueUtil.areEqual(aOneId, bOneId)) {
|
||||
// the ids are different
|
||||
map.put(ones[i].getName(), new ValuePair(aval, bval));
|
||||
map.put(propName, new ValuePair(aval, bval));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class InternalConfiguration {
|
||||
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
|
||||
serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder, backgroundExecutor);
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
|
||||
@@ -14,6 +14,8 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanFinder;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.BeanIdList;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
@@ -27,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.
|
||||
@@ -41,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 Integer cacheKey;
|
||||
|
||||
private int 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;
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private HashQueryPlan queryPlanHash;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -73,17 +69,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
this.queryEngine = queryEngine;
|
||||
this.query = query;
|
||||
this.readOnly = query.isReadOnly();
|
||||
|
||||
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
|
||||
graphContext.registerSecondaryQueries(query);
|
||||
}
|
||||
|
||||
public void setTotalHits(int totalHits) {
|
||||
query.setTotalHits(totalHits);
|
||||
}
|
||||
|
||||
public void executeSecondaryQueries(int defaultQueryBatch) {
|
||||
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
|
||||
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +84,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* </p>
|
||||
*/
|
||||
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
|
||||
return graphContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
|
||||
return loadContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +105,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* Return the graph context for this query.
|
||||
*/
|
||||
public LoadContext getGraphContext() {
|
||||
return graphContext;
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,12 +154,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
@Override
|
||||
public void initTransIfRequired() {
|
||||
// first check if the query requires its own transaction
|
||||
if (query.createOwnTransaction()) {
|
||||
// using background fetch or query listener etc
|
||||
transaction = ebeanServer.createQueryTransaction();
|
||||
createdTransaction = true;
|
||||
|
||||
} else if (transaction == null) {
|
||||
if (transaction == null) {
|
||||
// maybe a current one
|
||||
transaction = ebeanServer.getCurrentServerTransaction();
|
||||
if (transaction == null) {
|
||||
@@ -179,10 +163,20 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
// initialise the persistenceContext and loadContext
|
||||
this.persistenceContext = getPersistenceContext(query, transaction);
|
||||
this.graphContext.setPersistenceContext(persistenceContext);
|
||||
this.loadContext = new DLoadContext(this);
|
||||
this.loadContext.registerSecondaryQueries(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* For iterate queries reset the persistenceContext and loadContext.
|
||||
*/
|
||||
public void flushPersistenceContextOnIterate() {
|
||||
persistenceContext = new DefaultPersistenceContext();
|
||||
loadContext.setPersistenceContext(persistenceContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TransactionContext either explicitly set on the query or
|
||||
* transaction scoped.
|
||||
@@ -204,19 +198,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* </p>
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction && !backgroundFetching) {
|
||||
if (createdTransaction) {
|
||||
// we can rollback as readOnly transaction
|
||||
transaction.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This query is using background fetching.
|
||||
*/
|
||||
public void setBackgroundFetching() {
|
||||
backgroundFetching = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a find by id (rather than List Set or Map).
|
||||
*/
|
||||
@@ -279,12 +266,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
public Map<?, ?> findMap() {
|
||||
String mapKey = query.getMapKey();
|
||||
if (mapKey == null) {
|
||||
BeanProperty[] ids = beanDescriptor.propertiesId();
|
||||
if (ids.length == 1) {
|
||||
query.setMapKey(ids[0].getName());
|
||||
BeanProperty idProp = beanDescriptor.getIdProperty();
|
||||
if (idProp != null) {
|
||||
query.setMapKey(idProp.getName());
|
||||
} else {
|
||||
String msg = "No mapKey specified for query";
|
||||
throw new PersistenceException(msg);
|
||||
throw new PersistenceException("No mapKey specified for query");
|
||||
}
|
||||
}
|
||||
return (Map<?, ?>) queryEngine.findMany(this);
|
||||
@@ -332,7 +318,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* with just the bind variables changing.
|
||||
* </p>
|
||||
*/
|
||||
public int getQueryPlanHash() {
|
||||
public HashQueryPlan getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
}
|
||||
|
||||
@@ -356,17 +342,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.getType() == null) {
|
||||
// the query plan and bind values must be the same
|
||||
cacheKey = Integer.valueOf(query.queryHash());
|
||||
cacheKey = query.queryHash();
|
||||
|
||||
} else {
|
||||
// additionally the return type (List/Set/Map) must be the same
|
||||
cacheKey = Integer.valueOf(31 * query.queryHash() + query.getType().hashCode());
|
||||
}
|
||||
|
||||
// TODO: Sort out returning BeanCollection from L2 cache
|
||||
return null;
|
||||
return beanDescriptor.queryCacheGet(cacheKey);
|
||||
}
|
||||
|
||||
public void putToQueryCache(BeanCollection<T> queryResult) {
|
||||
@@ -389,4 +367,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the request wants to log the secondary queries (test purpose).
|
||||
*/
|
||||
public boolean isLogSecondaryQuery() {
|
||||
return query.isLogSecondaryQuery();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,17 +12,17 @@ import com.avaje.ebeaninternal.server.persist.PersistExecute;
|
||||
public abstract class PersistRequest extends BeanRequest implements BatchPostExecute {
|
||||
|
||||
public enum Type {
|
||||
INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
|
||||
DETERMINE, INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL
|
||||
};
|
||||
|
||||
boolean persistCascade;
|
||||
protected boolean persistCascade;
|
||||
|
||||
/**
|
||||
* One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL.
|
||||
*/
|
||||
Type type;
|
||||
protected Type type;
|
||||
|
||||
final PersistExecute persistExecute;
|
||||
protected final PersistExecute persistExecute;
|
||||
|
||||
/**
|
||||
* Used by CallableSqlRequest and UpdateSqlRequest.
|
||||
@@ -31,7 +31,7 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
super(server, t);
|
||||
this.persistExecute = persistExecute;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a the request or queue/batch it for later execution.
|
||||
*/
|
||||
@@ -90,14 +90,6 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or
|
||||
* CALLABLESQL.
|
||||
*/
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if save and delete should cascade.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
@@ -19,6 +22,7 @@ import com.avaje.ebeaninternal.api.TransactionEvent;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
import com.avaje.ebeaninternal.server.persist.PersistExecute;
|
||||
import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
@@ -28,150 +32,127 @@ import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
/**
|
||||
* PersistRequest for insert update or delete of a bean.
|
||||
*/
|
||||
public class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
|
||||
|
||||
protected final BeanManager<T> beanManager;
|
||||
private final BeanManager<T> beanManager;
|
||||
|
||||
protected final BeanDescriptor<T> beanDescriptor;
|
||||
private final BeanDescriptor<T> beanDescriptor;
|
||||
|
||||
protected final BeanPersistListener<T> beanPersistListener;
|
||||
private final BeanPersistListener<T> beanPersistListener;
|
||||
|
||||
/**
|
||||
* For per post insert update delete control.
|
||||
*/
|
||||
protected final BeanPersistController controller;
|
||||
private final BeanPersistController controller;
|
||||
|
||||
/**
|
||||
* The bean being persisted.
|
||||
*/
|
||||
private final T bean;
|
||||
|
||||
private final EntityBean entityBean;
|
||||
|
||||
/**
|
||||
* The associated intercept.
|
||||
*/
|
||||
protected final EntityBeanIntercept intercept;
|
||||
private final EntityBeanIntercept intercept;
|
||||
|
||||
/**
|
||||
* The parent bean for unidirectional save.
|
||||
*/
|
||||
protected final Object parentBean;
|
||||
private final Object parentBean;
|
||||
|
||||
protected final boolean isDirty;
|
||||
private final boolean dirty;
|
||||
|
||||
/**
|
||||
* The bean being persisted.
|
||||
*/
|
||||
protected final T bean;
|
||||
|
||||
/**
|
||||
* Old values used for concurrency checking.
|
||||
*/
|
||||
protected T oldValues;
|
||||
|
||||
/**
|
||||
* The concurrency mode used for update or delete.
|
||||
*/
|
||||
protected ConcurrencyMode concurrencyMode;
|
||||
|
||||
protected final Set<String> loadedProps;
|
||||
private ConcurrencyMode concurrencyMode;
|
||||
|
||||
/**
|
||||
* The unique id used for logging summary.
|
||||
*/
|
||||
protected Object idValue;
|
||||
private Object idValue;
|
||||
|
||||
/**
|
||||
* Hash value used to handle cascade delete both ways in a relationship.
|
||||
*/
|
||||
protected Integer beanHash;
|
||||
protected Integer beanIdentityHash;
|
||||
private Integer beanHash;
|
||||
|
||||
protected final Set<String> changedProps;
|
||||
private boolean notifyCache;
|
||||
|
||||
protected boolean notifyCache;
|
||||
|
||||
private boolean statelessUpdate;
|
||||
private boolean deleteMissingChildren;
|
||||
private boolean updateNullProperties;
|
||||
|
||||
private final Set<String> dirtyPropertyNames;
|
||||
|
||||
/**
|
||||
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
|
||||
* appropriate caches are updated in that case.
|
||||
*/
|
||||
private boolean updatedManysOnly;
|
||||
|
||||
/**
|
||||
* Used for forced update of a bean.
|
||||
* Many properties that were cascade saved (and hence might need caches updated later).
|
||||
*/
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, Set<String> updateProps, ConcurrencyMode concurrencyMode) {
|
||||
private List<BeanPropertyAssocMany<?>> updatedManys;
|
||||
|
||||
super(server, t, persistExecute);
|
||||
this.beanManager = mgr;
|
||||
this.beanDescriptor = mgr.getBeanDescriptor();
|
||||
this.beanPersistListener = beanDescriptor.getPersistListener();
|
||||
this.bean = bean;
|
||||
this.parentBean = parentBean;
|
||||
|
||||
this.controller = beanDescriptor.getPersistController();
|
||||
this.concurrencyMode = beanDescriptor.getConcurrencyMode();
|
||||
|
||||
this.concurrencyMode = concurrencyMode;
|
||||
this.loadedProps = updateProps;
|
||||
this.changedProps = updateProps;
|
||||
this.isDirty = true;
|
||||
this.oldValues = bean;
|
||||
if (bean instanceof EntityBean) {
|
||||
this.intercept = ((EntityBean) bean)._ebean_getIntercept();
|
||||
} else {
|
||||
this.intercept = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
this.entityBean = (EntityBean)bean;
|
||||
this.intercept = entityBean._ebean_getIntercept();
|
||||
this.beanManager = mgr;
|
||||
this.beanDescriptor = mgr.getBeanDescriptor();
|
||||
this.beanPersistListener = beanDescriptor.getPersistListener();
|
||||
|
||||
if (PersistRequest.Type.DETERMINE != type) {
|
||||
this.type = type;
|
||||
} else {
|
||||
// determine mode during cascade save (supporting stateless update)
|
||||
this.type = beanDescriptor.isInsertMode(intercept) ? Type.INSERT : Type.UPDATE;
|
||||
this.persistCascade = t.isPersistCascade();
|
||||
}
|
||||
|
||||
if (this.type == Type.UPDATE && intercept.isNew() ) {
|
||||
// 'stateless update' - set bean up for doing update
|
||||
intercept.setNewBeanForUpdate();
|
||||
}
|
||||
|
||||
// derive the set of property names now as we will pass them to a beanPersistListener later
|
||||
this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames();
|
||||
|
||||
this.bean = bean;
|
||||
this.parentBean = parentBean;
|
||||
|
||||
this.controller = beanDescriptor.getPersistController();
|
||||
this.concurrencyMode = beanDescriptor.getConcurrencyMode();
|
||||
this.intercept = ((EntityBean) bean)._ebean_getIntercept();
|
||||
if (intercept.isReference()) {
|
||||
// allowed to delete reference objects
|
||||
// with no concurrency checking
|
||||
this.concurrencyMode = ConcurrencyMode.NONE;
|
||||
}
|
||||
// this is ok to not use isNewOrDirty() as used for updates only
|
||||
this.isDirty = intercept.isDirty();
|
||||
if (!isDirty) {
|
||||
this.changedProps = intercept.getChangedProps();
|
||||
} else {
|
||||
// merge changed properties on the bean with changed embedded beans
|
||||
Set<String> beanChangedProps = intercept.getChangedProps();
|
||||
Set<String> dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean);
|
||||
this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded);
|
||||
}
|
||||
this.loadedProps = intercept.getLoadedProps();
|
||||
this.oldValues = (T) intercept.getOldValues();
|
||||
this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept);
|
||||
this.dirty = intercept.isDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the changed properties for the bean and embedded beans.
|
||||
* Return true if this is an insert request.
|
||||
*/
|
||||
private Set<String> mergeChangedProperties(Set<String> beanChangedProps, Set<String> embChanged) {
|
||||
if (embChanged == null) {
|
||||
return beanChangedProps;
|
||||
} else if (beanChangedProps == null) {
|
||||
return embChanged;
|
||||
} else {
|
||||
beanChangedProps.addAll(embChanged);
|
||||
return beanChangedProps;
|
||||
}
|
||||
}
|
||||
public boolean isInsert() {
|
||||
return Type.INSERT == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getLoadedProperties() {
|
||||
return intercept.getLoadedPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getUpdatedProperties() {
|
||||
return intercept.getDirtyPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ValuePair> getUpdatedValues() {
|
||||
return intercept.getDirtyValues();
|
||||
}
|
||||
|
||||
public boolean isNotify(TransactionEvent txnEvent) {
|
||||
this.notifyCache = beanDescriptor.isCacheNotify();
|
||||
return notifyCache || isNotifyPersistListener();
|
||||
}
|
||||
|
||||
public boolean isNotifyCache() {
|
||||
return notifyCache;
|
||||
}
|
||||
|
||||
public boolean isNotifyPersistListener() {
|
||||
return beanPersistListener != null;
|
||||
}
|
||||
@@ -183,10 +164,10 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
if (notifyCache) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
beanDescriptor.cacheInsert(idValue, this);
|
||||
beanDescriptor.cacheHandleInsert(idValue, this);
|
||||
break;
|
||||
case UPDATE:
|
||||
beanDescriptor.cacheUpdate(idValue, this);
|
||||
beanDescriptor.cacheHandleUpdate(idValue, this);
|
||||
break;
|
||||
case DELETE:
|
||||
// Bean deleted from cache early via postDelete()
|
||||
@@ -199,7 +180,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
|
||||
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
|
||||
|
||||
beanPersistMap.add(beanDescriptor, type, idValue);
|
||||
beanPersistMap.add(beanDescriptor, type, idValue);
|
||||
}
|
||||
|
||||
public boolean notifyLocalPersistListener() {
|
||||
@@ -212,7 +193,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
return beanPersistListener.inserted(bean);
|
||||
|
||||
case UPDATE:
|
||||
return beanPersistListener.updated(bean, getUpdatedProperties());
|
||||
return beanPersistListener.updated(bean, dirtyPropertyNames);
|
||||
|
||||
case DELETE:
|
||||
return beanPersistListener.deleted(bean);
|
||||
@@ -229,7 +210,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
|
||||
/**
|
||||
* Return true if this bean has been already been persisted
|
||||
* (inserted/updated or deleted) in this transaction.
|
||||
* (inserted or updated) in this transaction.
|
||||
*/
|
||||
public boolean isRegisteredBean() {
|
||||
return transaction.isRegisteredBean(bean);
|
||||
@@ -247,7 +228,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
*/
|
||||
private Integer getBeanHash() {
|
||||
if (beanHash == null) {
|
||||
Object id = beanDescriptor.getId(bean);
|
||||
Object id = beanDescriptor.getId(entityBean);
|
||||
int hc = 31 * bean.getClass().getName().hashCode();
|
||||
if (id != null) {
|
||||
hc += id.hashCode();
|
||||
@@ -276,21 +257,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or
|
||||
* CALLABLESQL.
|
||||
*/
|
||||
@Override
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
notifyCache = beanDescriptor.isCacheNotify();
|
||||
if (type == Type.DELETE || type == Type.UPDATE) {
|
||||
if (oldValues == null) {
|
||||
oldValues = bean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BeanManager<T> getBeanManager() {
|
||||
return beanManager;
|
||||
}
|
||||
@@ -302,13 +268,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a stateless update.
|
||||
*/
|
||||
public boolean isStatelessUpdate() {
|
||||
return statelessUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a stateless update should also delete any missing details
|
||||
* beans.
|
||||
@@ -318,25 +277,10 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if null properties should be updated (treated as loaded) for
|
||||
* stateless updates.
|
||||
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
|
||||
*/
|
||||
public boolean isUpdateNullProperties() {
|
||||
return updateNullProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if this is a stateless update.
|
||||
* <p>
|
||||
* By Stateless it means that the bean was not previously fetched (and so
|
||||
* does not have it's previous state) so we are doing an update on a bean
|
||||
* that was probably created from JSON or XML.
|
||||
* </p>
|
||||
*/
|
||||
public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren, boolean updateNullProperties) {
|
||||
this.statelessUpdate = statelessUpdate;
|
||||
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
|
||||
this.deleteMissingChildren = deleteMissingChildren;
|
||||
this.updateNullProperties = updateNullProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,7 +288,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
* for EntityBeans that have not been modified.
|
||||
*/
|
||||
public boolean isDirty() {
|
||||
return isDirty;
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,20 +298,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
return concurrencyMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loaded properties when generated values has added properties such as
|
||||
* created and updated timestamps.
|
||||
*/
|
||||
public void setLoadedProps(Set<String> additionalProps) {
|
||||
if (intercept != null) {
|
||||
intercept.setLoadedProps(additionalProps);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getLoadedProperties() {
|
||||
return loadedProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the request. This is typically the bean class
|
||||
* name or the base table for MapBeans.
|
||||
@@ -387,25 +317,22 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
public EntityBean getEntityBean() {
|
||||
return entityBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id value for the bean.
|
||||
*/
|
||||
public Object getBeanId() {
|
||||
return beanDescriptor.getId(bean);
|
||||
return beanDescriptor.getId(entityBean);
|
||||
}
|
||||
|
||||
public BeanDelta createDeltaBean() {
|
||||
return new BeanDelta(beanDescriptor, getBeanId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old values bean. This is used to perform optimistic concurrency
|
||||
* checking on updates and deletes.
|
||||
*/
|
||||
public T getOldValues() {
|
||||
return oldValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent bean for cascading save with unidirectional
|
||||
* relationship.
|
||||
@@ -434,11 +361,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
* bean).
|
||||
*/
|
||||
public boolean isLoadedProperty(BeanProperty prop) {
|
||||
if (loadedProps == null) {
|
||||
return true;
|
||||
} else {
|
||||
return loadedProps.contains(prop.getName());
|
||||
}
|
||||
return intercept.isLoadedProperty(prop.getPropertyIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -485,13 +408,8 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
*/
|
||||
public void setGeneratedKey(Object idValue) {
|
||||
if (idValue != null) {
|
||||
|
||||
// set back to the bean so that we can use the same bean later
|
||||
// for update [refer ebeanIntercept.setLoaded(true)].
|
||||
idValue = beanDescriptor.convertSetId(idValue, bean);
|
||||
|
||||
// remember it for logging summary
|
||||
this.idValue = idValue;
|
||||
this.idValue = beanDescriptor.convertSetId(idValue, entityBean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +436,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
// Delete the bean from the PersistenceContent
|
||||
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
|
||||
// Delete from cache early even if transaction fails
|
||||
beanDescriptor.cacheDelete(idValue, this);
|
||||
beanDescriptor.cacheHandleDelete(idValue, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -597,18 +515,18 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
* </p>
|
||||
*/
|
||||
public ConcurrencyMode determineConcurrencyMode() {
|
||||
if (loadedProps != null) {
|
||||
// 'partial bean' update/delete...
|
||||
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
|
||||
// check the version property was loaded
|
||||
BeanProperty prop = beanDescriptor.firstVersionProperty();
|
||||
if (prop != null && loadedProps.contains(prop.getName())) {
|
||||
// OK to use version property
|
||||
} else {
|
||||
concurrencyMode = ConcurrencyMode.ALL;
|
||||
}
|
||||
|
||||
// 'partial bean' update/delete...
|
||||
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
|
||||
// check the version property was loaded
|
||||
BeanProperty prop = beanDescriptor.getVersionProperty();
|
||||
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
|
||||
// OK to use version property
|
||||
} else {
|
||||
concurrencyMode = ConcurrencyMode.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return concurrencyMode;
|
||||
}
|
||||
|
||||
@@ -619,7 +537,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
* </p>
|
||||
*/
|
||||
public boolean isDynamicUpdateSql() {
|
||||
return beanDescriptor.isUpdateChangesOnly() || (loadedProps != null);
|
||||
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,37 +548,74 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
* </p>
|
||||
*/
|
||||
public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) {
|
||||
if (beanDescriptor.isUpdateChangesOnly()) {
|
||||
return new GenerateDmlRequest(emptyStringAsNull, changedProps, loadedProps, oldValues);
|
||||
} else {
|
||||
return new GenerateDmlRequest(emptyStringAsNull, loadedProps, loadedProps, oldValues);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the updated properties. If this returns null then all the
|
||||
* properties on the bean where updated.
|
||||
*/
|
||||
public Set<String> getUpdatedProperties() {
|
||||
if (changedProps != null) {
|
||||
return changedProps;
|
||||
}
|
||||
return loadedProps;
|
||||
return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the property value has changed and if so include it in the
|
||||
* update.
|
||||
*/
|
||||
public boolean hasChanged(BeanProperty prop) {
|
||||
if (changedProps == null) {
|
||||
return false;
|
||||
}
|
||||
return changedProps.contains(prop.getName());
|
||||
public boolean isAddToUpdate(BeanProperty prop) {
|
||||
return intercept.isDirtyProperty(prop.getPropertyIndex());
|
||||
}
|
||||
|
||||
public List<DerivedRelationshipData> getDerivedRelationships() {
|
||||
return transaction.getDerivedRelationship(bean);
|
||||
public List<DerivedRelationshipData> getDerivedRelationships() {
|
||||
return transaction.getDerivedRelationship(bean);
|
||||
}
|
||||
|
||||
public void postInsert() {
|
||||
// mark all properties as loaded after an insert to support immediate update
|
||||
int len = intercept.getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
intercept.setLoadedProperty(i);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReference() {
|
||||
return beanDescriptor.isReference(intercept);
|
||||
}
|
||||
|
||||
/**
|
||||
* This many property has been cascade saved. Keep note of this and update the 'many property'
|
||||
* cache on post commit.
|
||||
*/
|
||||
public void addUpdatedManyProperty(BeanPropertyAssocMany<?> updatedAssocMany) {
|
||||
//if (notifyCache) {
|
||||
if (updatedManys == null) {
|
||||
updatedManys = new ArrayList<BeanPropertyAssocMany<?>>(5);
|
||||
}
|
||||
updatedManys.add(updatedAssocMany);
|
||||
//}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of cascade updated many properties (can be null).
|
||||
*/
|
||||
public List<BeanPropertyAssocMany<?>> getUpdatedManyCollections() {
|
||||
return updatedManys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any of its many properties where cascade saved and hence we need to update related
|
||||
* many property caches.
|
||||
*/
|
||||
public void checkUpdatedManysOnly() {
|
||||
if (!dirty && updatedManys != null) {
|
||||
// set the flag and register for post commit processing if there
|
||||
// is caching or registered listeners
|
||||
if (idValue == null) {
|
||||
this.idValue = beanDescriptor.getId(entityBean);
|
||||
}
|
||||
updatedManysOnly = true;
|
||||
addEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if only many properties where updated.
|
||||
*/
|
||||
public boolean isUpdatedManysOnly() {
|
||||
return updatedManysOnly;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user