Refactor DefaultTransactionThreadLocal, convert ThreadLocal to field (from map) (#1716)

* Refactor DefaultTransactionThreadLocal, convert ThreadLocal to field (from map)

* Add test for multiple database thread locals
This commit is contained in:
Rob Bygrave
2019-05-18 15:16:17 +12:00
committed by GitHub
parent 201958f38c
commit c6ba4d8155
9 changed files with 120 additions and 141 deletions
+10 -9
View File
@@ -15,8 +15,7 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory;
import io.ebeaninternal.server.transaction.DefaultTransactionThreadLocal;
import io.ebeaninternal.server.transaction.TransactionScopeManager;
import org.avaje.agentloader.AgentLoader;
import org.junit.After;
import org.junit.Rule;
@@ -28,7 +27,6 @@ import org.tests.model.basic.Country;
import java.sql.Types;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
@@ -43,13 +41,12 @@ public abstract class BaseTestCase {
@After
public void checkForLeak() {
Map<String, SpiTransaction> trans = DefaultTransactionThreadLocal.currentTransactions();
TransactionScopeManager scope = spiEbeanServer().getTransactionManager().scope();
SpiTransaction trans = scope.getInScope();
if (trans != null) {
if (!trans.isEmpty()) {
String msg = getClass().getSimpleName() + "." + name.getMethodName() + " did not clear threadScope:" + trans;
trans.clear(); // clear for next test
fail(msg);
}
String msg = getClass().getSimpleName() + "." + name.getMethodName() + " did not clear threadScope:" + trans;
scope.clearExternal(); // clear for next test
fail(msg);
}
}
@@ -104,6 +101,10 @@ public abstract class BaseTestCase {
.collect(Collectors.toList());
}
protected SpiTransaction getInScopeTransaction() {
return spiEbeanServer().getTransactionManager().scope().getInScope();
}
/**
* Return the generated sql trimming column alias if required.
*/
@@ -1,13 +1,21 @@
package io.ebeaninternal.server.transaction;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.Ebean;
import io.ebean.Transaction;
import io.ebean.annotation.ForPlatform;
import io.ebean.annotation.Platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.api.SpiTransaction;
import org.junit.Test;
import org.tests.model.basic.EBasicVer;
import org.tests.model.basic.UTDetail;
import org.tests.model.basic.UTMaster;
import static org.assertj.core.api.StrictAssertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -18,12 +26,12 @@ public class DefaultTransactionThreadLocalTest extends BaseTestCase {
@Test
public void get() {
Ebean.execute(() -> {
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
SpiTransaction txn = getInScopeTransaction();
assertNotNull(txn);
});
// thread local should be set to null
assertNull(DefaultTransactionThreadLocal.get("h2"));
assertNull(getInScopeTransaction());
}
@ForPlatform({Platform.H2})
@@ -32,19 +40,19 @@ public class DefaultTransactionThreadLocalTest extends BaseTestCase {
try (Transaction transaction = Ebean.beginTransaction()) {
assertNotNull(transaction);
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
SpiTransaction txn = getInScopeTransaction();
assertSame(txn, transaction);
try (Transaction nested = Ebean.beginTransaction()) {
assertNotNull(nested);
SpiTransaction txnNested = DefaultTransactionThreadLocal.get("h2");
SpiTransaction txnNested = getInScopeTransaction();
assertSame(txnNested, nested);
}
assertNotNull(DefaultTransactionThreadLocal.get("h2"));
assertNotNull(getInScopeTransaction());
}
assertNull(DefaultTransactionThreadLocal.get("h2"));
assertNull(getInScopeTransaction());
}
@ForPlatform({Platform.H2})
@@ -53,9 +61,9 @@ public class DefaultTransactionThreadLocalTest extends BaseTestCase {
try (Transaction transaction = Ebean.beginTransaction()) {
transaction.commit();
assertNull(DefaultTransactionThreadLocal.get("h2"));
assertNull(getInScopeTransaction());
}
assertNull(DefaultTransactionThreadLocal.get("h2"));
assertNull(getInScopeTransaction());
}
@ForPlatform({Platform.H2})
@@ -65,9 +73,9 @@ public class DefaultTransactionThreadLocalTest extends BaseTestCase {
try (Transaction transaction = Ebean.beginTransaction()) {
transaction.rollback();
assertNull(DefaultTransactionThreadLocal.get("h2"));
assertNull(getInScopeTransaction());
}
assertNull(DefaultTransactionThreadLocal.get("h2"));
assertNull(getInScopeTransaction());
}
@ForPlatform({Platform.H2})
@@ -78,4 +86,39 @@ public class DefaultTransactionThreadLocalTest extends BaseTestCase {
Ebean.endTransaction();
}
@ForPlatform({Platform.H2})
@Test
public void multi_database_threadlocals() {
Database db = DB.getDefault();
Database otherDb = createOtherDatabase();
try {
try (Transaction dbTxn = db.beginTransaction()) {
try (Transaction otherDbTxn = otherDb.beginTransaction()) {
assertThat(dbTxn).isNotSameAs(otherDbTxn);
}
}
} finally {
otherDb.shutdown(true, false);
}
}
private Database createOtherDatabase() {
DatabaseConfig config = new DatabaseConfig();
config.setName("h2ebasicver");
config.loadFromProperties();
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setDdlExtra(false);
config.setRegister(false);
config.setDefaultServer(false);
config.getClasses().add(EBasicVer.class);
config.getClasses().add(UTMaster.class);
config.getClasses().add(UTDetail.class);
return DatabaseFactory.create(config);
}
}
@@ -10,7 +10,6 @@ import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.annotation.Transactional;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.transaction.DefaultTransactionThreadLocal;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
@@ -37,8 +36,7 @@ public class TestExecuteComplete extends BaseTestCase {
fail();
} catch (DataIntegrityException e) {
// assert the thread local has been cleaned up
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
assertThat(txn).isNull();
assertThat(getInScopeTransaction()).isNull();
}
}
@@ -59,8 +57,7 @@ public class TestExecuteComplete extends BaseTestCase {
fail();
} catch (DataIntegrityException e) {
// assert the thread local has been cleaned up
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
assertThat(txn).isNull();
assertThat(getInScopeTransaction()).isNull();
}
}
@@ -72,8 +69,7 @@ public class TestExecuteComplete extends BaseTestCase {
errorOnCommit();
fail();
} catch (DataIntegrityException e) {
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
assertThat(txn).isNull();
assertThat(getInScopeTransaction()).isNull();
}
}
@@ -97,8 +93,7 @@ public class TestExecuteComplete extends BaseTestCase {
txn1.end();
}
SpiTransaction txn2 = DefaultTransactionThreadLocal.get("h2");
assertThat(txn2).isNull();
assertThat(getInScopeTransaction()).isNull();
}
@ForPlatform(Platform.H2)
@@ -113,8 +108,7 @@ public class TestExecuteComplete extends BaseTestCase {
//txn1.end();
}
SpiTransaction txn2 = DefaultTransactionThreadLocal.get("h2");
assertThat(txn2).isNull();
assertThat(getInScopeTransaction()).isNull();
}
@ForPlatform(Platform.H2)
@@ -129,8 +123,7 @@ public class TestExecuteComplete extends BaseTestCase {
//txn1.end();
}
SpiTransaction txn2 = DefaultTransactionThreadLocal.get("h2");
assertThat(txn2).isNull();
assertThat(getInScopeTransaction()).isNull();
}
@ForPlatform(Platform.H2)
@@ -139,8 +132,7 @@ public class TestExecuteComplete extends BaseTestCase {
DB.find(Customer.class).findList();
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
assertThat(txn).isNull();
assertThat(getInScopeTransaction()).isNull();
}
@ForPlatform(Platform.H2)
@@ -151,8 +143,7 @@ public class TestExecuteComplete extends BaseTestCase {
cust.setName("Roland");
DB.save(cust);
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
assertThat(txn).isNull();
assertThat(getInScopeTransaction()).isNull();
}
@ForPlatform(Platform.H2)
@@ -160,15 +151,14 @@ public class TestExecuteComplete extends BaseTestCase {
public void no_transaction_expect_threadScopeCleanup() {
try (Transaction txn = DB.beginTransaction(TxScope.notSupported())) {
SpiTransaction txn2 = DefaultTransactionThreadLocal.get("h2");
SpiTransaction txn2 = getInScopeTransaction();
// The NoTransaction placeholder can normally only occur inside
// a scopedTrans. (Class is package private, so check
assertThat(txn2.toString()).contains("NoTransaction");
assertThat(txn2.toString()).contains("NoTransaction");
}
SpiTransaction txn = DefaultTransactionThreadLocal.get("h2");
assertThat(txn).isNull();
assertThat(getInScopeTransaction()).isNull();
}
}