Added @Transactional support for PersistBatch #220

This commit is contained in:
rbygrave
2014-12-12 23:32:41 +13:00
parent bb9d9f7e17
commit 1bd7b99235
10 changed files with 223 additions and 14 deletions
+2 -2
View File
@@ -106,7 +106,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.1.10</version>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
@@ -198,7 +198,7 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.1.9</version>
<version>4.5.1</version>
<executions>
<!-- Not going to enhance Model bean -->
<execution>
@@ -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.
* <p>
@@ -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<Class<? extends Throwable>> rollbackFor;
ArrayList<Class<? extends Throwable>> 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.
*/
@@ -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.
* <p>
* 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.
* </p>
*/
PersistBatch batchOnCascade() default PersistBatch.INHERIT;
/**
* The batch size to use when using JDBC batch mode.
* <p>
* If unset this defaults to the value set in ServerConfig.
* </p>
*/
int batchSize() default 0;
/**
* The transaction isolation level this transaction should have.
* <p>
@@ -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;
@@ -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<Class<? extends Throwable>> 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
@@ -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);
@@ -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;
@@ -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<Long> 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());
}
}
@@ -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() {