mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#120 - ENH: Add support for using where() on bulk Update statements
This commit is contained in:
@@ -1019,6 +1019,28 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().find(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Update query to perform a bulk update.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = Ebean.update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The type of entity bean to update
|
||||
* @param <T> The type of entity bean
|
||||
* @return The update query to use
|
||||
*/
|
||||
public static <T> UpdateQuery<T> update(Class<T> beanType) {
|
||||
return serverMgr.getDefaultServer().update(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a filter for sorting and filtering lists of entities locally without
|
||||
* going back to the database.
|
||||
|
||||
@@ -48,20 +48,20 @@ import java.util.Set;
|
||||
* <p>
|
||||
* Example: Get a EbeanServer
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* // Get access to the Human Resources EbeanServer/Database
|
||||
* EbeanServer hrServer = Ebean.getServer("HR");
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* // fetch contact 3 from the HR database Contact contact =
|
||||
* hrServer.find(Contact.class, new Integer(3));
|
||||
*
|
||||
*
|
||||
* contact.setStatus("INACTIVE"); ...
|
||||
*
|
||||
*
|
||||
* // save the contact back to the HR database hrServer.save(contact);
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* <b>EbeanServer has more API than Ebean</b><br/>
|
||||
* EbeanServer provides additional API compared with Ebean. For example it
|
||||
@@ -78,7 +78,7 @@ import java.util.Set;
|
||||
* ThreadLocal transaction management you can use the createTransaction()
|
||||
* method. Example: a single thread requires more than one transaction.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @see Ebean
|
||||
* @see EbeanServerFactory
|
||||
* @see ServerConfig
|
||||
@@ -95,16 +95,14 @@ public interface EbeanServer {
|
||||
* also have the option of shutting down the DataSource and deregistering the
|
||||
* JDBC driver.
|
||||
* </p>
|
||||
*
|
||||
* @param shutdownDataSource
|
||||
* if true then shutdown the underlying DataSource if it is the EbeanORM
|
||||
* DataSource implementation.
|
||||
* @param deregisterDriver
|
||||
* if true then deregister the JDBC driver if it is the EbeanORM
|
||||
* DataSource implementation.
|
||||
*
|
||||
* @param shutdownDataSource if true then shutdown the underlying DataSource if it is the EbeanORM
|
||||
* DataSource implementation.
|
||||
* @param deregisterDriver if true then deregister the JDBC driver if it is the EbeanORM
|
||||
* DataSource implementation.
|
||||
*/
|
||||
void shutdown(boolean shutdownDataSource, boolean deregisterDriver);
|
||||
|
||||
|
||||
/**
|
||||
* Return AutoTune which is used to control the AutoTune service at runtime.
|
||||
*/
|
||||
@@ -151,7 +149,8 @@ public interface EbeanServer {
|
||||
* For example, if the id value passed in is a String but ought to be a Long or UUID etc
|
||||
* then it will automatically be converted.
|
||||
* </p>
|
||||
* @param bean The entity bean to set the id value on.
|
||||
*
|
||||
* @param bean The entity bean to set the id value on.
|
||||
* @param id The id value to set.
|
||||
*/
|
||||
Object setBeanId(Object bean, Object id);
|
||||
@@ -179,6 +178,27 @@ public interface EbeanServer {
|
||||
*/
|
||||
<T> CsvReader<T> createCsvReader(Class<T> beanType);
|
||||
|
||||
/**
|
||||
* Create an Update query to perform a bulk update.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The type of entity bean to update
|
||||
* @param <T> The type of entity bean
|
||||
* @return The update query to use
|
||||
*/
|
||||
<T> UpdateQuery<T> update(Class<T> beanType);
|
||||
|
||||
/**
|
||||
* Create a query for an entity bean and synonym for {@link #find(Class)}.
|
||||
*
|
||||
@@ -201,7 +221,7 @@ public interface EbeanServer {
|
||||
* {@link Query#findSet()} etc will execute against the same EbeanServer from
|
||||
* which is was created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Find order 2 specifying explicitly the parts of the object graph to
|
||||
@@ -224,7 +244,6 @@ public interface EbeanServer {
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
<T> Query<T> find(Class<T> beanType);
|
||||
|
||||
@@ -272,7 +291,7 @@ public interface EbeanServer {
|
||||
* Note that the sorting uses a Comparator and Collections.sort(); and does
|
||||
* not invoke a DB query.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // find orders and their customers
|
||||
@@ -291,10 +310,8 @@ public interface EbeanServer {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param list
|
||||
* the list of entity beans
|
||||
* @param sortByClause
|
||||
* the properties to sort the list by
|
||||
* @param list the list of entity beans
|
||||
* @param sortByClause the properties to sort the list by
|
||||
*/
|
||||
<T> void sort(List<T> list, String sortByClause);
|
||||
|
||||
@@ -309,7 +326,7 @@ public interface EbeanServer {
|
||||
* <p>
|
||||
* An example:
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // The bean name and properties - "topic","postCount" and "id"
|
||||
@@ -362,7 +379,6 @@ public interface EbeanServer {
|
||||
* If there is no currently active transaction then a PersistenceException is thrown.
|
||||
*
|
||||
* @param transactionCallback The transaction callback to be registered with the current transaction.
|
||||
*
|
||||
* @throws PersistenceException If there is no currently active transaction
|
||||
*/
|
||||
void register(TransactionCallback transactionCallback) throws PersistenceException;
|
||||
@@ -400,7 +416,7 @@ public interface EbeanServer {
|
||||
* Example of using a transaction to span multiple calls to find(), save()
|
||||
* etc.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // start a transaction (stored in a ThreadLocal)
|
||||
@@ -419,7 +435,7 @@ public interface EbeanServer {
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <h3>Transaction options:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -452,7 +468,7 @@ public interface EbeanServer {
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* If you want to externalise the transaction management then you use
|
||||
* createTransaction() and pass the transaction around to the various methods on
|
||||
@@ -468,12 +484,12 @@ public interface EbeanServer {
|
||||
|
||||
/**
|
||||
* Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics.
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or
|
||||
* {@link #execute(TxScope, TxRunnable)}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <h3>REQUIRES_NEW example:</h3>
|
||||
* <pre>{@code
|
||||
* // Start a new transaction. If there is a current transaction
|
||||
@@ -494,7 +510,7 @@ public interface EbeanServer {
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <h3>REQUIRED example:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -543,25 +559,24 @@ public interface EbeanServer {
|
||||
* </p>
|
||||
* <p>
|
||||
* Code example:
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.beginTransaction();
|
||||
* try {
|
||||
* // do some fetching and or persisting ...
|
||||
*
|
||||
*
|
||||
* // commit at the end
|
||||
* ebeanServer.commitTransaction();
|
||||
*
|
||||
*
|
||||
* } finally {
|
||||
* // if commit didn't occur then rollback the transaction
|
||||
* ebeanServer.endTransaction();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
void endTransaction();
|
||||
|
||||
@@ -576,28 +591,25 @@ public interface EbeanServer {
|
||||
|
||||
/**
|
||||
* Refresh a many property of an entity bean.
|
||||
*
|
||||
* @param bean
|
||||
* the entity bean containing the 'many' property
|
||||
* @param propertyName
|
||||
* the 'many' property to be refreshed
|
||||
*
|
||||
* @param bean the entity bean containing the 'many' property
|
||||
* @param propertyName the 'many' property to be refreshed
|
||||
*/
|
||||
void refreshMany(Object bean, String propertyName);
|
||||
|
||||
/**
|
||||
* Find a bean using its unique id.
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* // Fetch order 1
|
||||
* Order order = ebeanServer.find(Order.class, 1);
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* If you want more control over the query then you can use createQuery() and
|
||||
* Query.findUnique();
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* // ... additionally fetching customer, customer shipping address,
|
||||
* // order details, and the product associated with each order detail.
|
||||
@@ -628,10 +640,8 @@ public interface EbeanServer {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType
|
||||
* the type of entity bean to fetch
|
||||
* @param id
|
||||
* the id value
|
||||
* @param beanType the type of entity bean to fetch
|
||||
* @param id the id value
|
||||
*/
|
||||
<T> T find(Class<T> beanType, Object id);
|
||||
|
||||
@@ -658,7 +668,7 @@ public interface EbeanServer {
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <h3>Lazy loading characteristics</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -673,10 +683,8 @@ public interface EbeanServer {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType
|
||||
* the type of entity bean
|
||||
* @param id
|
||||
* the id value
|
||||
* @param beanType the type of entity bean
|
||||
* @param id the id value
|
||||
*/
|
||||
<T> T getReference(Class<T> beanType, Object id);
|
||||
|
||||
@@ -707,7 +715,7 @@ public interface EbeanServer {
|
||||
* Internally this query using a PersistenceContext scoped to each bean (and the
|
||||
* beans associated object graph).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
@@ -741,7 +749,7 @@ public interface EbeanServer {
|
||||
* Internally this query using a PersistenceContext scoped to each bean (and the
|
||||
* beans associated object graph).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
@@ -766,8 +774,8 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Return versions of a @History entity bean.
|
||||
* <p>
|
||||
* Generally this query is expected to be a find by id or unique predicates query.
|
||||
* It will execute the query against the history returning the versions of the bean.
|
||||
* Generally this query is expected to be a find by id or unique predicates query.
|
||||
* It will execute the query against the history returning the versions of the bean.
|
||||
* </p>
|
||||
*/
|
||||
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
|
||||
@@ -779,7 +787,7 @@ public interface EbeanServer {
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
@@ -789,14 +797,10 @@ public interface EbeanServer {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean to fetch.
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched beans.
|
||||
*
|
||||
* @see Query#findList()
|
||||
*/
|
||||
<T> List<T> findList(Query<T> query, Transaction transaction);
|
||||
@@ -808,13 +812,10 @@ public interface EbeanServer {
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute the row count on
|
||||
* @param transaction
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the row count query
|
||||
*
|
||||
* @param query the query to execute the row count on
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the row count query
|
||||
* @see com.avaje.ebean.Query#findFutureRowCount()
|
||||
*/
|
||||
<T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction transaction);
|
||||
@@ -826,13 +827,10 @@ public interface EbeanServer {
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute the fetch Id's on
|
||||
* @param transaction
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the list of Id's
|
||||
*
|
||||
* @param query the query to execute the fetch Id's on
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the list of Id's
|
||||
* @see com.avaje.ebean.Query#findFutureIds()
|
||||
*/
|
||||
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
|
||||
@@ -846,13 +844,9 @@ public interface EbeanServer {
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
*
|
||||
*
|
||||
* @param query
|
||||
* the query to execute in the background
|
||||
* @param transaction
|
||||
* the transaction (can be null).
|
||||
* @param query the query to execute in the background
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the list result of the query
|
||||
*
|
||||
* @see Query#findFutureList()
|
||||
*/
|
||||
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
|
||||
@@ -867,7 +861,7 @@ public interface EbeanServer {
|
||||
* If maxRows is not set on the query prior to calling findPagedList() then a
|
||||
* PersistenceException is thrown.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
@@ -884,7 +878,6 @@ public interface EbeanServer {
|
||||
* }</pre>
|
||||
*
|
||||
* @return The PagedList
|
||||
*
|
||||
* @see Query#findPagedList()
|
||||
*/
|
||||
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
|
||||
@@ -896,7 +889,7 @@ public interface EbeanServer {
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
@@ -905,15 +898,11 @@ public interface EbeanServer {
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean to fetch.
|
||||
* @param query
|
||||
* the query to execute
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @return the set of fetched beans.
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the set of fetched beans.
|
||||
* @see Query#findSet()
|
||||
*/
|
||||
<T> Set<T> findSet(Query<T> query, Transaction transaction);
|
||||
@@ -925,15 +914,11 @@ public interface EbeanServer {
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean to fetch.
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @return the map of fetched beans.
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the map of fetched beans.
|
||||
* @see Query#findMap()
|
||||
*/
|
||||
<T> Map<?, T> findMap(Query<T> query, Transaction transaction);
|
||||
@@ -950,15 +935,11 @@ public interface EbeanServer {
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean to fetch.
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched beans.
|
||||
* @throws NonUniqueResultException if more than one result was found
|
||||
*
|
||||
* @see Query#findUnique()
|
||||
*/
|
||||
@Nullable
|
||||
@@ -979,6 +960,19 @@ public interface EbeanServer {
|
||||
*/
|
||||
<T> int delete(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the update query returning the number of rows updated.
|
||||
* <p>
|
||||
* The update query must be created using {@link #update(Class)}.
|
||||
* </p>
|
||||
*
|
||||
* @param query the update query to execute
|
||||
* @param transaction the optional transaction to use for the update (can be null)
|
||||
* @param <T> the type of entity bean
|
||||
* @return The number of rows updated
|
||||
*/
|
||||
<T> int update(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a list of MapBean.
|
||||
* <p>
|
||||
@@ -986,13 +980,10 @@ public interface EbeanServer {
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @return the list of fetched MapBean.
|
||||
*
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched MapBean.
|
||||
* @see SqlQuery#findList()
|
||||
*/
|
||||
List<SqlRow> findList(SqlQuery query, Transaction transaction);
|
||||
@@ -1027,13 +1018,10 @@ public interface EbeanServer {
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @return the fetched MapBean or null if none was found.
|
||||
*
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the fetched MapBean or null if none was found.
|
||||
* @see SqlQuery#findUnique()
|
||||
*/
|
||||
@Nullable
|
||||
@@ -1054,7 +1042,7 @@ public interface EbeanServer {
|
||||
* In this example below the details property has a CascadeType.ALL set so
|
||||
* saving an order will also save all its details.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* public class Order { ...
|
||||
*
|
||||
@@ -1063,7 +1051,7 @@ public interface EbeanServer {
|
||||
* ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* When a save cascades via a OneToMany or ManyToMany Ebean will automatically
|
||||
* set the 'parent' object to the 'detail' object. In the example below in
|
||||
@@ -1195,7 +1183,7 @@ public interface EbeanServer {
|
||||
* <p>
|
||||
* Example:
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example that uses 'named' parameters
|
||||
@@ -1212,11 +1200,8 @@ public interface EbeanServer {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param sqlUpdate
|
||||
* the update sql potentially with bind values
|
||||
*
|
||||
* @param sqlUpdate the update sql potentially with bind values
|
||||
* @return the number of rows updated or deleted. -1 if executed in batch.
|
||||
*
|
||||
* @see CallableSql
|
||||
*/
|
||||
int execute(SqlUpdate sqlUpdate);
|
||||
@@ -1241,7 +1226,7 @@ public interface EbeanServer {
|
||||
* <p>
|
||||
* Example:
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "{call sp_order_modify(?,?,?)}";
|
||||
@@ -1288,28 +1273,20 @@ public interface EbeanServer {
|
||||
* information is registered immediately (with the transaction manager).
|
||||
* </p>
|
||||
*
|
||||
* @param tableName
|
||||
* the name of the table that was modified
|
||||
* @param inserted
|
||||
* true if rows where inserted into the table
|
||||
* @param updated
|
||||
* true if rows on the table where updated
|
||||
* @param deleted
|
||||
* true if rows on the table where deleted
|
||||
* @param tableName the name of the table that was modified
|
||||
* @param inserted true if rows where inserted into the table
|
||||
* @param updated true if rows on the table where updated
|
||||
* @param deleted true if rows on the table where deleted
|
||||
*/
|
||||
void externalModification(String tableName, boolean inserted, boolean updated, boolean deleted);
|
||||
|
||||
/**
|
||||
* Find a entity bean with an explicit transaction.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean to find
|
||||
* @param beanType
|
||||
* the type of entity bean to find
|
||||
* @param id
|
||||
* the bean id value
|
||||
* @param transaction
|
||||
* the transaction to use (can be null)
|
||||
*
|
||||
* @param <T> the type of entity bean to find
|
||||
* @param beanType the type of entity bean to find
|
||||
* @param id the bean id value
|
||||
* @param transaction the transaction to use (can be null)
|
||||
*/
|
||||
<T> T find(Class<T> beanType, Object id, Transaction transaction);
|
||||
|
||||
@@ -1331,20 +1308,20 @@ public interface EbeanServer {
|
||||
* <p>
|
||||
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
|
||||
* dirty so that it is not skipped.
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
*
|
||||
* Customer customer = ebeanServer.find(Customer, id);
|
||||
*
|
||||
*
|
||||
* // mark the bean as dirty so that a save() or update() will
|
||||
* // increment the version property
|
||||
* ebeanServer.markAsDirty(customer);
|
||||
* ebeanServer.save(customer);
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
void markAsDirty(Object bean);
|
||||
|
||||
|
||||
/**
|
||||
* Saves the bean using an update. If you know you are updating a bean then it is preferable to
|
||||
* use this update() method rather than save().
|
||||
@@ -1367,17 +1344,17 @@ public interface EbeanServer {
|
||||
* controls if only the changed properties are included in the update or if all the loaded
|
||||
* properties are included instead.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
*
|
||||
* // 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)
|
||||
*/
|
||||
@@ -1390,14 +1367,11 @@ public interface EbeanServer {
|
||||
|
||||
/**
|
||||
* Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
|
||||
*
|
||||
* @param bean
|
||||
* the bean to update
|
||||
* @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 bean the bean to update
|
||||
* @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.
|
||||
*/
|
||||
void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;
|
||||
|
||||
@@ -1454,7 +1428,7 @@ public interface EbeanServer {
|
||||
* The scope can control the transaction type, isolation and rollback
|
||||
* semantics.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // set specific transactional scope settings
|
||||
@@ -1477,7 +1451,7 @@ public interface EbeanServer {
|
||||
* The default scope runs with REQUIRED and by default will rollback on any
|
||||
* exception (checked or runtime).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.execute(new TxRunnable() {
|
||||
@@ -1503,7 +1477,7 @@ public interface EbeanServer {
|
||||
* The scope can control the transaction type, isolation and rollback
|
||||
* semantics.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // set specific transactional scope settings
|
||||
@@ -1531,7 +1505,7 @@ public interface EbeanServer {
|
||||
* This is basically the same as TxRunnable except that it returns an Object
|
||||
* (and you specify the return type via generics).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.execute(new TxCallable<String>() {
|
||||
@@ -1555,7 +1529,6 @@ public interface EbeanServer {
|
||||
|
||||
/**
|
||||
* Return the manager of the server cache ("L2" cache).
|
||||
*
|
||||
*/
|
||||
ServerCacheManager getServerCacheManager();
|
||||
|
||||
@@ -1571,7 +1544,7 @@ public interface EbeanServer {
|
||||
* This instance is safe to be used concurrently by multiple threads and this
|
||||
* method is cheap to call.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <h3>Simple example:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -1580,7 +1553,7 @@ public interface EbeanServer {
|
||||
* System.out.println(jsonOutput);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <h3>Using PathProperties:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -1630,9 +1603,9 @@ public interface EbeanServer {
|
||||
* The values are published from the draft to the live bean.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean
|
||||
*/
|
||||
<T> T publish(Class<T> beanType, Object id);
|
||||
|
||||
@@ -1655,8 +1628,8 @@ public interface EbeanServer {
|
||||
* The values are published from the draft beans to the live beans.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to publish
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to publish
|
||||
*/
|
||||
<T> List<T> publish(Query<T> query);
|
||||
|
||||
@@ -1681,9 +1654,9 @@ public interface EbeanServer {
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean to restore
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean to restore
|
||||
*/
|
||||
<T> T draftRestore(Class<T> beanType, Object id);
|
||||
|
||||
@@ -1707,8 +1680,8 @@ public interface EbeanServer {
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to restore
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to restore
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query);
|
||||
|
||||
|
||||
@@ -135,10 +135,18 @@ public interface ExpressionList<T> {
|
||||
* optimal depending on the database platform.
|
||||
* </p>
|
||||
*
|
||||
* @return the number of beans/rows that were deleted.
|
||||
* @return the number of rows that were deleted.
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute as a update query.
|
||||
*
|
||||
* @return the number of rows that were updated.
|
||||
* @see UpdateQuery
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Execute the query process the beans one at a time.
|
||||
*
|
||||
|
||||
@@ -763,6 +763,11 @@ public interface Query<T> {
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the UpdateQuery returning the number of rows updated.
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Return the count of entities this query should return.
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* An update query typically intended to perform a bulk update of many rows that match the query.
|
||||
* <p>
|
||||
* Also note that you can also just use a raw SQL update via {@link SqlUpdate} which is pretty light and simple.
|
||||
* This UpdateQuery is more for the cases where we want to build the where expression of the update using the
|
||||
* {@link ExpressionList} "Criteria API" that is used with a normal ORM query.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example: Simple update</h4>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
* <pre>{@code sql
|
||||
*
|
||||
* update o_customer set status=?, updtime=? where id > ?
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Note that if the where() clause contains a join then the SQL update changes to use a
|
||||
* <code> WHERE ID IN () </code> form.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example: Update with a JOIN</h4>
|
||||
* <p>
|
||||
* In this example the expression <code>.eq("billingAddress.country", nz)</code> requires a join
|
||||
* to the address table.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .eq("status", Customer.Status.NEW)
|
||||
* .eq("billingAddress.country", nz)
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code sql
|
||||
*
|
||||
* update o_customer set status=?, updtime=?
|
||||
* where id in (
|
||||
* select t0.id c0
|
||||
* from o_customer t0
|
||||
* left outer join o_address t1 on t1.id = t0.billing_address_id
|
||||
* where t0.status = ?
|
||||
* and t1.country_code = ?
|
||||
* and t0.id > ? )
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> The type of entity bean being updated
|
||||
*
|
||||
* @see SqlUpdate
|
||||
*/
|
||||
public interface UpdateQuery<T> {
|
||||
|
||||
/**
|
||||
* Set the value of a property.
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param property The bean property to be set
|
||||
* @param value The value to set the property to
|
||||
*/
|
||||
UpdateQuery<T> set(String property, Object value);
|
||||
|
||||
/**
|
||||
* Set the property to be null.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .setNull("notes")
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param property The property to be set to null.
|
||||
*/
|
||||
UpdateQuery<T> setNull(String property);
|
||||
|
||||
/**
|
||||
*
|
||||
* Set using a property expression that does not need any bind values.
|
||||
* <p>
|
||||
* The property expression typically contains database functions.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .setRaw("status = coalesce(status, 'A')")
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyExpression A property expression
|
||||
*/
|
||||
UpdateQuery<T> setRaw(String propertyExpression);
|
||||
|
||||
/**
|
||||
* Set using a property expression that can contain <code>?</code> bind value placeholders.
|
||||
* <p>
|
||||
* For each <code>?</code> in the property expression there should be a matching bind value supplied.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .setRaw("status = coalesce(status, ?)", Customer.Status.ACTIVE)
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyExpression A raw property expression
|
||||
* @param values The values to bind with the property expression
|
||||
*/
|
||||
UpdateQuery<T> setRaw(String propertyExpression, Object... values);
|
||||
|
||||
/**
|
||||
* Return the query expression list to add predicates to.
|
||||
*/
|
||||
ExpressionList<T> where();
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmUpdateProperties;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
@@ -91,6 +92,11 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE,
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE,
|
||||
}
|
||||
|
||||
enum TemporalMode {
|
||||
@@ -677,4 +683,9 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
Set<String> validate(BeanType<T> desc);
|
||||
|
||||
/**
|
||||
* Return the properties for an update query.
|
||||
*/
|
||||
OrmUpdateProperties getUpdateProperties();
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.avaje.ebeaninternal.server.query.QueryFutureRowCount;
|
||||
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.DefaultUpdateQuery;
|
||||
import com.avaje.ebeaninternal.server.text.csv.TCsvReader;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
@@ -875,11 +876,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new TCsvReader<T>(this, descriptor);
|
||||
}
|
||||
|
||||
public <T> UpdateQuery<T> update(Class<T> beanType) {
|
||||
return new DefaultUpdateQuery<T>(createQuery(beanType));
|
||||
}
|
||||
|
||||
public <T> Query<T> find(Class<T> beanType) {
|
||||
return createQuery(beanType);
|
||||
}
|
||||
|
||||
public <T> Query<T> createQuery(Class<T> beanType) {
|
||||
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType) {
|
||||
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
|
||||
if (desc == null) {
|
||||
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
|
||||
@@ -1159,6 +1164,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
public <T> int update(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.UPDATE, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.markNotQueryOnly();
|
||||
return request.update();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
|
||||
|
||||
@@ -42,4 +42,9 @@ public interface OrmQueryEngine {
|
||||
* Execute the query as a delete statement.
|
||||
*/
|
||||
<T> int delete(OrmQueryRequest<T> request);
|
||||
|
||||
/**
|
||||
* Execute the query as a update statement.
|
||||
*/
|
||||
<T> int update(OrmQueryRequest<T> request);
|
||||
}
|
||||
|
||||
@@ -286,6 +286,13 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return queryEngine.delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query as a update.
|
||||
*/
|
||||
public int update() {
|
||||
return queryEngine.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query as findById.
|
||||
*/
|
||||
|
||||
@@ -52,6 +52,11 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the query as a update.
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Execute the query as findById.
|
||||
*/
|
||||
|
||||
@@ -367,6 +367,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private String deleteByIdSql;
|
||||
private String deleteByIdInSql;
|
||||
private String whereIdInSql;
|
||||
private String softDeleteByIdSql;
|
||||
private String softDeleteByIdInSql;
|
||||
|
||||
@@ -687,7 +688,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
String idEqualsSql = idBinder.getBindIdSql(null);
|
||||
|
||||
deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql;
|
||||
deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " ";
|
||||
whereIdInSql = " where " + idBinderInLHSSqlNoAlias + " ";
|
||||
deleteByIdInSql = "delete from " + baseTable + whereIdInSql;
|
||||
|
||||
if (softDelete) {
|
||||
softDeleteByIdSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idEqualsSql;
|
||||
@@ -796,6 +798,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "where id in" sql (for use with UpdateQuery).
|
||||
*/
|
||||
public String getWhereIdInSql() {
|
||||
return whereIdInSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "delete by id" sql.
|
||||
*/
|
||||
|
||||
@@ -277,6 +277,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update() {
|
||||
return query.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FutureIds<T> findFutureIds() {
|
||||
return query.findFutureIds();
|
||||
|
||||
@@ -279,6 +279,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update() {
|
||||
return exprList.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> asOf(Timestamp asOf) {
|
||||
return exprList.asOf(asOf);
|
||||
|
||||
@@ -88,7 +88,7 @@ public class CQueryBuilder {
|
||||
/**
|
||||
* Build the delete query.
|
||||
*/
|
||||
public <T> CQueryDelete buildDeleteQuery(OrmQueryRequest<T> request) {
|
||||
public <T> CQueryUpdate buildUpdateQuery(String type, OrmQueryRequest<T> request) {
|
||||
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
String rootTableAlias = query.getAlias();
|
||||
@@ -99,33 +99,54 @@ public class CQueryBuilder {
|
||||
if (queryPlan != null) {
|
||||
// skip building the SqlTree and Sql string
|
||||
predicates.prepare(false);
|
||||
String sql = queryPlan.getSql();
|
||||
return new CQueryDelete(request, predicates, sql);
|
||||
return new CQueryUpdate(type, request, predicates, queryPlan.getSql());
|
||||
}
|
||||
|
||||
predicates.prepare(true);
|
||||
|
||||
SqlTree sqlTree = createSqlTree(request, predicates, getHistorySupport(query), getDraftSupport(query));
|
||||
|
||||
boolean includeJoins = sqlTree.isIncludeJoins();
|
||||
|
||||
String sql;
|
||||
if (!includeJoins) {
|
||||
// simple - delete from table ...
|
||||
sql = aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql());
|
||||
if (type.equals("Delete")) {
|
||||
sql = buildDeleteSql(request, rootTableAlias, predicates, sqlTree);
|
||||
} else {
|
||||
// wrap as - delete from table where id in (select id ...)
|
||||
sql = buildSql(null, request, predicates, sqlTree).getSql();
|
||||
sql = request.getBeanDescriptor().getDeleteByIdInSql() + "in (" + sql + ")";
|
||||
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
|
||||
sql = aliasReplace(sql, alias);
|
||||
sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree);
|
||||
}
|
||||
|
||||
// cache the query plan
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree, false, false, predicates.getLogWhereSql());
|
||||
|
||||
request.putQueryPlan(queryPlan);
|
||||
return new CQueryDelete(request, predicates, sql);
|
||||
return new CQueryUpdate(type, request, predicates, sql);
|
||||
}
|
||||
|
||||
private <T> String buildDeleteSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
|
||||
|
||||
if (!sqlTree.isIncludeJoins()) {
|
||||
// simple - delete from table ...
|
||||
return aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql());
|
||||
}
|
||||
// wrap as - delete from table where id in (select id ...)
|
||||
String sql = buildSql(null, request, predicates, sqlTree).getSql();
|
||||
sql = request.getBeanDescriptor().getDeleteByIdInSql() + "in (" + sql + ")";
|
||||
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
|
||||
sql = aliasReplace(sql, alias);
|
||||
return sql;
|
||||
}
|
||||
|
||||
private <T> String buildUpdateSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
|
||||
|
||||
String updateClause = "update "+request.getBeanDescriptor().getBaseTable()+" set "+predicates.getDbUpdateClause();
|
||||
|
||||
if (!sqlTree.isIncludeJoins()) {
|
||||
// simple - update table set ... where ...
|
||||
return aliasStrip(buildSql(updateClause, request, predicates, sqlTree).getSql());
|
||||
}
|
||||
// wrap as - update table set ... where id in (select id ...)
|
||||
String sql = buildSql(null, request, predicates, sqlTree).getSql();
|
||||
sql = updateClause + " " + request.getBeanDescriptor().getWhereIdInSql() + "in (" + sql + ")";
|
||||
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
|
||||
sql = aliasReplace(sql, alias);
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -423,11 +444,10 @@ public class CQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" from ");
|
||||
|
||||
// build the from clause potentially with joins
|
||||
// required only for the predicates
|
||||
sb.append(select.getFromSql());
|
||||
if (selectClause == null || !selectClause.startsWith("update")) {
|
||||
sb.append(" from ");
|
||||
sb.append(select.getFromSql());
|
||||
}
|
||||
|
||||
String inheritanceWhere = select.getInheritanceWhereSql();
|
||||
|
||||
|
||||
@@ -51,10 +51,18 @@ public class CQueryEngine {
|
||||
}
|
||||
|
||||
public <T> int delete(OrmQueryRequest<T> request) {
|
||||
CQueryUpdate query = queryBuilder.buildUpdateQuery("Delete", request);
|
||||
return executeUpdate(request, query);
|
||||
}
|
||||
|
||||
CQueryDelete query = queryBuilder.buildDeleteQuery(request);
|
||||
public <T> int update(OrmQueryRequest<T> request) {
|
||||
CQueryUpdate query = queryBuilder.buildUpdateQuery("Update", request);
|
||||
return executeUpdate(request, query);
|
||||
}
|
||||
|
||||
private <T> int executeUpdate(OrmQueryRequest<T> request, CQueryUpdate query) {
|
||||
try {
|
||||
int rows = query.delete();
|
||||
int rows = query.execute();
|
||||
|
||||
if (request.isLogSql()) {
|
||||
String logSql = query.getGeneratedSql();
|
||||
|
||||
@@ -9,11 +9,12 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployParser;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmUpdateProperties;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -112,6 +113,8 @@ public class CQueryPredicates {
|
||||
|
||||
private String dbOrderBy;
|
||||
|
||||
private String dbUpdateClause;
|
||||
|
||||
/**
|
||||
* Includes from where and order by clauses.
|
||||
*/
|
||||
@@ -133,6 +136,12 @@ public class CQueryPredicates {
|
||||
|
||||
public String bind(DataBind dataBind) throws SQLException {
|
||||
|
||||
OrmUpdateProperties updateProperties = query.getUpdateProperties();
|
||||
if (updateProperties != null) {
|
||||
// bind the update set clause
|
||||
updateProperties.bind(binder, dataBind);
|
||||
}
|
||||
|
||||
if (query.isVersionsBetween() && binder.isBindAsOfWithFromClause()) {
|
||||
// sql2011 based versions between timestamp syntax
|
||||
Timestamp start = query.getVersionStart();
|
||||
@@ -196,6 +205,15 @@ public class CQueryPredicates {
|
||||
return dataBind.log().toString();
|
||||
}
|
||||
|
||||
private void buildUpdateClause(boolean buildSql, DeployParser deployParser) {
|
||||
if (buildSql) {
|
||||
OrmUpdateProperties updateProperties = query.getUpdateProperties();
|
||||
if (updateProperties != null) {
|
||||
dbUpdateClause = updateProperties.buildSetClause(deployParser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildBindHavingRawSql(boolean buildSql, boolean parseRaw, DeployParser deployParser) {
|
||||
if (buildSql || bindParams != null) {
|
||||
// having clause with named parameters...
|
||||
@@ -266,15 +284,12 @@ public class CQueryPredicates {
|
||||
prepare(buildSql, true, deployParser);
|
||||
}
|
||||
|
||||
public void prepareRawSql(DeployParser deployParser) {
|
||||
prepare(true, false, deployParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* This combines the sql from named/positioned parameters and expressions.
|
||||
*/
|
||||
private void prepare(boolean buildSql, boolean parseRaw, DeployParser deployParser) {
|
||||
|
||||
buildUpdateClause(buildSql, deployParser);
|
||||
buildBindWhereRawSql(buildSql, parseRaw, deployParser);
|
||||
buildBindHavingRawSql(buildSql, parseRaw, deployParser);
|
||||
|
||||
@@ -473,6 +488,13 @@ public class CQueryPredicates {
|
||||
return where.getBindValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the db update set clause for an UpdateQuery.
|
||||
*/
|
||||
public String getDbUpdateClause() {
|
||||
return dbUpdateClause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the db column version of the combined where clause.
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,6 @@ import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -19,8 +16,6 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public class CQueryRowCount {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQueryRowCount.class);
|
||||
|
||||
/**
|
||||
* The overall find request wrapper object.
|
||||
*/
|
||||
@@ -137,29 +132,12 @@ public class CQueryRowCount {
|
||||
|
||||
/**
|
||||
* Close the resources.
|
||||
* <p>
|
||||
* The jdbc resultSet and statement need to be closed. Its important that
|
||||
* this method is called.
|
||||
* </p>
|
||||
*/
|
||||
private void close() {
|
||||
try {
|
||||
if (rset != null) {
|
||||
rset.close();
|
||||
rset = null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
pstmt = null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
UtilJdbc.close(rset);
|
||||
UtilJdbc.close(pstmt);
|
||||
rset = null;
|
||||
pstmt = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+10
-22
@@ -4,9 +4,6 @@ import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
@@ -15,9 +12,7 @@ import java.sql.SQLException;
|
||||
/**
|
||||
* Executes the delete query.
|
||||
*/
|
||||
public class CQueryDelete {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQueryDelete.class);
|
||||
public class CQueryUpdate {
|
||||
|
||||
private final OrmQueryRequest<?> request;
|
||||
|
||||
@@ -35,6 +30,8 @@ public class CQueryDelete {
|
||||
*/
|
||||
private final String sql;
|
||||
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* The statement used to create the resultSet.
|
||||
*/
|
||||
@@ -49,8 +46,8 @@ public class CQueryDelete {
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
public CQueryDelete(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
|
||||
|
||||
public CQueryUpdate(String type, OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
|
||||
this.type = type;
|
||||
this.request = request;
|
||||
this.query = request.getQuery();
|
||||
this.sql = sql;
|
||||
@@ -63,14 +60,12 @@ public class CQueryDelete {
|
||||
* Return a summary description of this query.
|
||||
*/
|
||||
public String getSummary() {
|
||||
//noinspection StringBufferReplaceableByString
|
||||
StringBuilder sb = new StringBuilder(80);
|
||||
sb.append("Delete exeMicros[").append(executionTimeMicros)
|
||||
sb.append(type).append(" exeMicros[").append(executionTimeMicros)
|
||||
.append("] rows[").append(rowCount)
|
||||
.append("] type[").append(desc.getName())
|
||||
.append("] predicates[").append(predicates.getLogWhereSql())
|
||||
.append("] bind[").append(bindLog).append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -89,12 +84,11 @@ public class CQueryDelete {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query returning the row count.
|
||||
* Execute the update or delete statement returning the row count.
|
||||
*/
|
||||
public int delete() throws SQLException {
|
||||
public int execute() throws SQLException {
|
||||
|
||||
long startNano = System.nanoTime();
|
||||
|
||||
try {
|
||||
|
||||
SpiTransaction t = request.getTransaction();
|
||||
@@ -122,14 +116,8 @@ public class CQueryDelete {
|
||||
* Close the resources.
|
||||
*/
|
||||
private void close() {
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
pstmt = null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
UtilJdbc.close(pstmt);
|
||||
pstmt = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,6 +53,12 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
return queryEngine.delete(request);
|
||||
}
|
||||
|
||||
public <T> int update(OrmQueryRequest<T> request) {
|
||||
|
||||
flushJdbcBatchOnQuery(request);
|
||||
return queryEngine.update(request);
|
||||
}
|
||||
|
||||
public <T> int findRowCount(OrmQueryRequest<T> request) {
|
||||
|
||||
flushJdbcBatchOnQuery(request);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class UtilJdbc {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(UtilJdbc.class);
|
||||
|
||||
public static void close(ResultSet resultSet) {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing ResultSet", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void close(PreparedStatement statement) {
|
||||
try {
|
||||
if (statement != null) {
|
||||
statement.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing PreparedStatement", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -230,6 +230,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private boolean useDocStore;
|
||||
|
||||
private OrmUpdateProperties updateProperties;
|
||||
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory) {
|
||||
this.beanDescriptor = desc;
|
||||
this.beanType = desc.getBeanType();
|
||||
@@ -831,7 +833,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
queryPlanKey = new OrmQueryPlanKey(includeTableJoin, type, detail, maxRows, firstRow,
|
||||
disableLazyLoading, rawWhereClause, orderBy, query, additionalWhere, additionalHaving,
|
||||
distinct, sqlDistinct, mapKey, id, bindParams, whereExpressions, havingExpressions,
|
||||
temporalMode, forUpdate, rootTableAlias, rawSql);
|
||||
temporalMode, forUpdate, rootTableAlias, rawSql, updateProperties);
|
||||
|
||||
return queryPlanKey;
|
||||
}
|
||||
@@ -1036,6 +1038,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return server.delete(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update() {
|
||||
return server.update(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> findIds() {
|
||||
// a copy of this query is made in the server
|
||||
@@ -1528,4 +1535,13 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
return validation.getUnknownProperties();
|
||||
}
|
||||
|
||||
public void setUpdateProperties(OrmUpdateProperties updateProperties) {
|
||||
this.updateProperties = updateProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrmUpdateProperties getUpdateProperties() {
|
||||
return updateProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.UpdateQuery;
|
||||
|
||||
/**
|
||||
* Default implementation of UpdateQuery.
|
||||
*/
|
||||
public class DefaultUpdateQuery<T> implements UpdateQuery<T> {
|
||||
|
||||
private final OrmUpdateProperties values = new OrmUpdateProperties();
|
||||
|
||||
private final DefaultOrmQuery<T> query;
|
||||
|
||||
public DefaultUpdateQuery(DefaultOrmQuery<T> query) {
|
||||
this.query = query;
|
||||
query.setUpdateProperties(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> set(String property, Object value) {
|
||||
values.set(property, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> setNull(String property) {
|
||||
values.set(property, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> setRaw(String propertyExpression) {
|
||||
values.setRaw(propertyExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateQuery<T> setRaw(String propertyExpression, Object... vals) {
|
||||
values.setRaw(propertyExpression, vals);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> where() {
|
||||
return query.where();
|
||||
}
|
||||
}
|
||||
@@ -35,11 +35,12 @@ public class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
private final boolean forUpdate;
|
||||
private final String rootTableAlias;
|
||||
private final OrmUpdateProperties updateProperties;
|
||||
|
||||
private final int planHash;
|
||||
private final int bindCount;
|
||||
|
||||
public OrmQueryPlanKey(TableJoin includeTableJoin, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading, String rawWhereClause, OrderBy<?> orderBy, String query, String additionalWhere, String additionalHaving, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams, SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode, boolean forUpdate, String rootTableAlias, RawSql rawSql) {
|
||||
public OrmQueryPlanKey(TableJoin includeTableJoin, SpiQuery.Type type, OrmQueryDetail detail, int maxRows, int firstRow, boolean disableLazyLoading, String rawWhereClause, OrderBy<?> orderBy, String query, String additionalWhere, String additionalHaving, boolean distinct, boolean sqlDistinct, String mapKey, Object id, BindParams bindParams, SpiExpression whereExpressions, SpiExpression havingExpressions, SpiQuery.TemporalMode temporalMode, boolean forUpdate, String rootTableAlias, RawSql rawSql, OrmUpdateProperties updateProperties) {
|
||||
|
||||
this.includeTableJoin = includeTableJoin;
|
||||
this.type = type;
|
||||
@@ -61,6 +62,7 @@ public class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
this.temporalMode = temporalMode;
|
||||
this.forUpdate = forUpdate;
|
||||
this.rootTableAlias = rootTableAlias;
|
||||
this.updateProperties = updateProperties;
|
||||
this.rawSqlKey = (rawSql == null) ? null : rawSql.getKey();
|
||||
|
||||
// exclude bind values and things unrelated to the sql being generated
|
||||
@@ -91,6 +93,9 @@ public class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
if (having != null) {
|
||||
having.queryPlanHash(builder);
|
||||
}
|
||||
if (updateProperties != null) {
|
||||
updateProperties.buildQueryPlanHash(builder);
|
||||
}
|
||||
|
||||
this.planHash = builder.getPlanHash();
|
||||
this.bindCount = builder.getBindCount();
|
||||
@@ -128,6 +133,7 @@ public class OrmQueryPlanKey implements CQueryPlanKey {
|
||||
if (orderByAsSting != null ? !orderByAsSting.equals(that.orderByAsSting) : that.orderByAsSting != null) return false;
|
||||
if (where != null ? !where.isSameByPlan(that.where) : that.where != null) return false;
|
||||
if (having != null ? !having.isSameByPlan(that.having) : that.having != null) return false;
|
||||
if (updateProperties != null ? !updateProperties.isSameByPlan(that.updateProperties) : that.updateProperties != null) return false;
|
||||
if (rawSqlKey != null ? !rawSqlKey.equals(that.rawSqlKey) : that.rawSqlKey != null) return false;
|
||||
|
||||
// if (detail != null ? !detail.equals(that.detail) : that.detail != null) return false;
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployParser;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Set properties for a UpdateQuery.
|
||||
*/
|
||||
public class OrmUpdateProperties {
|
||||
|
||||
private static final NullValue NULL_VALUE = new NullValue();
|
||||
|
||||
private static final NoneValue NONE_VALUE = new NoneValue();
|
||||
|
||||
/**
|
||||
* Bind value used in the set clause for update query.
|
||||
* It may/may not have bind values etc.
|
||||
*/
|
||||
public static abstract class Value {
|
||||
|
||||
public void bind(Binder binder, DataBind dataBind) throws SQLException {
|
||||
// default to no bind values
|
||||
}
|
||||
|
||||
public String bindClause() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public int getBindCount() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property to null.
|
||||
*/
|
||||
static class NullValue extends Value {
|
||||
@Override
|
||||
public String bindClause() {
|
||||
return "=null";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property to a simple value.
|
||||
*/
|
||||
static class SimpleValue extends Value {
|
||||
|
||||
final Object value;
|
||||
|
||||
SimpleValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBindCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String bindClause() {
|
||||
return "=?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(Binder binder, DataBind dataBind) throws SQLException {
|
||||
binder.bindObject(dataBind, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set using an expression with no bind value.
|
||||
*/
|
||||
static class NoneValue extends Value {
|
||||
@Override
|
||||
public String bindClause() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set using an expression with many bind values.
|
||||
*/
|
||||
static class RawArrayValue extends Value {
|
||||
|
||||
final Object[] bindValues;
|
||||
|
||||
public RawArrayValue(Object[] bindValues) {
|
||||
this.bindValues = bindValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBindCount() {
|
||||
return bindValues.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(Binder binder, DataBind dataBind) throws SQLException {
|
||||
for (Object val : bindValues) {
|
||||
binder.bindObject(dataBind, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set properties/expressions and their bind values.
|
||||
*/
|
||||
private LinkedHashMap<String, Value> values = new LinkedHashMap<String, Value>();
|
||||
|
||||
/**
|
||||
* Normal set property.
|
||||
*/
|
||||
public void set(String propertyName, Object value) {
|
||||
if (value == null) {
|
||||
values.put(propertyName, NULL_VALUE);
|
||||
|
||||
} else {
|
||||
values.put(propertyName, new SimpleValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a raw expression with no bind values.
|
||||
*/
|
||||
public void setRaw(String propertyName) {
|
||||
values.put(propertyName, NONE_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a raw expression with many bind values.
|
||||
*/
|
||||
public void setRaw(String propertyExpression, Object... vals) {
|
||||
if (vals.length == 0) {
|
||||
setRaw(propertyExpression);
|
||||
} else {
|
||||
values.put(propertyExpression, new RawArrayValue(vals));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this update has the same logical set clause.
|
||||
*/
|
||||
public boolean isSameByPlan(OrmUpdateProperties that) {
|
||||
return that.values.size() == values.size()
|
||||
&& logicalSetClause().equals(that.logicalSetClause());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the hash for the query plan caching.
|
||||
*/
|
||||
public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(OrmUpdateProperties.class);
|
||||
Set<Map.Entry<String, Value>> entries = values.entrySet();
|
||||
for (Map.Entry<String, Value> entry : entries) {
|
||||
builder.add(entry.getKey());
|
||||
builder.bind(entry.getValue().getBindCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all the bind values for the update set clause.
|
||||
*/
|
||||
public void bind(Binder binder, DataBind dataBind) throws SQLException {
|
||||
for (Value bindValue : values.values()) {
|
||||
bindValue.bind(binder, dataBind);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the actual set clause converting logical property names to db columns etc.
|
||||
*/
|
||||
public String buildSetClause(DeployParser deployParser) {
|
||||
|
||||
int setCount = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Map.Entry<String, Value> entry : values.entrySet()) {
|
||||
String property = entry.getKey();
|
||||
if (setCount++ > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
// translate to db columns and remove table alias placeholders
|
||||
sb.append(deployParser.parse(property).replace("${}", ""));
|
||||
sb.append(entry.getValue().bindClause());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a logical set clause to use for isSameByPlan() use.
|
||||
*/
|
||||
private String logicalSetClause() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, Value> entry : values.entrySet()) {
|
||||
sb.append(", ");
|
||||
sb.append(entry.getKey()).append(entry.getValue().bindClause());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user