diff --git a/pom.xml b/pom.xml index eaaf4fb01..2179b7269 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.avaje.ebeanorm avaje-ebeanorm-agent - 4.1.10 + 4.5.1 test @@ -198,7 +198,7 @@ org.avaje.ebeanorm avaje-ebeanorm-mavenenhancer - 4.1.9 + 4.5.1 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() {