From 6c7074e2883998ec40c20c25ee256c3c6ac7abb2 Mon Sep 17 00:00:00 2001
From: Robin Bygrave
+ *
* Example: Get a EbeanServer
*
*
*
* EbeanServer has more API than Ebean{@code
+ *
+ * int rows = Ebean.update(Customer.class)
+ * .set("status", Customer.Status.ACTIVE)
+ * .set("updtime", new Timestamp(System.currentTimeMillis()))
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ *
+ * @param beanType The type of entity bean to update
+ * @param {@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);
* }
- *
+ *
* 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.
*
+ *
{@code
+ *
+ * int rows = ebeanServer
+ * .update(Customer.class)
+ * .set("status", Customer.Status.ACTIVE)
+ * .set("updtime", new Timestamp(System.currentTimeMillis()))
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ *
+ * @param beanType The type of entity bean to update
+ * @param *
{@code
*
* // Find order 2 specifying explicitly the parts of the object graph to
@@ -224,7 +244,6 @@ public interface EbeanServer {
* .findList();
*
* }
- *
*/
*
{@code
*
* // find orders and their customers
@@ -291,10 +310,8 @@ public interface EbeanServer {
*
* }
*
- * @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
*/
* An example: *
- * + **
{@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.
*
- *
+ *
*
{@code
*
* // start a transaction (stored in a ThreadLocal)
@@ -419,7 +435,7 @@ public interface EbeanServer {
* }
*
* }
- *
+ *
*
Transaction options:
* {@code
*
@@ -452,7 +468,7 @@ public interface EbeanServer {
* }
*
* }
- *
+ *
*
* 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.
- *
+ *
*
* Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or
* {@link #execute(TxScope, TxRunnable)}.
*
- *
+ *
*
REQUIRES_NEW example:
* {@code
* // Start a new transaction. If there is a current transaction
@@ -494,7 +510,7 @@ public interface EbeanServer {
* }
*
* }
- *
+ *
*
REQUIRED example:
* {@code
*
@@ -543,25 +559,24 @@ public interface EbeanServer {
*
*
* Code example:
- *
+ *
*
{@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();
* }
*
* }
- *
+ *
*
- *
*/
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.
- *
+ *
*
{@code
* // Fetch order 1
* Order order = ebeanServer.find(Order.class, 1);
* }
- *
+ *
*
* If you want more control over the query then you can use createQuery() and
* Query.findUnique();
*
- *
+ *
*
{@code
* // ... additionally fetching customer, customer shipping address,
* // order details, and the product associated with each order detail.
@@ -628,10 +640,8 @@ public interface EbeanServer {
*
* }
*
- * @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 find(Class beanType, Object id);
@@ -658,7 +668,7 @@ public interface EbeanServer {
*
*
* }
- *
+ *
*
Lazy loading characteristics
* {@code
*
@@ -673,10 +683,8 @@ public interface EbeanServer {
*
* }
*
- * @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 getReference(Class 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).
*
- *
+ *
*
{@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).
*
- *
+ *
*
{@code
*
* ebeanServer.find(Order.class)
@@ -766,8 +774,8 @@ public interface EbeanServer {
/**
* Return versions of a @History entity 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.
+ * 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.
*
*/
List> findVersions(Query 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.
*
- *
+ *
*
{@code
*
* List customers =
@@ -789,14 +797,10 @@ public interface EbeanServer {
*
* }
*
- * @param
- * the type of entity bean to fetch.
- * @param query
- * the query to execute.
- * @param transaction
- * the transaction to use (can be null).
+ * @param 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()
*/
List findList(Query query, Transaction transaction);
@@ -808,13 +812,10 @@ public interface EbeanServer {
* execution status (isDone etc) and get the value (with or without a
* timeout).
*
- *
- * @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()
*/
FutureRowCount findFutureRowCount(Query query, Transaction transaction);
@@ -826,13 +827,10 @@ public interface EbeanServer {
* execution status (isDone etc) and get the value (with or without a
* timeout).
*
- *
- * @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()
*/
FutureIds findFutureIds(Query 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()
*/
FutureList findFutureList(Query 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.
*
- *
+ *
*
{@code
*
* PagedList pagedList = Ebean.find(Order.class)
@@ -884,7 +878,6 @@ public interface EbeanServer {
* }
*
* @return The PagedList
- *
* @see Query#findPagedList()
*/
PagedList findPagedList(Query 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.
*
- *
+ *
*
{@code
*
* Set customers =
@@ -905,15 +898,11 @@ public interface EbeanServer {
* .findSet();
*
* }
- *
- * @param
- * 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 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()
*/
Set findSet(Query 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.
*
- *
- * @param
- * 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 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()
*/
Map, T> findMap(Query query, Transaction transaction);
@@ -950,15 +935,11 @@ public interface EbeanServer {
* explicitly control the transaction used for the query.
*
*
- * @param
- * the type of entity bean to fetch.
- * @param query
- * the query to execute.
- * @param transaction
- * the transaction to use (can be null).
+ * @param 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 {
*/
int delete(Query query, Transaction transaction);
+ /**
+ * Execute the update query returning the number of rows updated.
+ *
+ * The update query must be created using {@link #update(Class)}.
+ *
+ *
+ * @param query the update query to execute
+ * @param transaction the optional transaction to use for the update (can be null)
+ * @param the type of entity bean
+ * @return The number of rows updated
+ */
+ int update(Query query, Transaction transaction);
+
/**
* Execute the sql query returning a list of MapBean.
*
@@ -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.
*
- *
- * @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 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.
*
- *
- * @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.
*
- *
+ *
*
{@code
* public class Order { ...
*
@@ -1063,7 +1051,7 @@ public interface EbeanServer {
* ...
* }
* }
- *
+ *
*
* 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 {
*
* Example:
*
- *
+ *
*
{@code
*
* // example that uses 'named' parameters
@@ -1212,11 +1200,8 @@ public interface EbeanServer {
*
* }
*
- * @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 {
*
* Example:
*
- *
+ *
*
{@code
*
* String sql = "{call sp_order_modify(?,?,?)}";
@@ -1288,28 +1273,20 @@ public interface EbeanServer {
* information is registered immediately (with the transaction manager).
*
*
- * @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
- * 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 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 find(Class beanType, Object id, Transaction transaction);
@@ -1331,20 +1308,20 @@ public interface EbeanServer {
*
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
- *
+ *
*
{@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);
- *
+ *
* }
*/
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.
*
- *
+ *
*
{@code
- *
+ *
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName("ModifiedNameNoOCC");
* ebeanServer.update(customer);
- *
+ *
* }
- *
+ *
* @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.
*
- *
+ *
*
{@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).
*
- *
+ *
*
{@code
*
* ebeanServer.execute(new TxRunnable() {
@@ -1503,7 +1477,7 @@ public interface EbeanServer {
* The scope can control the transaction type, isolation and rollback
* semantics.
*
- *
+ *
*
{@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).
*
- *
+ *
*
{@code
*
* ebeanServer.execute(new TxCallable() {
@@ -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.
*
- *
+ *
*
Simple example:
* {@code
*
@@ -1580,7 +1553,7 @@ public interface EbeanServer {
* System.out.println(jsonOutput);
*
* }
- *
+ *
*
Using PathProperties:
* {@code
*
@@ -1630,9 +1603,9 @@ public interface EbeanServer {
* The values are published from the draft to the live bean.
*
*
- * @param the type of the entity bean
- * @param beanType the type of the entity bean
- * @param id the id of the entity bean
+ * @param the type of the entity bean
+ * @param beanType the type of the entity bean
+ * @param id the id of the entity bean
*/
T publish(Class beanType, Object id);
@@ -1655,8 +1628,8 @@ public interface EbeanServer {
* The values are published from the draft beans to the live beans.
*
*
- * @param the type of the entity bean
- * @param query the query used to select the draft beans to publish
+ * @param the type of the entity bean
+ * @param query the query used to select the draft beans to publish
*/
List publish(Query query);
@@ -1681,9 +1654,9 @@ public interface EbeanServer {
* @DraftDirty and @DraftReset properties are reset.
*
*
- * @param 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 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 draftRestore(Class beanType, Object id);
@@ -1707,8 +1680,8 @@ public interface EbeanServer {
* @DraftDirty and @DraftReset properties are reset.
*
*
- * @param the type of the entity bean
- * @param query the query used to select the draft beans to restore
+ * @param the type of the entity bean
+ * @param query the query used to select the draft beans to restore
*/
List draftRestore(Query query);
diff --git a/src/main/java/com/avaje/ebean/ExpressionList.java b/src/main/java/com/avaje/ebean/ExpressionList.java
index d5cc62bc6..31cbfdc49 100644
--- a/src/main/java/com/avaje/ebean/ExpressionList.java
+++ b/src/main/java/com/avaje/ebean/ExpressionList.java
@@ -135,10 +135,18 @@ public interface ExpressionList {
* optimal depending on the database platform.
*
*
- * @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.
*
diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java
index f4f205018..c75a5ba0d 100644
--- a/src/main/java/com/avaje/ebean/Query.java
+++ b/src/main/java/com/avaje/ebean/Query.java
@@ -763,6 +763,11 @@ public interface Query {
*/
int delete();
+ /**
+ * Execute the UpdateQuery returning the number of rows updated.
+ */
+ int update();
+
/**
* Return the count of entities this query should return.
*
diff --git a/src/main/java/com/avaje/ebean/UpdateQuery.java b/src/main/java/com/avaje/ebean/UpdateQuery.java
new file mode 100644
index 000000000..0006f608e
--- /dev/null
+++ b/src/main/java/com/avaje/ebean/UpdateQuery.java
@@ -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.
+ *
+ * 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.
+ *
+ *
+ * Example: Simple update
+ *
+ * {@code
+ *
+ * int rows = ebeanServer
+ * .update(Customer.class)
+ * .set("status", Customer.Status.ACTIVE)
+ * .set("updtime", new Timestamp(System.currentTimeMillis()))
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ * {@code sql
+ *
+ * update o_customer set status=?, updtime=? where id > ?
+ *
+ * }
+ *
+ *
+ * Note that if the where() clause contains a join then the SQL update changes to use a
+ * WHERE ID IN () form.
+ *
+ *
+ * Example: Update with a JOIN
+ *
+ * In this example the expression .eq("billingAddress.country", nz) requires a join
+ * to the address table.
+ *
+ *
+ * {@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();
+ * }
+ *
+ * {@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 > ? )
+ *
+ * }
+ *
+ * @param The type of entity bean being updated
+ *
+ * @see SqlUpdate
+ */
+public interface UpdateQuery {
+
+ /**
+ * Set the value of a property.
+ *
+ *
+ * {@code
+ *
+ * int rows = ebeanServer
+ * .update(Customer.class)
+ * .set("status", Customer.Status.ACTIVE)
+ * .set("updtime", new Timestamp(System.currentTimeMillis()))
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ *
+ * @param property The bean property to be set
+ * @param value The value to set the property to
+ */
+ UpdateQuery set(String property, Object value);
+
+ /**
+ * Set the property to be null.
+ *
+ * {@code
+ *
+ * int rows = ebeanServer
+ * .update(Customer.class)
+ * .setNull("notes")
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ *
+ * @param property The property to be set to null.
+ */
+ UpdateQuery setNull(String property);
+
+ /**
+ *
+ * Set using a property expression that does not need any bind values.
+ *
+ * The property expression typically contains database functions.
+ *
+ *
+ * {@code
+ *
+ * int rows = ebeanServer
+ * .update(Customer.class)
+ * .setRaw("status = coalesce(status, 'A')")
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ *
+ * @param propertyExpression A property expression
+ */
+ UpdateQuery setRaw(String propertyExpression);
+
+ /**
+ * Set using a property expression that can contain ? bind value placeholders.
+ *
+ * For each ? in the property expression there should be a matching bind value supplied.
+ *
+ * {@code
+ *
+ * int rows = ebeanServer
+ * .update(Customer.class)
+ * .setRaw("status = coalesce(status, ?)", Customer.Status.ACTIVE)
+ * .where()
+ * .gt("id", 1000)
+ * .update();
+ *
+ * }
+ *
+ * @param propertyExpression A raw property expression
+ * @param values The values to bind with the property expression
+ */
+ UpdateQuery setRaw(String propertyExpression, Object... values);
+
+ /**
+ * Return the query expression list to add predicates to.
+ */
+ ExpressionList where();
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
index a74fcc50e..67a0f4e95 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
@@ -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 extends Query {
* Delete query.
*/
DELETE,
+
+ /**
+ * Update query.
+ */
+ UPDATE,
}
enum TemporalMode {
@@ -677,4 +683,9 @@ public interface SpiQuery extends Query {
*/
Set validate(BeanType desc);
+ /**
+ * Return the properties for an update query.
+ */
+ OrmUpdateProperties getUpdateProperties();
+
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index 024f02b9c..1546a2428 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -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(this, descriptor);
}
+ public UpdateQuery update(Class beanType) {
+ return new DefaultUpdateQuery(createQuery(beanType));
+ }
+
public Query find(Class beanType) {
return createQuery(beanType);
}
- public Query createQuery(Class beanType) {
+ public DefaultOrmQuery createQuery(Class beanType) {
BeanDescriptor 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 int update(Query query, Transaction t) {
+
+ SpiOrmQueryRequest request = createQueryRequest(Type.UPDATE, query, t);
+ try {
+ request.initTransIfRequired();
+ request.markNotQueryOnly();
+ return request.update();
+ } finally {
+ request.endTransIfRequired();
+ }
+ }
+
public FutureRowCount findFutureRowCount(Query q, Transaction t) {
SpiQuery copy = ((SpiQuery) q).copy();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java
index 8223af5a3..02464b5bc 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java
@@ -42,4 +42,9 @@ public interface OrmQueryEngine {
* Execute the query as a delete statement.
*/
int delete(OrmQueryRequest request);
+
+ /**
+ * Execute the query as a update statement.
+ */
+ int update(OrmQueryRequest request);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
index f6ed671f6..754637407 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
@@ -286,6 +286,13 @@ public final class OrmQueryRequest 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.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java
index b36cd132b..b5f22603a 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java
@@ -52,6 +52,11 @@ public interface SpiOrmQueryRequest extends DocQueryRequest {
*/
int delete();
+ /**
+ * Execute the query as a update.
+ */
+ int update();
+
/**
* Execute the query as findById.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
index f08c3d3ee..6671e8e78 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -367,6 +367,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
private String deleteByIdSql;
private String deleteByIdInSql;
+ private String whereIdInSql;
private String softDeleteByIdSql;
private String softDeleteByIdInSql;
@@ -687,7 +688,8 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
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 implements MetaBeanInfo, BeanType {
}
}
+ /**
+ * Return the "where id in" sql (for use with UpdateQuery).
+ */
+ public String getWhereIdInSql() {
+ return whereIdInSql;
+ }
+
/**
* Return the "delete by id" sql.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionList.java b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionList.java
index cf711dcc0..d6fd82fa4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/DefaultExpressionList.java
@@ -277,6 +277,11 @@ public class DefaultExpressionList implements SpiExpressionList {
return query.delete();
}
+ @Override
+ public int update() {
+ return query.update();
+ }
+
@Override
public FutureIds findFutureIds() {
return query.findFutureIds();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
index e7d3502de..b1541b3f8 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
@@ -279,6 +279,11 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression
return exprList.delete();
}
+ @Override
+ public int update() {
+ return exprList.update();
+ }
+
@Override
public Query asOf(Timestamp asOf) {
return exprList.asOf(asOf);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java
index e9dde50d5..cc0de5c46 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java
@@ -88,7 +88,7 @@ public class CQueryBuilder {
/**
* Build the delete query.
*/
- public CQueryDelete buildDeleteQuery(OrmQueryRequest request) {
+ public CQueryUpdate buildUpdateQuery(String type, OrmQueryRequest request) {
SpiQuery 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 String buildDeleteSql(OrmQueryRequest 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 String buildUpdateSql(OrmQueryRequest 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();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java
index 72a9d3e0c..43f793e14 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java
@@ -51,10 +51,18 @@ public class CQueryEngine {
}
public int delete(OrmQueryRequest request) {
+ CQueryUpdate query = queryBuilder.buildUpdateQuery("Delete", request);
+ return executeUpdate(request, query);
+ }
- CQueryDelete query = queryBuilder.buildDeleteQuery(request);
+ public int update(OrmQueryRequest request) {
+ CQueryUpdate query = queryBuilder.buildUpdateQuery("Update", request);
+ return executeUpdate(request, query);
+ }
+
+ private int executeUpdate(OrmQueryRequest request, CQueryUpdate query) {
try {
- int rows = query.delete();
+ int rows = query.execute();
if (request.isLogSql()) {
String logSql = query.getGeneratedSql();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java
index 4b69efc4b..645221b6e 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPredicates.java
@@ -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.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java
index 9a8d81da1..8949b4868 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java
@@ -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.
- *
- * The jdbc resultSet and statement need to be closed. Its important that
- * this method is called.
- *
*/
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;
}
-
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryDelete.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryUpdate.java
similarity index 77%
rename from src/main/java/com/avaje/ebeaninternal/server/query/CQueryDelete.java
rename to src/main/java/com/avaje/ebeaninternal/server/query/CQueryUpdate.java
index 5f005ad33..e8a012af4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryDelete.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryUpdate.java
@@ -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;
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java
index f32c791da..3342637d4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java
@@ -53,6 +53,12 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
return queryEngine.delete(request);
}
+ public int update(OrmQueryRequest request) {
+
+ flushJdbcBatchOnQuery(request);
+ return queryEngine.update(request);
+ }
+
public int findRowCount(OrmQueryRequest request) {
flushJdbcBatchOnQuery(request);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/UtilJdbc.java b/src/main/java/com/avaje/ebeaninternal/server/query/UtilJdbc.java
new file mode 100644
index 000000000..824e0f0d5
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/UtilJdbc.java
@@ -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);
+ }
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 2d343c0cf..472b1a698 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -230,6 +230,8 @@ public class DefaultOrmQuery implements SpiQuery {
private boolean useDocStore;
+ private OrmUpdateProperties updateProperties;
+
public DefaultOrmQuery(BeanDescriptor desc, EbeanServer server, ExpressionFactory expressionFactory) {
this.beanDescriptor = desc;
this.beanType = desc.getBeanType();
@@ -831,7 +833,7 @@ public class DefaultOrmQuery implements SpiQuery {
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 implements SpiQuery {
return server.delete(this, null);
}
+ @Override
+ public int update() {
+ return server.update(this, null);
+ }
+
@Override
public List