diff --git a/src/main/java/com/avaje/ebean/Transaction.java b/src/main/java/com/avaje/ebean/Transaction.java
index 250283c36..c61dbad0b 100644
--- a/src/main/java/com/avaje/ebean/Transaction.java
+++ b/src/main/java/com/avaje/ebean/Transaction.java
@@ -212,6 +212,11 @@ public interface Transaction extends Closeable {
*/
public void setBatchSize(int batchSize);
+ /**
+ * Return the current batch size.
+ */
+ public int getBatchSize();
+
/**
* Specify if you want batched inserts to use getGeneratedKeys.
*
diff --git a/src/main/java/com/avaje/ebean/TxScope.java b/src/main/java/com/avaje/ebean/TxScope.java
index 286f2fd75..1b1ee4268 100644
--- a/src/main/java/com/avaje/ebean/TxScope.java
+++ b/src/main/java/com/avaje/ebean/TxScope.java
@@ -1,5 +1,7 @@
package com.avaje.ebean;
+import com.avaje.ebean.config.PersistBatch;
+
import java.util.ArrayList;
/**
@@ -28,12 +30,39 @@ public final class TxScope {
TxIsolation isolation;
+ PersistBatch batch;
+
+ PersistBatch batchOnCascade;
+
+ int batchSize;
+
boolean readOnly;
ArrayList> rollbackFor;
ArrayList> noRollbackFor;
+ /**
+ * Return true if PersistBatch has been set.
+ */
+ public boolean isBatchSet() {
+ return batch != null && batch != PersistBatch.INHERIT;
+ }
+
+ /**
+ * Return true if batch on cascade has been set.
+ */
+ public boolean isBatchOnCascadeSet() {
+ return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
+ }
+
+ /**
+ * Return true if batch size has been set.
+ */
+ public boolean isBatchSizeSet() {
+ return batchSize > 0;
+ }
+
/**
* Helper method to create a TxScope with REQUIRES.
*/
@@ -114,6 +143,51 @@ public final class TxScope {
return this;
}
+ /**
+ * Return the batch mode.
+ */
+ public PersistBatch getBatch() {
+ return batch;
+ }
+
+ /**
+ * Set the batch mode to use.
+ */
+ public TxScope setBatch(PersistBatch batch) {
+ this.batch = batch;
+ return this;
+ }
+
+ /**
+ * Return the batch on cascade mode.
+ */
+ public PersistBatch getBatchOnCascade() {
+ return batchOnCascade;
+ }
+
+ /**
+ * Set the batch on cascade mode.
+ */
+ public TxScope setBatchOnCascade(PersistBatch batchOnCascade) {
+ this.batchOnCascade = batchOnCascade;
+ return this;
+ }
+
+ /**
+ * Return the batch size. 0 means use the default value.
+ */
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ /**
+ * Set the batch size to use.
+ */
+ public TxScope setBatchSize(int batchSize) {
+ this.batchSize = batchSize;
+ return this;
+ }
+
/**
* Return if the transaction should be treated as read only.
*/
diff --git a/src/main/java/com/avaje/ebean/annotation/Transactional.java b/src/main/java/com/avaje/ebean/annotation/Transactional.java
index a27b80fe4..a6eb1b18b 100644
--- a/src/main/java/com/avaje/ebean/annotation/Transactional.java
+++ b/src/main/java/com/avaje/ebean/annotation/Transactional.java
@@ -7,6 +7,7 @@ import java.lang.annotation.Target;
import com.avaje.ebean.TxIsolation;
import com.avaje.ebean.TxType;
+import com.avaje.ebean.config.PersistBatch;
/**
* Specify transaction scoping for a method.
@@ -66,6 +67,29 @@ public @interface Transactional {
*/
TxType type() default TxType.REQUIRED;
+ /**
+ * Persist batch mode for the transaction.
+ */
+ PersistBatch batch() default PersistBatch.INHERIT;
+
+ /**
+ * Persist batch mode for the request if not set on the transaction.
+ *
+ * If batch is set to NONE then batchOnCascade can be set to INSERT or ALL
+ * and then each save(), delete(), insert(), update() request that cascades
+ * to child beans can use JDBC batch.
+ *
+ */
+ PersistBatch batchOnCascade() default PersistBatch.INHERIT;
+
+ /**
+ * The batch size to use when using JDBC batch mode.
+ *
+ * If unset this defaults to the value set in ServerConfig.
+ *
+ */
+ int batchSize() default 0;
+
/**
* The transaction isolation level this transaction should have.
*
diff --git a/src/main/java/com/avaje/ebean/config/PersistBatch.java b/src/main/java/com/avaje/ebean/config/PersistBatch.java
index 6890d23a5..462a29a19 100644
--- a/src/main/java/com/avaje/ebean/config/PersistBatch.java
+++ b/src/main/java/com/avaje/ebean/config/PersistBatch.java
@@ -27,7 +27,14 @@ public enum PersistBatch {
/**
* Use JDBC Batch mode on Inserts, Updates and Deletes.
*/
- ALL(true);
+ ALL(true),
+
+ /**
+ * You should not use this value explicitly. It should only used on the Transactional annotation
+ * to indicate that the value should inherit from the ServerConfig setting.
+ */
+ INHERIT(false);
+
boolean forInsert;
diff --git a/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java b/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java
index 42a86c48f..bfca3fabf 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.api;
import java.util.ArrayList;
import com.avaje.ebean.TxScope;
+import com.avaje.ebean.config.PersistBatch;
/**
* Used internally to handle the scoping of transactions for methods.
@@ -10,14 +11,14 @@ import com.avaje.ebean.TxScope;
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
private static final int OPCODE_ATHROW = 191;
- //private static final int OPCODE_ATHROW = com.avaje.ebean.enhance.asm.Opcodes.ATHROW;
-
+
private final SpiTransactionScopeManager scopeMgr;
/**
* The suspended transaction (can be null).
*/
private final SpiTransaction suspendedTransaction;
+
/**
* The transaction in scope (can be null).
*/
@@ -44,7 +45,13 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
*/
private final ArrayList> rollbackFor;
- /**
+ private PersistBatch restoreBatch;
+
+ private PersistBatch restoreBatchOnCascade;
+
+ private int restoreBatchSize;
+
+ /**
* Flag set when a rollback has occurred.
*/
private boolean rolledBack;
@@ -61,6 +68,24 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
+
+ if (transaction != null) {
+ if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
+ restoreBatch = transaction.getBatch();
+ restoreBatchOnCascade = transaction.getBatchOnCascade();
+ restoreBatchSize = transaction.getBatchSize();
+ }
+ if (txScope.isBatchSet()) {
+ transaction.setBatch(txScope.getBatch());
+ }
+ if (txScope.isBatchOnCascadeSet()) {
+ transaction.setBatchOnCascade(txScope.getBatchOnCascade());
+ }
+ if (txScope.isBatchSizeSet()) {
+ transaction.setBatchSize(txScope.getBatchSize());
+ }
+ }
+
}
/**
@@ -97,10 +122,22 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
*/
public void onFinally() {
try {
- if (!rolledBack && created) {
- transaction.commit();
+ if (!rolledBack) {
+ if (created) {
+ transaction.commit();
+ } else {
+ if (restoreBatch != null) {
+ transaction.setBatch(restoreBatch);
+ }
+ if (restoreBatchOnCascade != null) {
+ transaction.setBatchOnCascade(restoreBatchOnCascade);
+ }
+ if (restoreBatchSize > 0) {
+ transaction.setBatchSize(restoreBatchSize);
+ }
+ }
}
-
+
} finally {
if (suspendedTransaction != null){
// put the previously suspended transaction
diff --git a/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertSimple.java b/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertSimple.java
index 7bad50f4d..0cde6e621 100644
--- a/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertSimple.java
+++ b/src/test/java/com/avaje/tests/batchinsert/TestBatchInsertSimple.java
@@ -3,6 +3,7 @@ package com.avaje.tests.batchinsert;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Transaction;
+import com.avaje.ebean.annotation.Transactional;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.tests.model.basic.UTDetail;
import com.avaje.tests.model.basic.UTMaster;
@@ -29,7 +30,7 @@ public class TestBatchInsertSimple extends BaseTestCase {
for (int i = 0; i < numOfMasters; i++) {
UTMaster master = createMasterAndDetails(i, 20);
- Ebean.save(master);
+ master.save();
}
transaction.commit();
@@ -39,6 +40,24 @@ public class TestBatchInsertSimple extends BaseTestCase {
}
}
+ @Test
+ public void testTransactional() {
+
+ saveWithFullBatchMode();
+ }
+
+ @Transactional(batch=PersistBatch.ALL, batchSize=50)
+ public void saveWithFullBatchMode() {
+
+ int numOfMasters = 4;
+
+ for (int i = 0; i < numOfMasters; i++) {
+ UTMaster master = createMasterAndDetails(i, 5);
+ // the save is 'batched' and does not execute immediately
+ // ... it now acts more like 'merge/persist'
+ master.save();
+ }
+ }
@Test
public void testJdbcBatchPerRequestWithMasterOnly() {
@@ -132,8 +151,8 @@ public class TestBatchInsertSimple extends BaseTestCase {
UTDetail detail = new UTDetail();
detail.setName("batchInsert-detail-" + position);
- detail.setQty(Integer.valueOf(qty));
- detail.setAmount(Double.valueOf(amount));
+ detail.setQty(qty);
+ detail.setAmount(amount);
// System.out.println("-- "+detail);
diff --git a/src/test/java/com/avaje/tests/model/basic/UTMaster.java b/src/test/java/com/avaje/tests/model/basic/UTMaster.java
index 828fbdf94..b8854d529 100644
--- a/src/test/java/com/avaje/tests/model/basic/UTMaster.java
+++ b/src/test/java/com/avaje/tests/model/basic/UTMaster.java
@@ -1,5 +1,7 @@
package com.avaje.tests.model.basic;
+import com.avaje.ebean.Model;
+
import java.util.ArrayList;
import java.util.List;
@@ -12,7 +14,7 @@ import javax.persistence.Version;
@Entity
@Table(name="ut_master")
-public class UTMaster {
+public class UTMaster extends Model {
@Id
Integer id;
diff --git a/src/test/java/com/avaje/tests/model/basic/xtra/DummyDao.java b/src/test/java/com/avaje/tests/model/basic/xtra/DummyDao.java
index bba947a8f..87eab84a0 100644
--- a/src/test/java/com/avaje/tests/model/basic/xtra/DummyDao.java
+++ b/src/test/java/com/avaje/tests/model/basic/xtra/DummyDao.java
@@ -4,6 +4,7 @@ import java.util.List;
import javax.persistence.EntityNotFoundException;
+import com.avaje.ebean.config.PersistBatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,6 +13,8 @@ import com.avaje.ebean.Transaction;
import com.avaje.ebean.TxType;
import com.avaje.ebean.annotation.Transactional;
+import static org.junit.Assert.assertEquals;
+
public class DummyDao {
Logger logger = LoggerFactory.getLogger(DummyDao.class);
@@ -33,5 +36,34 @@ public class DummyDao {
public void addToObject(Long id, Double anotherNumber, List ids) throws EntityNotFoundException {
// and more code
}
-
+
+
+ @Transactional(batch = PersistBatch.ALL, batchOnCascade = PersistBatch.ALL, batchSize = 99)
+ public void doWithBatchOptionsSet() {
+
+ Transaction txn = Ebean.currentTransaction();
+ assertEquals(PersistBatch.ALL, txn.getBatch());
+ assertEquals(PersistBatch.ALL, txn.getBatchOnCascade());
+ assertEquals(99, txn.getBatchSize());
+ }
+
+
+ @Transactional(batch = PersistBatch.INSERT, batchOnCascade = PersistBatch.NONE, batchSize = 77)
+ public void doOuterWithBatchOptionsSet() {
+
+ Transaction txn = Ebean.currentTransaction();
+
+ assertEquals(PersistBatch.INSERT, txn.getBatch());
+ assertEquals(PersistBatch.NONE, txn.getBatchOnCascade());
+ assertEquals(77, txn.getBatchSize());
+
+ doWithBatchOptionsSet();
+
+ // batch options set back
+ assertEquals(PersistBatch.INSERT, txn.getBatch());
+ assertEquals(PersistBatch.NONE, txn.getBatchOnCascade());
+ assertEquals(77, txn.getBatchSize());
+
+ }
+
}
diff --git a/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java b/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java
index 6f0cda2b6..ddecf3197 100644
--- a/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java
+++ b/src/test/java/com/avaje/tests/unitinternal/TestTxTypeOnTransactional.java
@@ -18,6 +18,15 @@ public class TestTxTypeOnTransactional extends BaseTestCase {
Logger logger = LoggerFactory.getLogger(TestTxTypeOnTransactional.class);
+ @Test
+ public void testBatchOptionsAreSet() {
+
+ logger.info("-- test pre doOuterWithBatchOptionsSet");
+ DummyDao dao = new DummyDao();
+ dao.doOuterWithBatchOptionsSet();
+ logger.info("-- test post doOuterWithBatchOptionsSet");
+ }
+
@Test
public void test() {