mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Fixes for #94 - Additional API - added update(Collection beans); and insert(Collection beans); and #93 - ebean.properties defaultDeleteMissingChildren moved to updatesDeleteMissingChildren
This commit is contained in:
@@ -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
|
||||
@@ -462,48 +462,59 @@ public final class Ebean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean updating the non-null properties.
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the beans in the collection.
|
||||
*/
|
||||
public static void update(Collection<?> beans) throws OptimisticLockException {
|
||||
serverMgr.getPrimaryServer().update(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all the beans from an Iterator.
|
||||
*/
|
||||
@@ -514,8 +525,8 @@ public final class Ebean {
|
||||
/**
|
||||
* Save all the beans from a Collection.
|
||||
*/
|
||||
public static int save(Collection<?> c) throws OptimisticLockException {
|
||||
return save(c.iterator());
|
||||
public static int save(Collection<?> beans) throws OptimisticLockException {
|
||||
return serverMgr.getPrimaryServer().save(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,9 +85,12 @@ import com.avaje.ebean.text.json.JsonContext;
|
||||
public interface EbeanServer {
|
||||
|
||||
/**
|
||||
* Shutdown the EbeanServer.
|
||||
* Shutdown the EbeanServer programmatically.
|
||||
* <p>
|
||||
* If the under underlying DataSource is the EbeanORM implementation then you
|
||||
* This method is not normally required. Ebean registers a shutdown hook and shuts down cleanly.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the under underlying DataSource is the Ebean implementation then you
|
||||
* also have the option of shutting down the DataSource and deregistering the
|
||||
* JDBC driver.
|
||||
* </p>
|
||||
@@ -410,12 +413,25 @@ public interface EbeanServer {
|
||||
public <T> T find(Class<T> beanType, Object uid);
|
||||
|
||||
/**
|
||||
* Get a reference Object (see {@link Ebean#getReference(Class, Object)}.
|
||||
* Get a reference bean (see {@link Ebean#getReference(Class, Object)}.
|
||||
* <p>
|
||||
* This will not perform a query against the database.
|
||||
* </p>
|
||||
* <pre class="code">
|
||||
* Product product = Ebean.getReference(Product.class, 1);
|
||||
*
|
||||
* @see Ebean#getReference(Class, Object)
|
||||
* // You can get the id without causing a fetch/lazy load
|
||||
* Integer productId = product.getId();
|
||||
*
|
||||
* // If you try to get any other property a fetch/lazy loading will occur
|
||||
* // This will cause a query to execute...
|
||||
* String name = product.getName();
|
||||
* </pre>
|
||||
*
|
||||
* @param beanType
|
||||
* the type of entity bean
|
||||
* @param id
|
||||
* the id value
|
||||
*/
|
||||
public <T> T getReference(Class<T> beanType, Object uid);
|
||||
|
||||
@@ -428,21 +444,21 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Return the Id values of the query as a List.
|
||||
*/
|
||||
public <T> List<Object> findIds(Query<T> query, Transaction t);
|
||||
public <T> List<Object> findIds(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return a QueryIterator for the query. This is similar to findVisit in that
|
||||
* not all the result beans need to be held in memory at the same time and as
|
||||
* such is go for processing large queries.
|
||||
*/
|
||||
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t);
|
||||
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the results. This is similar to findIterate in
|
||||
* that not all the result beans need to be held in memory at the same time
|
||||
* and as such is go for processing large queries.
|
||||
*/
|
||||
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t);
|
||||
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute a query returning a list of beans.
|
||||
@@ -477,7 +493,7 @@ public interface EbeanServer {
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the row count query
|
||||
*/
|
||||
public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction t);
|
||||
public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find Id's query in a background thread.
|
||||
@@ -493,7 +509,7 @@ public interface EbeanServer {
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the list of Id's
|
||||
*/
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t);
|
||||
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find list query in a background thread.
|
||||
@@ -509,7 +525,7 @@ public interface EbeanServer {
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
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.
|
||||
@@ -525,12 +541,12 @@ public interface EbeanServer {
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
public SqlFutureList findFutureList(SqlQuery query, Transaction t);
|
||||
public SqlFutureList findFutureList(SqlQuery query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Find using a PagingList with explicit transaction and pageSize.
|
||||
*/
|
||||
public <T> PagingList<T> findPagingList(Query<T> query, Transaction t, int pageSize);
|
||||
public <T> PagingList<T> findPagingList(Query<T> query, Transaction transaction, int pageSize);
|
||||
|
||||
/**
|
||||
* Execute the query returning a set of entity beans.
|
||||
@@ -677,7 +693,7 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Save all the beans in the collection.
|
||||
*/
|
||||
public int save(Collection<?> it) throws OptimisticLockException;
|
||||
public int save(Collection<?> beans) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete the bean.
|
||||
@@ -704,7 +720,7 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Delete the bean given its type and id with an explicit transaction.
|
||||
*/
|
||||
public int delete(Class<?> beanType, Object id, Transaction t);
|
||||
public int delete(Class<?> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Delete several beans given their type and id values.
|
||||
@@ -715,7 +731,7 @@ public interface EbeanServer {
|
||||
* Delete several beans given their type and id values with an explicit
|
||||
* transaction.
|
||||
*/
|
||||
public void delete(Class<?> beanType, Collection<?> ids, Transaction t);
|
||||
public void delete(Class<?> beanType, Collection<?> ids, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute a SQL Update Delete or Insert statement using the current
|
||||
@@ -784,133 +800,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 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 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.
|
||||
|
||||
*/
|
||||
public void update(Object bean, Transaction t, boolean deleteMissingChildren);
|
||||
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.
|
||||
|
||||
@@ -511,7 +511,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* For forced update on a 'New' bean move set all the changedProperties to loaded properties.
|
||||
* For forced update on a 'New' bean set all the loaded properties to changed.
|
||||
*/
|
||||
public void setNewBeanForUpdate() {
|
||||
|
||||
|
||||
@@ -66,7 +66,10 @@ import com.avaje.ebean.util.ClassUtil;
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class ServerConfig {
|
||||
/** The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100 */
|
||||
|
||||
/**
|
||||
* The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100
|
||||
*/
|
||||
private final static int DEFAULT_QUERY_BATCH_SIZE = 100;
|
||||
|
||||
/**
|
||||
@@ -112,13 +115,19 @@ public class ServerConfig {
|
||||
*/
|
||||
private List<String> searchJars = new ArrayList<String>();
|
||||
|
||||
/** The autofetch config. */
|
||||
/**
|
||||
* Config controlling the autofetch behaviour.
|
||||
*/
|
||||
private AutofetchConfig autofetchConfig = new AutofetchConfig();
|
||||
|
||||
/** The database platform name. */
|
||||
/**
|
||||
* The database platform name. Used to imply a DatabasePlatform to use.
|
||||
*/
|
||||
private String databasePlatformName;
|
||||
|
||||
/** The database platform. */
|
||||
/**
|
||||
* The database platform.
|
||||
*/
|
||||
private DatabasePlatform databasePlatform;
|
||||
|
||||
/**
|
||||
@@ -130,10 +139,14 @@ public class ServerConfig {
|
||||
|
||||
private int persistBatchSize = 20;
|
||||
|
||||
/** The default batch size for lazy loading */
|
||||
/**
|
||||
* The default batch size for lazy loading
|
||||
*/
|
||||
private int lazyLoadBatchSize = 1;
|
||||
|
||||
/** The query batch size. */
|
||||
/**
|
||||
* The query batch size.
|
||||
*/
|
||||
private int queryBatchSize = -1;
|
||||
|
||||
private boolean ddlGenerate;
|
||||
@@ -152,26 +165,45 @@ 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;
|
||||
|
||||
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
|
||||
private List<BeanPersistListener<?>> persistListeners = new ArrayList<BeanPersistListener<?>>();
|
||||
@@ -931,8 +963,23 @@ public class ServerConfig {
|
||||
public void setUpdateChangesOnly(boolean updateChangesOnly) {
|
||||
this.updateChangesOnly = updateChangesOnly;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return true if updates by default delete missing children when cascading save to a OneToMany or
|
||||
* ManyToMany. When not set this defaults to true.
|
||||
*/
|
||||
public boolean isUpdatesDeleteMissingChildren() {
|
||||
return updatesDeleteMissingChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if updates by default delete missing children when cascading save to a OneToMany or
|
||||
* ManyToMany. When not set this defaults to true.
|
||||
*/
|
||||
public void setUpdatesDeleteMissingChildren(boolean updatesDeleteMissingChildren) {
|
||||
this.updatesDeleteMissingChildren = updatesDeleteMissingChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ebeanServer should collection query statistics by ObjectGraphNode.
|
||||
*/
|
||||
@@ -1237,6 +1284,9 @@ public class ServerConfig {
|
||||
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);
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanLoader;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.core.PstmtBatch;
|
||||
@@ -30,13 +31,15 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
public void shutdownManaged();
|
||||
|
||||
/**
|
||||
* Return true if query origins should be collected.
|
||||
*/
|
||||
public boolean isCollectQueryOrigins();
|
||||
|
||||
/**
|
||||
* Return true if DeleteMissingChildren defaults to true for stateless
|
||||
* updates.
|
||||
* Return the server configuration.
|
||||
*/
|
||||
public boolean isDefaultDeleteMissingChildren();
|
||||
public ServerConfig getServerConfig();
|
||||
|
||||
/**
|
||||
* Return the DatabasePlatform for this server.
|
||||
|
||||
@@ -21,18 +21,18 @@ public abstract class BeanRequest {
|
||||
/**
|
||||
* The server processing the request.
|
||||
*/
|
||||
final SpiEbeanServer ebeanServer;
|
||||
protected final SpiEbeanServer ebeanServer;
|
||||
|
||||
final String serverName;
|
||||
protected final String serverName;
|
||||
|
||||
/**
|
||||
* The transaction this is part of.
|
||||
*/
|
||||
SpiTransaction transaction;
|
||||
protected SpiTransaction transaction;
|
||||
|
||||
boolean createdTransaction;
|
||||
protected boolean createdTransaction;
|
||||
|
||||
boolean readOnly;
|
||||
protected boolean readOnly;
|
||||
|
||||
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
|
||||
@@ -90,7 +90,6 @@ 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;
|
||||
@@ -105,7 +104,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;
|
||||
@@ -125,6 +123,8 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
|
||||
private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15);
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final String serverName;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
@@ -143,8 +143,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
|
||||
private final boolean defaultDeleteMissingChildren;
|
||||
|
||||
/**
|
||||
* Handles the save, delete, updateSql CallableSql.
|
||||
*/
|
||||
@@ -228,8 +226,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
ServerConfig serverConfig = config.getServerConfig();
|
||||
|
||||
this.serverConfig = config.getServerConfig();
|
||||
this.objectGraphStats = new ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics>();
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
this.serverCacheManager = cache;
|
||||
@@ -251,9 +248,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
|
||||
this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5);
|
||||
|
||||
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();
|
||||
@@ -309,10 +303,6 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return collectQueryOrigins;
|
||||
}
|
||||
|
||||
public boolean isDefaultDeleteMissingChildren() {
|
||||
return defaultDeleteMissingChildren;
|
||||
}
|
||||
|
||||
public int getLazyLoadBatchSize() {
|
||||
return lazyLoadBatchSize;
|
||||
}
|
||||
@@ -321,6 +311,10 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
return pstmtBatch;
|
||||
}
|
||||
|
||||
public ServerConfig getServerConfig() {
|
||||
return serverConfig;
|
||||
}
|
||||
|
||||
public DatabasePlatform getDatabasePlatform() {
|
||||
return databasePlatform;
|
||||
}
|
||||
@@ -1571,63 +1565,109 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
* Save the bean with an explicit transaction.
|
||||
*/
|
||||
public void save(Object bean, Transaction 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an update using the bean explicitly stating which properties to
|
||||
* include in the update.
|
||||
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
|
||||
*/
|
||||
public void update(Object bean, Transaction t) {
|
||||
update(bean, t, defaultDeleteMissingChildren);
|
||||
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, Transaction t, boolean deleteMissingChildren) {
|
||||
|
||||
persister.forceUpdate(checkEntityBean(bean), t, deleteMissingChildren);
|
||||
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 all beans in the collection.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
persister.forceInsert(checkEntityBean(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 NullPointerException(Message.msg("bean.isnull"));
|
||||
throw new IllegalArgumentException(Message.msg("bean.isnull"));
|
||||
}
|
||||
if (bean instanceof EntityBean == false) {
|
||||
throw new IllegalArgumentException("Was expecting an EntityBean but got a "+bean.getClass());
|
||||
@@ -1735,6 +1775,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.
|
||||
*/
|
||||
|
||||
@@ -15,14 +15,14 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
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.
|
||||
|
||||
@@ -32,66 +32,64 @@ 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.
|
||||
*/
|
||||
protected final T bean;
|
||||
private final T bean;
|
||||
|
||||
protected final EntityBean entityBean;
|
||||
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 dirty;
|
||||
private final boolean dirty;
|
||||
|
||||
protected ConcurrencyMode concurrencyMode;
|
||||
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 boolean notifyCache;
|
||||
private boolean notifyCache;
|
||||
|
||||
private boolean statelessUpdate;
|
||||
private boolean deleteMissingChildren;
|
||||
|
||||
private final Set<String> dirtyPropertyNames;
|
||||
|
||||
/**
|
||||
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
|
||||
* appropriate cache updates occur in that case.
|
||||
* appropriate caches are updated in that case.
|
||||
*/
|
||||
private boolean updatedManysOnly;
|
||||
|
||||
/**
|
||||
* Many properties that were cascade saved (and hence might need cache update later).
|
||||
* Many properties that were cascade saved (and hence might need caches updated later).
|
||||
*/
|
||||
private List<BeanPropertyAssocMany<?>> updatedManys;
|
||||
|
||||
@@ -108,21 +106,23 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
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(intercept);
|
||||
// this is ok to not use isNewOrDirty() as used for updates only
|
||||
this.dirty = intercept.isDirty();
|
||||
}
|
||||
|
||||
@@ -268,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.
|
||||
@@ -284,15 +277,9 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
|
||||
*/
|
||||
public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren) {
|
||||
this.statelessUpdate = statelessUpdate;
|
||||
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
|
||||
this.deleteMissingChildren = deleteMissingChildren;
|
||||
}
|
||||
|
||||
@@ -609,8 +596,8 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference bean was saved. Check if any of its many properties where
|
||||
* cascade saved and hence we need to update related many property caches.
|
||||
* 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) {
|
||||
|
||||
@@ -14,15 +14,20 @@ import com.avaje.ebean.bean.EntityBean;
|
||||
*/
|
||||
public interface Persister {
|
||||
|
||||
/**
|
||||
* Update the bean.
|
||||
*/
|
||||
public void update(EntityBean entityBean, Transaction t);
|
||||
|
||||
/**
|
||||
* Force an Update using the given bean.
|
||||
* Update the bean specifying deleteMissingChildren.
|
||||
*/
|
||||
public void forceUpdate(EntityBean entityBean, Transaction t, boolean deleteMissingChildren);
|
||||
public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren);
|
||||
|
||||
/**
|
||||
* Force an Insert using the given bean.
|
||||
*/
|
||||
public void forceInsert(EntityBean entityBean, Transaction t);
|
||||
public void insert(EntityBean entityBean, Transaction t);
|
||||
|
||||
/**
|
||||
* Insert or update the bean depending on its state.
|
||||
|
||||
@@ -207,8 +207,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
* Derived list of properties that are used for version concurrency checking.
|
||||
*/
|
||||
private final BeanProperty versionProperty;
|
||||
|
||||
private final int versionPropertyIndex;
|
||||
|
||||
|
||||
/**
|
||||
* Properties local to this type (not from a super type).
|
||||
*/
|
||||
@@ -436,7 +437,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
this.versionPropertyIndex = (versionProperty == null) ? -1 : ebi.findProperty(versionProperty.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an entity bean that is used as a prototype/factory to create new instances.
|
||||
*/
|
||||
@@ -1818,6 +1819,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
|
||||
}
|
||||
|
||||
public boolean isInsertMode(EntityBeanIntercept ebi) {
|
||||
|
||||
if (ebi.isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// determine based on Id property
|
||||
if (idProperty.isEmbedded()) {
|
||||
return !ebi.isLoaded();
|
||||
}
|
||||
|
||||
@@ -107,6 +107,13 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property value as an entity bean.
|
||||
*/
|
||||
public EntityBean getValueAsEntityBean(EntityBean owner) {
|
||||
return (EntityBean)getValue(owner);
|
||||
}
|
||||
|
||||
public void setRelationshipProperty(BeanPropertyAssocMany<?> relationshipProperty){
|
||||
this.relationshipProperty = relationshipProperty;
|
||||
|
||||
@@ -71,10 +71,12 @@ public final class DefaultPersister implements Persister {
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
private final boolean updatesDeleteMissingChildren;
|
||||
|
||||
public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch) {
|
||||
|
||||
this.server = server;
|
||||
this.updatesDeleteMissingChildren = server.getServerConfig().isUpdatesDeleteMissingChildren();
|
||||
this.beanDescriptorManager = descMgr;
|
||||
this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch);
|
||||
}
|
||||
@@ -151,21 +153,34 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an Update using the given bean.
|
||||
* Update the bean.
|
||||
*/
|
||||
public void forceUpdate(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) {
|
||||
public void update(EntityBean entityBean, Transaction t) {
|
||||
update(entityBean, t, updatesDeleteMissingChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bean specifying deleteMissingChildren.
|
||||
*/
|
||||
public void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) {
|
||||
|
||||
PersistRequestBean<?> req = createRequest(entityBean, t, null, PersistRequest.Type.UPDATE);
|
||||
if (req.isReference()) {
|
||||
// skip update as only got the Id property set
|
||||
return;
|
||||
}
|
||||
req.setStatelessUpdate(true, deleteMissingChildren);
|
||||
req.setDeleteMissingChildren(deleteMissingChildren);
|
||||
try {
|
||||
req.initTransIfRequired();
|
||||
update(req);
|
||||
|
||||
if (req.isReference()) {
|
||||
// its a reference so see if there are manys to save...
|
||||
if (req.isPersistCascade()) {
|
||||
saveAssocMany(false, req);
|
||||
}
|
||||
req.checkUpdatedManysOnly();
|
||||
|
||||
} else {
|
||||
update(req);
|
||||
}
|
||||
|
||||
req.commitTransIfRequired();
|
||||
// finished a 'normal' update
|
||||
return;
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
@@ -174,14 +189,22 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update the bean.
|
||||
*/
|
||||
public void save(EntityBean bean, Transaction t) {
|
||||
saveRecurse(bean, t, null);
|
||||
if (bean._ebean_getIntercept().isLoaded()) {
|
||||
// deleteMissingChildren is false when using 'save' on 'loaded' beans
|
||||
update(bean, t, false);
|
||||
} else {
|
||||
insert(bean, t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly specify to insert this bean.
|
||||
* Insert this bean.
|
||||
*/
|
||||
public void forceInsert(EntityBean bean, Transaction t) {
|
||||
public void insert(EntityBean bean, Transaction t) {
|
||||
|
||||
PersistRequestBean<?> req = createRequest(bean, t, null, PersistRequest.Type.INSERT);
|
||||
try {
|
||||
@@ -195,43 +218,17 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
private void saveRecurse(Object bean, Transaction t, Object parentBean) {
|
||||
if (bean == null) {
|
||||
throw new NullPointerException(Message.msg("bean.isnull"));
|
||||
}
|
||||
|
||||
if (bean instanceof EntityBean == false) {
|
||||
throw new IllegalArgumentException("This bean is of type ["+bean.getClass()+"] is not enhanced?");
|
||||
}
|
||||
|
||||
PersistRequestBean<?> req = createRequest(bean, t, parentBean, PersistRequest.Type.DETERMINE);
|
||||
try {
|
||||
req.initTransIfRequired();
|
||||
saveEnhanced(req);
|
||||
req.commitTransIfRequired();
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
req.rollbackTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update the bean depending on PersistControl and the bean state.
|
||||
*/
|
||||
private void saveEnhanced(PersistRequestBean<?> request) {
|
||||
|
||||
EntityBeanIntercept intercept = request.getEntityBeanIntercept();
|
||||
private void saveRecurse(EntityBean bean, Transaction t, Object parentBean) {
|
||||
|
||||
// determine insert or update taking into account stateless updates
|
||||
PersistRequestBean<?> request = createRequest(bean, t, parentBean, PersistRequest.Type.DETERMINE);
|
||||
|
||||
if (request.isReference()) {
|
||||
// its a reference...
|
||||
if (request.isPersistCascade()) {
|
||||
// save any associated List held beans
|
||||
intercept.setLoaded();
|
||||
saveAssocMany(false, request);
|
||||
intercept.setReference(-1);
|
||||
}
|
||||
|
||||
}
|
||||
request.checkUpdatedManysOnly();
|
||||
|
||||
} else {
|
||||
@@ -273,7 +270,7 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bean. Return NOT_SAVED if the bean values have not changed.
|
||||
* Update the bean.
|
||||
*/
|
||||
private void update(PersistRequestBean<?> request) {
|
||||
|
||||
@@ -555,7 +552,7 @@ public final class DefaultPersister implements Persister {
|
||||
|
||||
// check for partial beans
|
||||
if (request.isLoadedProperty(prop)) {
|
||||
Object detailBean = prop.getValue(parentBean);
|
||||
EntityBean detailBean = prop.getValueAsEntityBean(parentBean);
|
||||
if (detailBean != null) {
|
||||
if (prop.isSaveRecurseSkippable(detailBean)) {
|
||||
// skip saving this bean
|
||||
@@ -591,7 +588,6 @@ public final class DefaultPersister implements Persister {
|
||||
private final EntityBean parentBean;
|
||||
private final SpiTransaction transaction;
|
||||
private final boolean cascade;
|
||||
private final boolean statelessUpdate;
|
||||
private final boolean deleteMissingChildren;
|
||||
|
||||
private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
@@ -600,7 +596,6 @@ public final class DefaultPersister implements Persister {
|
||||
this.cascade = many.getCascadeInfo().isSave();
|
||||
this.parentBean = parentBean;
|
||||
this.transaction = request.getTransaction();
|
||||
this.statelessUpdate = request.isStatelessUpdate();
|
||||
this.deleteMissingChildren = request.isDeleteMissingChildren();
|
||||
}
|
||||
|
||||
@@ -610,7 +605,6 @@ public final class DefaultPersister implements Persister {
|
||||
this.parentBean = parentBean;
|
||||
this.transaction = t;
|
||||
this.cascade = true;
|
||||
this.statelessUpdate = false;
|
||||
this.deleteMissingChildren = false;
|
||||
}
|
||||
|
||||
@@ -626,10 +620,6 @@ public final class DefaultPersister implements Persister {
|
||||
return ModifyListenMode.REMOVALS.equals(many.getModifyListenMode());
|
||||
}
|
||||
|
||||
private boolean isStatelessUpdate() {
|
||||
return statelessUpdate;
|
||||
}
|
||||
|
||||
private boolean isDeleteMissingChildren() {
|
||||
return deleteMissingChildren;
|
||||
}
|
||||
@@ -672,12 +662,15 @@ public final class DefaultPersister implements Persister {
|
||||
saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren());
|
||||
}
|
||||
} else {
|
||||
if (saveMany.isCascade()) {
|
||||
if (saveMany.isModifyListenMode()) {
|
||||
// delete any removed beans via private owned. Needs to occur before
|
||||
// a 'deleteMissingChildren' statement occurs
|
||||
removeAssocManyPrivateOwned(saveMany);
|
||||
}
|
||||
if (saveMany.isCascade()) {
|
||||
// potentially deletes 'missing children' for 'stateless update'
|
||||
saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren());
|
||||
}
|
||||
if (saveMany.isModifyListenMode()) {
|
||||
removeAssocManyPrivateOwned(saveMany);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,42 +782,32 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
if (skipSavingThisBean) {
|
||||
// unmodified bean that does not recurse its save
|
||||
// so we can skip the save for this bean.
|
||||
// Reset skipSavingThisBean for the next detailBean
|
||||
skipSavingThisBean = false;
|
||||
if (skipSavingThisBean) {
|
||||
// unmodified bean that does not recurse its save
|
||||
// so we can skip the save for this bean.
|
||||
// Reset skipSavingThisBean for the next detailBean
|
||||
skipSavingThisBean = false;
|
||||
|
||||
} else if (!saveMany.isStatelessUpdate()) {
|
||||
// normal save recurse
|
||||
saveRecurse(detailBean, t, parentBean);
|
||||
|
||||
} else {
|
||||
if (targetDescriptor.isStatelessUpdate(detail)) {
|
||||
// update based on the value of Version/Id properties
|
||||
// cascade update in stateless mode
|
||||
forceUpdate(detail, t, deleteMissingChildren);
|
||||
} else {
|
||||
// cascade insert
|
||||
forceInsert(detail, t);
|
||||
}
|
||||
}
|
||||
if (detailIds != null) {
|
||||
// remember the Id (other details not in the collection) will be removed
|
||||
Object id = targetDescriptor.getId(detail);
|
||||
if (!DmlUtil.isNullOrZero(id)) {
|
||||
detailIds.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal save recurse
|
||||
saveRecurse(detail, t, parentBean);
|
||||
}
|
||||
if (detailIds != null) {
|
||||
// remember the Id (other details not in the collection) will be removed
|
||||
Object id = targetDescriptor.getId(detail);
|
||||
if (!DmlUtil.isNullOrZero(id)) {
|
||||
detailIds.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (detailIds != null) {
|
||||
// deleteMissingChildren is true so deleting children that were not just processed
|
||||
deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds);
|
||||
}
|
||||
|
||||
t.depth(-1);
|
||||
|
||||
}
|
||||
|
||||
public int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t) {
|
||||
@@ -859,7 +842,7 @@ public final class DefaultPersister implements Persister {
|
||||
|
||||
} else if (prop instanceof BeanPropertyAssocOne<?>) {
|
||||
BeanPropertyAssocOne<?> oneProp = (BeanPropertyAssocOne<?>) prop;
|
||||
Object assocBean = oneProp.getValue(parentBean);
|
||||
EntityBean assocBean = oneProp.getValueAsEntityBean(parentBean);
|
||||
|
||||
int depth = oneProp.isOneToOneExported() ? 1 : -1;
|
||||
int revertDepth = -1 * depth;
|
||||
@@ -1094,7 +1077,7 @@ public final class DefaultPersister implements Persister {
|
||||
|
||||
// check for partial objects
|
||||
if (request.isLoadedProperty(prop)) {
|
||||
Object detailBean = prop.getValue(request.getEntityBean());
|
||||
EntityBean detailBean = prop.getValueAsEntityBean(request.getEntityBean());
|
||||
if (detailBean != null) {
|
||||
if (prop.isReference(detailBean)) {
|
||||
// skip saving a reference
|
||||
|
||||
Reference in New Issue
Block a user