From b682293a32f038fefeb9f90ec070d79754dec00a Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 31 Jan 2018 11:56:38 +1300 Subject: [PATCH] #1245 - Refactor internals - move logic to TransactionManager from DefaultServer --- .../server/core/DefaultServer.java | 124 +---------------- .../{core => transaction}/NoTransaction.java | 6 +- .../transaction/TransactionManager.java | 126 +++++++++++++++++- 3 files changed, 133 insertions(+), 123 deletions(-) rename src/main/java/io/ebeaninternal/server/{core => transaction}/NoTransaction.java (96%) diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index db8da4d7c..bd5c42eab 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -26,7 +26,6 @@ import io.ebean.UpdateQuery; import io.ebean.ValuePair; import io.ebean.Version; import io.ebean.annotation.TxIsolation; -import io.ebean.annotation.TxType; import io.ebean.bean.BeanCollection; import io.ebean.bean.CallStack; import io.ebean.bean.EntityBean; @@ -55,7 +54,6 @@ import io.ebean.text.csv.CsvReader; import io.ebean.text.json.JsonContext; import io.ebeaninternal.api.LoadBeanRequest; import io.ebeaninternal.api.LoadManyRequest; -import io.ebeaninternal.api.ScopeTrans; import io.ebeaninternal.api.ScopedTransaction; import io.ebeaninternal.api.SpiBackgroundExecutor; import io.ebeaninternal.api.SpiEbeanServer; @@ -135,12 +133,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private final CallStackFactory callStackFactory; - /** - * Ebean defaults this to true but for EJB compatible behaviour set this to - * false; - */ - private final boolean rollbackOnChecked; - /** * Handles the save, delete, updateSql CallableSql. */ @@ -248,8 +240,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode(); this.callStackFactory = initCallStackFactory(serverConfig); - this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked(); - this.persister = config.createPersister(this); this.queryEngine = config.createOrmQueryEngine(); this.relationalQueryEngine = config.createRelationalQueryEngine(); @@ -689,7 +679,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override public T executeCall(TxScope scope, Callable c) { - ScopedTransaction scopeTrans = scopedTransaction(scope); + ScopedTransaction scopeTrans = transactionManager.beginScopedTransaction(scope); try { return c.call(); @@ -711,7 +701,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override public void execute(TxScope scope, Runnable r) { - ScopedTransaction t = scopedTransaction(scope); + ScopedTransaction t = transactionManager.beginScopedTransaction(scope); try { r.run(); @@ -726,43 +716,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } } - /** - * Determine whether to create a new transaction or not. - *

- * This will also potentially throw exceptions for MANDATORY and NEVER types. - *

- */ - private boolean createNewTransaction(SpiTransaction current, TxType type) { - switch (type) { - case REQUIRED: - return current == null; - - case REQUIRES_NEW: - return true; - - case MANDATORY: - if (current == null) { - throw new PersistenceException("Transaction missing when MANDATORY"); - } - return false; - - case SUPPORTS: - return current == null; - - case NEVER: - if (current != null) { - throw new PersistenceException("Transaction exists for Transactional NEVER"); - } - return true; // always use NoTransaction instance - - case NOT_SUPPORTED: - return true; // always use NoTransaction instance - - default: - throw new RuntimeException("Should never get here?"); - } - } - @Override public void scopedTransactionEnter(TxScope txScope) { beginTransaction(txScope); @@ -770,12 +723,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override public void scopedTransactionExit(Object returnOrThrowable, int opCode) { - ScopedTransaction st = transactionManager.getMaybeInactive(); - if (st != null) { - // can be null for Supports as that can start as a 'No Transaction' and then - // effectively be replaced by transactions inside the scope - st.complete(returnOrThrowable, opCode); - } + transactionManager.exitScopedTransaction(returnOrThrowable, opCode); } /** @@ -802,71 +750,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override 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 = transactionManager.getScoped(); - if (txnContainer == null) { - setToScope = true; - txnContainer = transactionManager.createScopedTransaction(); - } - - SpiTransaction transaction = txnContainer.current(); - - TxType type = txScope.getType(); - boolean createTransaction = createNewTransaction(transaction, type); - if (createTransaction) { - switch (type) { - case SUPPORTS: - case NOT_SUPPORTED: - case NEVER: - transaction = NoTransaction.INSTANCE; - break; - default: - transaction = transactionManager.createTransaction(txScope.getProfileId(), true, txScope.getIsolationLevel()); - initNewTransaction(transaction, txScope); - } - } - - txnContainer.push(new ScopeTrans(rollbackOnChecked, createTransaction, transaction, txScope)); - if (setToScope) { - transactionManager.set(txnContainer); - } - return txnContainer; - } - - private void initNewTransaction(SpiTransaction transaction, TxScope txScope) { - - if (txScope.isSkipCache()) { - transaction.setSkipCache(true); - } - String label = txScope.getLabel(); - if (label != null) { - transaction.setLabel(label); - } - ProfileLocation profileLocation = txScope.getProfileLocation(); - if (profileLocation != null) { - profileLocation.obtain(); - transaction.setProfileLocation(profileLocation); - } - } - - private TxScope initTxScope(TxScope txScope) { - if (txScope == null) { - return new TxScope(); - } else { - // check for implied batch mode via setting batchSize - txScope.checkBatchMode(); - return txScope; - } + return transactionManager.beginScopedTransaction(txScope); } /** diff --git a/src/main/java/io/ebeaninternal/server/core/NoTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java similarity index 96% rename from src/main/java/io/ebeaninternal/server/core/NoTransaction.java rename to src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java index 97c480dbf..5a2e202de 100644 --- a/src/main/java/io/ebeaninternal/server/core/NoTransaction.java +++ b/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java @@ -1,4 +1,4 @@ -package io.ebeaninternal.server.core; +package io.ebeaninternal.server.transaction; import io.ebean.ProfileLocation; import io.ebean.TransactionCallback; @@ -10,8 +10,10 @@ import io.ebean.event.changelog.ChangeSet; import io.ebeaninternal.api.SpiProfileTransactionEvent; import io.ebeaninternal.api.SpiTransaction; import io.ebeaninternal.api.TransactionEvent; +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; diff --git a/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java index 22c541576..54d847089 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java @@ -1,7 +1,10 @@ package io.ebeaninternal.server.transaction; import io.ebean.BackgroundExecutor; +import io.ebean.ProfileLocation; +import io.ebean.TxScope; import io.ebean.annotation.PersistBatch; +import io.ebean.annotation.TxType; import io.ebean.config.CurrentTenantProvider; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly; @@ -9,6 +12,7 @@ import io.ebean.event.changelog.ChangeLogListener; import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeSet; import io.ebean.meta.MetaTimedMetric; +import io.ebeaninternal.api.ScopeTrans; import io.ebeaninternal.api.ScopedTransaction; import io.ebeaninternal.api.SpiProfileHandler; import io.ebeaninternal.api.SpiTransaction; @@ -56,6 +60,12 @@ public class TransactionManager { protected final BeanDescriptorManager beanDescriptorManager; + /** + * Ebean defaults this to true but for EJB compatible behaviour set this to + * false; + */ + private final boolean rollbackOnChecked; + /** * Prefix for transaction id's (logging). */ @@ -137,6 +147,7 @@ public class TransactionManager { this.localL2Caching = options.localL2Caching; this.persistBatch = options.config.getPersistBatch(); this.persistBatchOnCascade = options.config.appliedPersistBatchOnCascade(); + this.rollbackOnChecked = options.config.isTransactionRollbackOnChecked(); this.beanDescriptorManager = options.descMgr; this.viewInvalidation = options.descMgr.requiresViewEntityCacheInvalidation(); this.changeLogPrepare = options.descMgr.getChangeLogPrepare(); @@ -203,7 +214,7 @@ public class TransactionManager { /** * Return the current scoped transaction allowing it to be inactive (already committed or rolled back). */ - public ScopedTransaction getMaybeInactive() { + private ScopedTransaction getMaybeInactive() { return (ScopedTransaction)scopeManager.getMaybeInactive(); } @@ -534,4 +545,117 @@ public class TransactionManager { scopeManager.set(t); return t; } + + /** + * Exit a scoped transaction (that can be inactive - already committed etc). + */ + public void exitScopedTransaction(Object returnOrThrowable, int opCode) { + ScopedTransaction st = getMaybeInactive(); + if (st != null) { + // can be null for Supports as that can start as a 'No Transaction' and then + // effectively be replaced by transactions inside the scope + st.complete(returnOrThrowable, opCode); + } + } + + /** + * Begin a scoped transaction. + */ + public ScopedTransaction beginScopedTransaction(TxScope txScope) { + + txScope = initTxScope(txScope); + + boolean setToScope = false; + ScopedTransaction txnContainer = getScoped(); + if (txnContainer == null) { + setToScope = true; + txnContainer = createScopedTransaction(); + } + + SpiTransaction transaction = txnContainer.current(); + + TxType type = txScope.getType(); + boolean createTransaction = isCreateNewTransaction(transaction, type); + if (createTransaction) { + switch (type) { + case SUPPORTS: + case NOT_SUPPORTED: + case NEVER: + transaction = NoTransaction.INSTANCE; + break; + default: + transaction = createTransaction(txScope.getProfileId(), true, txScope.getIsolationLevel()); + initNewTransaction(transaction, txScope); + } + } + + txnContainer.push(new ScopeTrans(rollbackOnChecked, createTransaction, transaction, txScope)); + if (setToScope) { + set(txnContainer); + } + return txnContainer; + } + + private void initNewTransaction(SpiTransaction transaction, TxScope txScope) { + + if (txScope.isSkipCache()) { + transaction.setSkipCache(true); + } + String label = txScope.getLabel(); + if (label != null) { + transaction.setLabel(label); + } + ProfileLocation profileLocation = txScope.getProfileLocation(); + if (profileLocation != null) { + profileLocation.obtain(); + transaction.setProfileLocation(profileLocation); + } + } + + private TxScope initTxScope(TxScope txScope) { + if (txScope == null) { + return new TxScope(); + } else { + // check for implied batch mode via setting batchSize + txScope.checkBatchMode(); + return txScope; + } + } + + /** + * Determine whether to create a new transaction or not. + *

+ * This will also potentially throw exceptions for MANDATORY and NEVER types. + *

+ */ + private boolean isCreateNewTransaction(SpiTransaction current, TxType type) { + switch (type) { + case REQUIRED: + return current == null; + + case REQUIRES_NEW: + return true; + + case MANDATORY: + if (current == null) { + throw new PersistenceException("Transaction missing when MANDATORY"); + } + return false; + + case SUPPORTS: + return current == null; + + case NEVER: + if (current != null) { + throw new PersistenceException("Transaction exists for Transactional NEVER"); + } + return true; // always use NoTransaction instance + + case NOT_SUPPORTED: + return true; // always use NoTransaction instance + + default: + throw new RuntimeException("Should never get here?"); + } + } }