#1197 - Refactor how @Transactional methods internally managed

This commit is contained in:
Rob Bygrave
2017-11-03 02:13:56 +13:00
parent 0a1747d8e3
commit ff67ef60b0
14 changed files with 658 additions and 565 deletions
+19 -12
View File
@@ -25,32 +25,32 @@ import java.util.concurrent.Callable;
*/
public final class TxScope {
int profileId;
private int profileId;
TxType type;
private TxType type;
String serverName;
private String serverName;
TxIsolation isolation;
private TxIsolation isolation;
PersistBatch batch;
private PersistBatch batch;
PersistBatch batchOnCascade;
private PersistBatch batchOnCascade;
int batchSize;
private int batchSize;
boolean skipGeneratedKeys;
private boolean skipGeneratedKeys;
boolean readOnly;
private boolean readOnly;
/**
* Set this to false if the JDBC batch should not be automatically be flushed when a query is executed.
*/
boolean flushOnQuery = true;
private boolean flushOnQuery = true;
ArrayList<Class<? extends Throwable>> rollbackFor;
private ArrayList<Class<? extends Throwable>> rollbackFor;
ArrayList<Class<? extends Throwable>> noRollbackFor;
private ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Helper method to create a TxScope with REQUIRES.
@@ -275,6 +275,13 @@ public final class TxScope {
return this;
}
/**
* Return the isolation level.
*/
public int getIsolationLevel() {
return isolation != null ? isolation.getLevel() : -1;
}
/**
* Return the Isolation level this transaction should run with.
*/
@@ -1,7 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.TxScope;
/**
@@ -10,28 +9,20 @@ import io.ebean.TxScope;
public class HelpScopeTrans {
/**
* Create a ScopeTrans for a given methods TxScope.
* Entering an enhanced transactional method.
*/
public static ScopeTrans createScopeTrans(TxScope txScope) {
EbeanServer server = Ebean.getServer(txScope.getServerName());
SpiEbeanServer iserver = (SpiEbeanServer) server;
return iserver.createScopeTrans(txScope);
public static void enter(TxScope txScope) {
server().scopedTransactionEnter(txScope);
}
/**
* Exiting the method in an expected fashion.
* <p>
* That is returning successfully or via a caught exception.
* Unexpected exceptions are caught via the Thread uncaughtExceptionHandler.
* </p>
*
* @param returnOrThrowable the return or throwable object
* @param opCode the opcode for ATHROW or ARETURN etc
* @param scopeTrans the scoped transaction the method was run with.
* Exiting an enhanced transactional method.
*/
public static void onExitScopeTrans(Object returnOrThrowable, int opCode, ScopeTrans scopeTrans) {
public static void exit(Object returnOrThrowable, int opCode) {
server().scopedTransactionExit(returnOrThrowable, opCode);
}
scopeTrans.onExit(returnOrThrowable, opCode);
private static SpiEbeanServer server() {
return (SpiEbeanServer) Ebean.getDefaultServer();
}
}
@@ -8,17 +8,10 @@ import java.util.ArrayList;
/**
* Used internally to handle the scoping of transactions for methods.
*/
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
public class ScopeTrans {
private static final int OPCODE_ATHROW = 191;
private final SpiTransactionScopeManager scopeMgr;
/**
* The suspended transaction (can be null).
*/
private final SpiTransaction suspendedTransaction;
/**
* The transaction in scope (can be null).
*/
@@ -61,15 +54,11 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
private boolean rolledBack;
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope) {
this.rollbackOnChecked = rollbackOnChecked;
this.created = created;
this.transaction = transaction;
this.suspendedTransaction = suspendedTransaction;
this.scopeMgr = scopeMgr;
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
@@ -108,55 +97,30 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
}
/**
* Called when the Thread catches any uncaught exception.
* For example, an unexpected NullPointerException or Error.
* Complete the transaction from enhanced transactional. Try to commit.
*/
@Override
public void uncaughtException(Thread thread, Throwable e) {
// rollback transaction if required
caughtThrowable(e);
// reinstate suspended transaction
onFinally();
}
/**
* Returned via RETURN or expected Exception from the method.
*
* @param returnOrThrowable the return value or Throwable
* @param opCode indicates
*/
public void onExit(Object returnOrThrowable, int opCode) {
void complete(Object returnOrThrowable, int opCode) {
if (opCode == OPCODE_ATHROW) {
// exited with a Throwable
caughtThrowable((Throwable) returnOrThrowable);
}
onFinally();
complete();
}
/**
* Commit if the transaction exists and has not already been rolled back.
* Also reinstate the suspended transaction if there was one.
* Complete the transaction programmatically. Try to commit.
*/
public void onFinally() {
try {
if (!rolledBack) {
commitTransaction();
}
} finally {
restoreSuspended();
public void complete() {
if (!rolledBack) {
commitTransaction();
}
}
protected void restoreSuspended() {
if (created || suspendedTransaction != null) {
// put the previously suspended transaction
// back onto the ThreadLocal or equivalent
scopeMgr.replace(suspendedTransaction);
public void end() {
if (created) {
transaction.end();
}
}
@@ -247,17 +211,10 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
}
}
if (e instanceof RuntimeException) {
return true;
} else {
// checked exceptions...
// EJB defaults this to false which is not intuitive IMO
// Ebean makes this configurable (default to true)
return rollbackOnChecked;
}
// checked exceptions...
// EJB defaults this to false which is not intuitive IMO
// Ebean makes this configurable (default to true)
return e instanceof RuntimeException || rollbackOnChecked;
}
}
@@ -1,432 +1,116 @@
package io.ebeaninternal.api;
import io.ebean.annotation.PersistBatch;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.transaction.ProfileStream;
import io.ebeanservice.docstore.api.DocStoreTransaction;
import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.server.util.ArrayStack;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Wrapper of a ScopeTrans request and it's underlying transaction.
* Manage scoped (typically thread local) transactions.
*
* These can be nested and internally they are pushed and popped from a stack.
*/
public class ScopedTransaction implements SpiTransaction {
public class ScopedTransaction extends SpiTransactionProxy {
private final ScopeTrans scopeTrans;
private final TransactionScopeManager manager;
private final SpiTransaction transaction;
/**
* Stack of 'nested' transactions.
*/
private ArrayStack<ScopeTrans> stack = new ArrayStack<>();
private boolean committed;
private ScopeTrans current;
public ScopedTransaction(ScopeTrans scopeTrans) {
this.scopeTrans = scopeTrans;
this.transaction = scopeTrans.getTransaction();
public ScopedTransaction(TransactionScopeManager manager) {
this.manager = manager;
}
@Override
public PersistenceException translate(String message, SQLException cause) {
return transaction.translate(message, cause);
/**
* Push the scope transaction.
*/
public void push(ScopeTrans scopeTrans) {
if (current != null) {
stack.push(current);
}
current = scopeTrans;
transaction = scopeTrans.getTransaction();
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
/**
* Exiting an enhanced transactional method.
*/
public void complete(Object returnOrThrowable, int opCode) {
current.complete(returnOrThrowable, opCode);
pop();
}
@Override
public void commit() {
scopeTrans.commitTransaction();
committed = true;
/**
* Programmatic complete - finally block, try to commit.
*/
public void complete() {
current.complete();
pop();
}
@Override
public void rollback() throws PersistenceException {
scopeTrans.rollback(null);
}
@Override
public void rollback(Throwable e) throws PersistenceException {
scopeTrans.rollback(e);
}
@Override
public void setRollbackOnly() {
scopeTrans.setRollbackOnly();
}
@Override
public boolean isRollbackOnly() {
return transaction.isRollbackOnly();
}
@Override
public void end() throws PersistenceException {
try {
if (!committed) {
scopeTrans.rollback(null);
}
} finally {
scopeTrans.restoreSuspended();
private void pop() {
if (!stack.isEmpty()) {
current = stack.pop();
} else {
manager.set(null);
}
}
@Override
public long profileOffset() {
return transaction.profileOffset();
}
@Override
public void profileEvent(SpiProfileTransactionEvent event) {
transaction.profileEvent(event);
}
@Override
public ProfileStream profileStream() {
return transaction.profileStream();
}
@Override
public void setTenantId(Object tenantId) {
transaction.setTenantId(tenantId);
}
@Override
public Object getTenantId() {
return transaction.getTenantId();
}
@Override
public DocStoreTransaction getDocStoreTransaction() {
return transaction.getDocStoreTransaction();
}
@Override
public DocStoreMode getDocStoreMode() {
return transaction.getDocStoreMode();
}
@Override
public void setDocStoreMode(DocStoreMode mode) {
transaction.setDocStoreMode(mode);
}
@Override
public int getDocStoreBatchSize() {
return transaction.getDocStoreBatchSize();
}
@Override
public void setDocStoreBatchSize(int batchSize) {
transaction.setDocStoreBatchSize(batchSize);
}
@Override
public String getLogPrefix() {
return transaction.getLogPrefix();
}
@Override
public boolean isLogSql() {
return transaction.isLogSql();
}
@Override
public boolean isLogSummary() {
return transaction.isLogSummary();
}
@Override
public void logSql(String msg) {
transaction.logSql(msg);
}
@Override
public void logSummary(String msg) {
transaction.logSummary(msg);
}
@Override
public void setSkipCache(boolean skipCache) {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
}
@Override
public void addBeanChange(BeanChange beanChange) {
transaction.addBeanChange(beanChange);
}
@Override
public void sendChangeLog(ChangeSet changes) {
transaction.sendChangeLog(changes);
}
@Override
public void registerDeferred(PersistDeferredRelationship derived) {
transaction.registerDeferred(derived);
}
@Override
public void registerDeleteBean(Integer hash) {
transaction.registerDeleteBean(hash);
}
@Override
public void unregisterDeleteBean(Integer hash) {
transaction.unregisterDeleteBean(hash);
}
@Override
public boolean isRegisteredDeleteBean(Integer hash) {
return transaction.isRegisteredDeleteBean(hash);
}
@Override
public void unregisterBean(Object bean) {
transaction.unregisterBean(bean);
}
@Override
public boolean isRegisteredBean(Object bean) {
return transaction.isRegisteredBean(bean);
}
@Override
public String getId() {
return transaction.getId();
}
@Override
public void register(TransactionCallback callback) {
transaction.register(callback);
}
@Override
public boolean isReadOnly() {
return transaction.isReadOnly();
}
@Override
public void setReadOnly(boolean readOnly) {
transaction.setReadOnly(readOnly);
}
@Override
public boolean isActive() {
return transaction.isActive();
}
@Override
public void setPersistCascade(boolean persistCascade) {
transaction.setPersistCascade(persistCascade);
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
}
@Override
public Boolean isUpdateAllLoadedProperties() {
return transaction.isUpdateAllLoadedProperties();
}
@Override
public void setBatchMode(boolean useBatch) {
transaction.setBatchMode(useBatch);
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
}
@Override
public PersistBatch getBatch() {
return transaction.getBatch();
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
transaction.setBatchOnCascade(batchOnCascadeMode);
}
@Override
public PersistBatch getBatchOnCascade() {
return transaction.getBatchOnCascade();
}
@Override
public void setBatchSize(int batchSize) {
transaction.setBatchSize(batchSize);
}
@Override
public int getBatchSize() {
return transaction.getBatchSize();
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
public Boolean getBatchGetGeneratedKeys() {
return transaction.getBatchGetGeneratedKeys();
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
}
@Override
public boolean isBatchFlushOnQuery() {
return transaction.isBatchFlushOnQuery();
}
@Override
public void flush() throws PersistenceException {
transaction.flush();
}
@Override
public void flushBatch() throws PersistenceException {
flush();
}
@Override
public Connection getConnection() {
return transaction.getConnection();
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
transaction.addModification(tableName, inserts, updates, deletes);
}
@Override
public void putUserObject(String name, Object value) {
transaction.putUserObject(name, value);
}
@Override
public Object getUserObject(String name) {
return transaction.getUserObject(name);
}
@Override
public void depth(int diff) {
transaction.depth();
}
@Override
public int depth() {
return transaction.depth();
}
@Override
public boolean isExplicit() {
return transaction.isExplicit();
}
@Override
public TransactionEvent getEvent() {
return transaction.getEvent();
}
@Override
public boolean isPersistCascade() {
return transaction.isPersistCascade();
}
@Override
public boolean isBatchThisRequest(PersistRequest.Type type) {
return transaction.isBatchThisRequest(type);
}
@Override
public BatchControl getBatchControl() {
return transaction.getBatchControl();
}
@Override
public void setBatchControl(BatchControl control) {
transaction.setBatchControl(control);
}
@Override
public PersistenceContext getPersistenceContext() {
return transaction.getPersistenceContext();
}
@Override
public void setPersistenceContext(PersistenceContext context) {
transaction.setPersistenceContext(context);
}
@Override
public Connection getInternalConnection() {
return transaction.getInternalConnection();
}
@Override
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
return transaction.isSaveAssocManyIntersection(intersectionTable, beanName);
}
@Override
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request) {
return transaction.checkBatchEscalationOnCascade(request);
}
@Override
public void flushBatchOnCascade() {
transaction.flushBatchOnCascade();
}
@Override
public void flushBatchOnRollback() {
transaction.flushBatchOnRollback();
}
@Override
public void markNotQueryOnly() {
transaction.markNotQueryOnly();
}
@Override
public void checkBatchEscalationOnCollection() {
transaction.checkBatchEscalationOnCollection();
}
@Override
public void flushBatchOnCollection() {
transaction.flushBatchOnCollection();
public void end() throws PersistenceException {
current.end();
pop();
}
@Override
public void close() {
transaction.close();
end();
}
@Override
public void commit() {
current.commitTransaction();
}
@Override
public void rollback() throws PersistenceException {
current.rollback(null);
}
@Override
public void rollback(Throwable e) throws PersistenceException {
current.rollback(e);
}
@Override
public void setRollbackOnly() {
current.setRollbackOnly();
}
/**
* Return the current transaction.
*/
public SpiTransaction current() {
return transaction;
}
/**
* Rollback for Error.
*/
public Error caughtError(Error e) {
return current.caughtError(e);
}
/**
* Maybe rollback based on TxScope rollback on settings.
*/
public Exception caughtThrowable(Exception e) {
return current.caughtThrowable(e);
}
}
@@ -125,11 +125,6 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
*/
SpiTransaction currentServerTransaction();
/**
* Create a ScopeTrans for a method for the given scope definition.
*/
ScopeTrans createScopeTrans(TxScope txScope);
/**
* Create a ServerTransaction for query purposes.
*
@@ -213,4 +208,15 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* Create DDL handler given the platform and configuration of the server.
*/
DdlHandler createDdlHandler();
/**
* Start an enhanced transactional method.
*/
void scopedTransactionEnter(TxScope txScope);
/**
* Handle the end of an enhanced Transactional method.
*/
void scopedTransactionExit(Object returnOrThrowable, int opCode);
}
@@ -0,0 +1,387 @@
package io.ebeaninternal.api;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.transaction.ProfileStream;
import io.ebeanservice.docstore.api.DocStoreTransaction;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Proxy for an underlying SpiTransaction (most of the API).
*/
abstract class SpiTransactionProxy implements SpiTransaction {
protected SpiTransaction transaction;
@Override
public PersistenceException translate(String message, SQLException cause) {
return transaction.translate(message, cause);
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
}
@Override
public boolean isRollbackOnly() {
return transaction.isRollbackOnly();
}
@Override
public long profileOffset() {
return transaction.profileOffset();
}
@Override
public void profileEvent(SpiProfileTransactionEvent event) {
transaction.profileEvent(event);
}
@Override
public ProfileStream profileStream() {
return transaction.profileStream();
}
@Override
public void setTenantId(Object tenantId) {
transaction.setTenantId(tenantId);
}
@Override
public Object getTenantId() {
return transaction.getTenantId();
}
@Override
public DocStoreTransaction getDocStoreTransaction() {
return transaction.getDocStoreTransaction();
}
@Override
public DocStoreMode getDocStoreMode() {
return transaction.getDocStoreMode();
}
@Override
public void setDocStoreMode(DocStoreMode mode) {
transaction.setDocStoreMode(mode);
}
@Override
public int getDocStoreBatchSize() {
return transaction.getDocStoreBatchSize();
}
@Override
public void setDocStoreBatchSize(int batchSize) {
transaction.setDocStoreBatchSize(batchSize);
}
@Override
public String getLogPrefix() {
return transaction.getLogPrefix();
}
@Override
public boolean isLogSql() {
return transaction.isLogSql();
}
@Override
public boolean isLogSummary() {
return transaction.isLogSummary();
}
@Override
public void logSql(String msg) {
transaction.logSql(msg);
}
@Override
public void logSummary(String msg) {
transaction.logSummary(msg);
}
@Override
public void setSkipCache(boolean skipCache) {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
}
@Override
public void addBeanChange(BeanChange beanChange) {
transaction.addBeanChange(beanChange);
}
@Override
public void sendChangeLog(ChangeSet changes) {
transaction.sendChangeLog(changes);
}
@Override
public void registerDeferred(PersistDeferredRelationship derived) {
transaction.registerDeferred(derived);
}
@Override
public void registerDeleteBean(Integer hash) {
transaction.registerDeleteBean(hash);
}
@Override
public void unregisterDeleteBean(Integer hash) {
transaction.unregisterDeleteBean(hash);
}
@Override
public boolean isRegisteredDeleteBean(Integer hash) {
return transaction.isRegisteredDeleteBean(hash);
}
@Override
public void unregisterBean(Object bean) {
transaction.unregisterBean(bean);
}
@Override
public boolean isRegisteredBean(Object bean) {
return transaction.isRegisteredBean(bean);
}
@Override
public String getId() {
return transaction.getId();
}
@Override
public void register(TransactionCallback callback) {
transaction.register(callback);
}
@Override
public boolean isReadOnly() {
return transaction.isReadOnly();
}
@Override
public void setReadOnly(boolean readOnly) {
transaction.setReadOnly(readOnly);
}
@Override
public boolean isActive() {
return transaction.isActive();
}
@Override
public void setPersistCascade(boolean persistCascade) {
transaction.setPersistCascade(persistCascade);
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
}
@Override
public Boolean isUpdateAllLoadedProperties() {
return transaction.isUpdateAllLoadedProperties();
}
@Override
public void setBatchMode(boolean useBatch) {
transaction.setBatchMode(useBatch);
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
}
@Override
public PersistBatch getBatch() {
return transaction.getBatch();
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
transaction.setBatchOnCascade(batchOnCascadeMode);
}
@Override
public PersistBatch getBatchOnCascade() {
return transaction.getBatchOnCascade();
}
@Override
public void setBatchSize(int batchSize) {
transaction.setBatchSize(batchSize);
}
@Override
public int getBatchSize() {
return transaction.getBatchSize();
}
@Override
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
public Boolean getBatchGetGeneratedKeys() {
return transaction.getBatchGetGeneratedKeys();
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
}
@Override
public void setBatchFlushOnQuery(boolean batchFlushOnQuery) {
transaction.setBatchFlushOnQuery(batchFlushOnQuery);
}
@Override
public boolean isBatchFlushOnQuery() {
return transaction.isBatchFlushOnQuery();
}
@Override
public void flush() throws PersistenceException {
transaction.flush();
}
@Override
public void flushBatch() throws PersistenceException {
flush();
}
@Override
public Connection getConnection() {
return transaction.getConnection();
}
@Override
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
transaction.addModification(tableName, inserts, updates, deletes);
}
@Override
public void putUserObject(String name, Object value) {
transaction.putUserObject(name, value);
}
@Override
public Object getUserObject(String name) {
return transaction.getUserObject(name);
}
@Override
public void depth(int diff) {
transaction.depth();
}
@Override
public int depth() {
return transaction.depth();
}
@Override
public boolean isExplicit() {
return transaction.isExplicit();
}
@Override
public TransactionEvent getEvent() {
return transaction.getEvent();
}
@Override
public boolean isPersistCascade() {
return transaction.isPersistCascade();
}
@Override
public boolean isBatchThisRequest(PersistRequest.Type type) {
return transaction.isBatchThisRequest(type);
}
@Override
public BatchControl getBatchControl() {
return transaction.getBatchControl();
}
@Override
public void setBatchControl(BatchControl control) {
transaction.setBatchControl(control);
}
@Override
public PersistenceContext getPersistenceContext() {
return transaction.getPersistenceContext();
}
@Override
public void setPersistenceContext(PersistenceContext context) {
transaction.setPersistenceContext(context);
}
@Override
public Connection getInternalConnection() {
return transaction.getInternalConnection();
}
@Override
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
return transaction.isSaveAssocManyIntersection(intersectionTable, beanName);
}
@Override
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request) {
return transaction.checkBatchEscalationOnCascade(request);
}
@Override
public void flushBatchOnCascade() {
transaction.flushBatchOnCascade();
}
@Override
public void flushBatchOnRollback() {
transaction.flushBatchOnRollback();
}
@Override
public void markNotQueryOnly() {
transaction.markNotQueryOnly();
}
@Override
public void checkBatchEscalationOnCollection() {
transaction.checkBatchEscalationOnCollection();
}
@Override
public void flushBatchOnCollection() {
transaction.flushBatchOnCollection();
}
}
@@ -659,7 +659,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public Transaction createTransaction() {
return transactionManager.createTransaction(0,true, -1);
return transactionManager.createTransaction(0, true, -1);
}
/**
@@ -671,7 +671,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public Transaction createTransaction(TxIsolation isolation) {
return transactionManager.createTransaction(0,true, isolation.getLevel());
return transactionManager.createTransaction(0, true, isolation.getLevel());
}
@Override
@@ -681,7 +681,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> T executeCall(TxScope scope, Callable<T> c) {
ScopeTrans scopeTrans = createScopeTrans(scope);
ScopedTransaction scopeTrans = scopedTransaction(scope);
try {
return c.call();
@@ -692,7 +692,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
throw new PersistenceException(scopeTrans.caughtThrowable(e));
} finally {
scopeTrans.onFinally();
scopeTrans.complete();
}
}
@@ -703,18 +703,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void execute(TxScope scope, Runnable r) {
ScopeTrans scopeTrans = createScopeTrans(scope);
ScopedTransaction t = scopedTransaction(scope);
try {
r.run();
} catch (Error e) {
throw scopeTrans.caughtError(e);
throw t.caughtError(e);
} catch (Exception e) {
throw new PersistenceException(scopeTrans.caughtThrowable(e));
throw new PersistenceException(t.caughtThrowable(e));
} finally {
scopeTrans.onFinally();
t.complete();
}
}
@@ -758,51 +758,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public ScopeTrans createScopeTrans(TxScope txScope) {
public void scopedTransactionEnter(TxScope txScope) {
beginTransaction(txScope);
}
if (txScope == null) {
// create a TxScope with default settings
txScope = new TxScope();
} else {
// check for implied batch mode via setting batchSize
txScope.checkBatchMode();
}
SpiTransaction suspended = null;
// get current transaction from ThreadLocal or equivalent
SpiTransaction t = transactionScopeManager.get();
boolean newTransaction;
if (txScope.getType().equals(TxType.NOT_SUPPORTED)) {
// Suspend existing transaction and
// run without a transaction in scope
newTransaction = false;
suspended = t;
t = null;
transactionScopeManager.replace(null);
} else {
// create a new Transaction based on TxType and t
newTransaction = createNewTransaction(t, txScope);
if (newTransaction) {
// suspend existing transaction (if there is one)
suspended = t;
// create a new transaction
int isoLevel = -1;
TxIsolation isolation = txScope.getIsolation();
if (isolation != null) {
isoLevel = isolation.getLevel();
}
t = transactionManager.createTransaction(txScope.getProfileId(), true, isoLevel);
// note ScopeTrans.onFinally() restores the suspended transaction
transactionScopeManager.replace(t);
}
}
return new ScopeTrans(rollbackOnChecked, newTransaction, t, txScope, suspended, transactionScopeManager);
@Override
public void scopedTransactionExit(Object returnOrThrowable, int opCode) {
ScopedTransaction st = (ScopedTransaction) transactionScopeManager.getScoped();
st.complete(returnOrThrowable, opCode);
}
/**
@@ -828,9 +791,52 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public Transaction beginTransaction(TxScope scope) {
ScopeTrans scopeTrans = createScopeTrans(scope);
return new ScopedTransaction(scopeTrans);
public Transaction beginTransaction(TxScope txScope) {
return scopedTransaction(txScope);
}
/**
* Create a Scoped transaction which internally can 'nest' transactions on it's own stack.
*/
ScopedTransaction scopedTransaction(TxScope txScope) {
txScope = initTxScope(txScope);
boolean setToScope = false;
ScopedTransaction txnContainer = (ScopedTransaction) transactionScopeManager.get();
if (txnContainer == null) {
setToScope = true;
txnContainer = new ScopedTransaction(transactionScopeManager);
}
SpiTransaction transaction = txnContainer.current();
boolean createTransaction;
if (txScope.getType() == TxType.NOT_SUPPORTED) {
createTransaction = false;
transaction = null;
} else {
createTransaction = createNewTransaction(transaction, txScope);
if (createTransaction) {
transaction = transactionManager.createTransaction(txScope.getProfileId(), true, txScope.getIsolationLevel());
}
}
txnContainer.push(new ScopeTrans(rollbackOnChecked, createTransaction, transaction, txScope));
if (setToScope) {
transactionScopeManager.set(txnContainer);
}
return txnContainer;
}
private TxScope initTxScope(TxScope txScope) {
if (txScope == null) {
return new TxScope();
} else {
// check for implied batch mode via setting batchSize
txScope.checkBatchMode();
return txScope;
}
}
/**
@@ -842,7 +848,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public Transaction beginTransaction(TxIsolation isolation) {
// start an explicit transaction
SpiTransaction t = transactionManager.createTransaction(0,true, isolation.getLevel());
SpiTransaction t = transactionManager.createTransaction(0, true, isolation.getLevel());
try {
transactionScopeManager.set(t);
} catch (PersistenceException existingTransactionError) {
@@ -1717,7 +1723,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> List<T> draftRestore(Query<T> query, Transaction transaction) {
return executeInTrans((txn)-> persister.draftRestore(query, txn), transaction);
return executeInTrans((txn) -> persister.draftRestore(query, txn), transaction);
}
@Override
@@ -2063,7 +2069,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
transactionManager.remoteTransactionEvent(event);
}
private <P> P executeInTrans(Function<SpiTransaction,P> fun, Transaction t) {
private <P> P executeInTrans(Function<SpiTransaction, P> fun, Transaction t) {
ObtainedTransaction wrap = initTransIfRequired(t);
try {
P result = fun.apply(wrap.transaction());
@@ -2094,7 +2100,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public SpiTransaction beginServerTransaction() {
SpiTransaction t = transactionManager.createTransaction(0,false, -1);
SpiTransaction t = transactionManager.createTransaction(0, false, -1);
transactionScopeManager.set(t);
return t;
}
@@ -22,6 +22,11 @@ public class DefaultTransactionScopeManager extends TransactionScopeManager {
DefaultTransactionThreadLocal.end(serverName);
}
@Override
public SpiTransaction getScoped() {
return DefaultTransactionThreadLocal.get(serverName);
}
@Override
public SpiTransaction get() {
SpiTransaction t = DefaultTransactionThreadLocal.get(serverName);
@@ -32,8 +32,12 @@ public class ExternalTransactionScopeManager extends TransactionScopeManager {
}
@Override
public SpiTransaction get() {
public SpiTransaction getScoped() {
return get();
}
@Override
public SpiTransaction get() {
return (SpiTransaction) externalManager.getCurrentTransaction();
}
@@ -17,6 +17,11 @@ public abstract class TransactionScopeManager implements SpiTransactionScopeMana
this.serverName = transactionManager.getServerName();
}
/**
* Return the current Transaction allowing it to be inactive.
*/
public abstract SpiTransaction getScoped();
/**
* Return the current Transaction for this serverName and Thread.
*/