diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java
index fadc44283fd..7f9a013c621 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy.java
@@ -76,6 +76,8 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
+ private boolean lazyTransactionalConnections = true;
+
private boolean reobtainTransactionalConnections = false;
@@ -94,6 +96,18 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
super(targetDataSource);
}
+
+ /**
+ * Specify whether to obtain the transactional target Connection lazily on
+ * actual data access.
+ *
The default is "true". Specify "false" to immediately obtain a target
+ * Connection when a transaction-aware Connection handle is retrieved.
+ * @since 6.1.2
+ */
+ public void setLazyTransactionalConnections(boolean lazyTransactionalConnections) {
+ this.lazyTransactionalConnections = lazyTransactionalConnections;
+ }
+
/**
* Specify whether to reobtain the target Connection for each operation
* performed within a transaction.
@@ -119,7 +133,12 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
*/
@Override
public Connection getConnection() throws SQLException {
- return getTransactionAwareConnectionProxy(obtainTargetDataSource());
+ DataSource ds = obtainTargetDataSource();
+ Connection con = getTransactionAwareConnectionProxy(ds);
+ if (!this.lazyTransactionalConnections && shouldObtainFixedConnection(ds)) {
+ ((ConnectionProxy) con).getTargetConnection();
+ }
+ return con;
}
/**
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceJtaTransactionTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceJtaTransactionTests.java
index a50c8610c6c..68a2aea470d 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceJtaTransactionTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceJtaTransactionTests.java
@@ -73,6 +73,7 @@ public class DataSourceJtaTransactionTests {
private Transaction transaction = mock();
+
@BeforeEach
public void setup() throws Exception {
given(dataSource.getConnection()).willReturn(connection);
@@ -88,6 +89,7 @@ public class DataSourceJtaTransactionTests {
assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse();
}
+
@Test
public void testJtaTransactionCommit() throws Exception {
doTestJtaTransaction(false);
@@ -110,26 +112,23 @@ public class DataSourceJtaTransactionTests {
JtaTransactionManager ptm = new JtaTransactionManager(userTransaction);
TransactionTemplate tt = new TransactionTemplate(ptm);
- boolean condition3 = !TransactionSynchronizationManager.hasResource(dataSource);
- assertThat(condition3).as("Hasn't thread connection").isTrue();
- boolean condition2 = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition2).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- boolean condition = !TransactionSynchronizationManager.hasResource(dataSource);
- assertThat(condition).as("Hasn't thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("JTA synchronizations active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
- Connection c = DataSourceUtils.getConnection(dataSource);
- assertThat(TransactionSynchronizationManager.hasResource(dataSource)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dataSource);
+ Connection con = DataSourceUtils.getConnection(dataSource);
+ assertThat(TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ DataSourceUtils.releaseConnection(con, dataSource);
- c = DataSourceUtils.getConnection(dataSource);
- assertThat(TransactionSynchronizationManager.hasResource(dataSource)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dataSource);
+ con = DataSourceUtils.getConnection(dataSource);
+ assertThat(TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ DataSourceUtils.releaseConnection(con, dataSource);
if (rollback) {
status.setRollbackOnly();
@@ -137,10 +136,8 @@ public class DataSourceJtaTransactionTests {
}
});
- boolean condition1 = !TransactionSynchronizationManager.hasResource(dataSource);
- assertThat(condition1).as("Hasn't thread connection").isTrue();
- boolean condition = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
verify(userTransaction).begin();
if (rollback) {
verify(userTransaction).rollback();
@@ -220,29 +217,26 @@ public class DataSourceJtaTransactionTests {
JtaTransactionManager ptm = new JtaTransactionManager(userTransaction, transactionManager);
final TransactionTemplate tt = new TransactionTemplate(ptm);
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- boolean condition3 = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition3).as("Hasn't thread connection").isTrue();
- boolean condition2 = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition2).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- boolean condition = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition).as("Hasn't thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("JTA synchronizations active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
- Connection c = DataSourceUtils.getConnection(dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
try {
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- c.isReadOnly();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ con.isReadOnly();
+ DataSourceUtils.releaseConnection(con, dsToUse);
- c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
+ con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
if (!openOuterConnection) {
- DataSourceUtils.releaseConnection(c, dsToUse);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
}
catch (SQLException ex) {
@@ -253,20 +247,19 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- boolean condition = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition).as("Hasn't thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("JTA synchronizations active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
try {
- Connection c = DataSourceUtils.getConnection(dsToUse);
- c.isReadOnly();
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
+ con.isReadOnly();
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ DataSourceUtils.releaseConnection(con, dsToUse);
- c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
catch (SQLException ex) {
}
@@ -282,15 +275,15 @@ public class DataSourceJtaTransactionTests {
if (accessAfterResume) {
try {
if (!openOuterConnection) {
- c = DataSourceUtils.getConnection(dsToUse);
+ con = DataSourceUtils.getConnection(dsToUse);
}
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- c.isReadOnly();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ con.isReadOnly();
+ DataSourceUtils.releaseConnection(con, dsToUse);
- c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
catch (SQLException ex) {
}
@@ -298,16 +291,14 @@ public class DataSourceJtaTransactionTests {
else {
if (openOuterConnection) {
- DataSourceUtils.releaseConnection(c, dsToUse);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
}
}
});
- boolean condition1 = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition1).as("Hasn't thread connection").isTrue();
- boolean condition = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
verify(userTransaction, times(6)).begin();
verify(transactionManager, times(5)).resume(transaction);
if (rollback) {
@@ -480,31 +471,28 @@ public class DataSourceJtaTransactionTests {
JtaTransactionManager ptm = new JtaTransactionManager(userTransaction, transactionManager);
final TransactionTemplate tt = new TransactionTemplate(ptm);
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- boolean condition3 = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition3).as("Hasn't thread connection").isTrue();
- boolean condition2 = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition2).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
assertThatExceptionOfType(TransactionException.class).isThrownBy(() ->
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- boolean condition = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition).as("Hasn't thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("JTA synchronizations active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
- Connection c = DataSourceUtils.getConnection(dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
try {
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- c.isReadOnly();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ con.isReadOnly();
+ DataSourceUtils.releaseConnection(con, dsToUse);
- c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
+ con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
if (!openOuterConnection) {
- DataSourceUtils.releaseConnection(c, dsToUse);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
}
catch (SQLException ex) {
@@ -514,26 +502,25 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- boolean condition = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition).as("Hasn't thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("JTA synchronizations active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
- Connection c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ DataSourceUtils.releaseConnection(con, dsToUse);
- c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
});
}
finally {
if (openOuterConnection) {
try {
- c.isReadOnly();
- DataSourceUtils.releaseConnection(c, dsToUse);
+ con.isReadOnly();
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
catch (SQLException ex) {
}
@@ -542,10 +529,8 @@ public class DataSourceJtaTransactionTests {
}
}));
- boolean condition1 = !TransactionSynchronizationManager.hasResource(dsToUse);
- assertThat(condition1).as("Hasn't thread connection").isTrue();
- boolean condition = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
verify(userTransaction).begin();
if (suspendException) {
@@ -586,10 +571,8 @@ public class DataSourceJtaTransactionTests {
}
};
TransactionTemplate tt = new TransactionTemplate(ptm);
- boolean condition2 = !TransactionSynchronizationManager.hasResource(dataSource);
- assertThat(condition2).as("Hasn't thread connection").isTrue();
- boolean condition1 = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition1).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
given(userTransaction.getStatus()).willReturn(Status.STATUS_ACTIVE);
for (int i = 0; i < 3; i++) {
@@ -598,31 +581,28 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("JTA synchronizations active").isTrue();
- boolean condition = !status.isNewTransaction();
- assertThat(condition).as("Is existing transaction").isTrue();
+ assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
+ assertThat(!status.isNewTransaction()).isTrue();
- Connection c = DataSourceUtils.getConnection(dataSource);
- assertThat(TransactionSynchronizationManager.hasResource(dataSource)).as("Has thread connection").isTrue();
- DataSourceUtils.releaseConnection(c, dataSource);
+ Connection con = DataSourceUtils.getConnection(dataSource);
+ assertThat(TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
+ DataSourceUtils.releaseConnection(con, dataSource);
- c = DataSourceUtils.getConnection(dataSource);
- assertThat(TransactionSynchronizationManager.hasResource(dataSource)).as("Has thread connection").isTrue();
+ con = DataSourceUtils.getConnection(dataSource);
+ assertThat(TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
if (releaseCon) {
- DataSourceUtils.releaseConnection(c, dataSource);
+ DataSourceUtils.releaseConnection(con, dataSource);
}
}
});
if (!releaseCon) {
- assertThat(TransactionSynchronizationManager.hasResource(dataSource)).as("Still has connection holder").isTrue();
+ assertThat(TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
}
else {
- boolean condition = !TransactionSynchronizationManager.hasResource(dataSource);
- assertThat(condition).as("Hasn't thread connection").isTrue();
+ assertThat(!TransactionSynchronizationManager.hasResource(dataSource)).isTrue();
}
- boolean condition = !TransactionSynchronizationManager.isSynchronizationActive();
- assertThat(condition).as("JTA synchronizations not active").isTrue();
+ assertThat(!TransactionSynchronizationManager.isSynchronizationActive()).isTrue();
}
verify(connection, times(3)).close();
}
@@ -648,10 +628,10 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- Connection c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- assertThat(c).isSameAs(connection);
- DataSourceUtils.releaseConnection(c, dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(con).isSameAs(connection);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
});
@@ -660,10 +640,10 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- Connection c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- assertThat(c).isSameAs(connection);
- DataSourceUtils.releaseConnection(c, dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(con).isSameAs(connection);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
});
@@ -720,10 +700,10 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- Connection c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- assertThat(c).isSameAs(connection1);
- DataSourceUtils.releaseConnection(c, dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(con).isSameAs(connection1);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
});
@@ -731,10 +711,10 @@ public class DataSourceJtaTransactionTests {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- Connection c = DataSourceUtils.getConnection(dsToUse);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- assertThat(c).isSameAs(connection2);
- DataSourceUtils.releaseConnection(c, dsToUse);
+ Connection con = DataSourceUtils.getConnection(dsToUse);
+ assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isTrue();
+ assertThat(con).isSameAs(connection2);
+ DataSourceUtils.releaseConnection(con, dsToUse);
}
});
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java
index 7a52e160059..938dadaa3ac 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java
@@ -67,20 +67,25 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
* @since 04.07.2003
* @see org.springframework.jdbc.support.JdbcTransactionManagerTests
*/
-public class DataSourceTransactionManagerTests {
+public class DataSourceTransactionManagerTests {
- private DataSource ds = mock();
+ protected DataSource ds = mock();
- private Connection con = mock();
+ protected Connection con = mock();
- private DataSourceTransactionManager tm = new DataSourceTransactionManager(ds);
+ protected DataSourceTransactionManager tm;
@BeforeEach
public void setup() throws Exception {
+ tm = createTransactionManager(ds);
given(ds.getConnection()).willReturn(con);
}
+ protected DataSourceTransactionManager createTransactionManager(DataSource ds) {
+ return new DataSourceTransactionManager(ds);
+ }
+
@AfterEach
public void verifyTransactionSynchronizationManagerState() {
assertThat(TransactionSynchronizationManager.getResourceMap()).isEmpty();
@@ -123,18 +128,15 @@ public class DataSourceTransactionManagerTests {
private void doTestTransactionCommitRestoringAutoCommit(
boolean autoCommit, boolean lazyConnection, final boolean createStatement) throws Exception {
+ given(con.getAutoCommit()).willReturn(autoCommit);
+
if (lazyConnection) {
- given(con.getAutoCommit()).willReturn(autoCommit);
given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
given(con.getWarnings()).willThrow(new SQLException());
}
- if (!lazyConnection || createStatement) {
- given(con.getAutoCommit()).willReturn(autoCommit);
- }
-
final DataSource dsToUse = (lazyConnection ? new LazyConnectionDataSourceProxy(ds) : ds);
- tm = new DataSourceTransactionManager(dsToUse);
+ tm = createTransactionManager(dsToUse);
TransactionTemplate tt = new TransactionTemplate(tm);
assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isFalse();
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
@@ -213,17 +215,14 @@ public class DataSourceTransactionManagerTests {
private void doTestTransactionRollbackRestoringAutoCommit(
boolean autoCommit, boolean lazyConnection, final boolean createStatement) throws Exception {
+ given(con.getAutoCommit()).willReturn(autoCommit);
+
if (lazyConnection) {
- given(con.getAutoCommit()).willReturn(autoCommit);
given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
}
- if (!lazyConnection || createStatement) {
- given(con.getAutoCommit()).willReturn(autoCommit);
- }
-
final DataSource dsToUse = (lazyConnection ? new LazyConnectionDataSourceProxy(ds) : ds);
- tm = new DataSourceTransactionManager(dsToUse);
+ tm = createTransactionManager(dsToUse);
TransactionTemplate tt = new TransactionTemplate(tm);
assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).isFalse();
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
@@ -522,7 +521,7 @@ public class DataSourceTransactionManagerTests {
@Test
public void testParticipatingTransactionWithRollbackOnlyAndInnerSynch() throws Exception {
tm.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER);
- DataSourceTransactionManager tm2 = new DataSourceTransactionManager(ds);
+ DataSourceTransactionManager tm2 = createTransactionManager(ds);
// tm has no synch enabled (used at outer level), tm2 has synch enabled (inner level)
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
@@ -613,7 +612,7 @@ public class DataSourceTransactionManagerTests {
final TransactionTemplate tt = new TransactionTemplate(tm);
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- PlatformTransactionManager tm2 = new DataSourceTransactionManager(ds2);
+ PlatformTransactionManager tm2 = createTransactionManager(ds2);
final TransactionTemplate tt2 = new TransactionTemplate(tm2);
tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
@@ -662,7 +661,7 @@ public class DataSourceTransactionManagerTests {
final TransactionTemplate tt = new TransactionTemplate(tm);
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- DataSourceTransactionManager tm2 = new DataSourceTransactionManager(ds2);
+ DataSourceTransactionManager tm2 = createTransactionManager(ds2);
tm2.setTransactionSynchronization(DataSourceTransactionManager.SYNCHRONIZATION_NEVER);
final TransactionTemplate tt2 = new TransactionTemplate(tm2);
tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
@@ -1023,6 +1022,42 @@ public class DataSourceTransactionManagerTests {
verify(con).close();
}
+ @Test
+ public void testTransactionAwareDataSourceProxyWithLazyFalse() throws Exception {
+ given(con.getAutoCommit()).willReturn(true);
+ given(con.getWarnings()).willThrow(new SQLException());
+
+ TransactionTemplate tt = new TransactionTemplate(tm);
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
+ tt.execute(new TransactionCallbackWithoutResult() {
+ @Override
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+ // something transactional
+ assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
+ TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
+ dsProxy.setLazyTransactionalConnections(false);
+ try {
+ Connection tCon = dsProxy.getConnection();
+ assertThatExceptionOfType(SQLException.class).isThrownBy(tCon::getWarnings);
+ tCon.clearWarnings();
+ assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
+ // should be ignored
+ dsProxy.getConnection().close();
+ }
+ catch (SQLException ex) {
+ throw new UncategorizedSQLException("", "", ex);
+ }
+ }
+ });
+
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
+ InOrder ordered = inOrder(con);
+ ordered.verify(con).setAutoCommit(false);
+ ordered.verify(con).commit();
+ ordered.verify(con).setAutoCommit(true);
+ verify(con).close();
+ }
+
@Test
public void testTransactionAwareDataSourceProxyWithSuspension() throws Exception {
given(con.getAutoCommit()).willReturn(true);
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java
index a33e5df514a..a40a03699c7 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/JdbcTransactionManagerTests.java
@@ -16,1142 +16,66 @@
package org.springframework.jdbc.support;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.PreparedStatement;
import java.sql.SQLException;
-import java.sql.Savepoint;
-import java.sql.Statement;
import javax.sql.DataSource;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InOrder;
-import org.springframework.core.testfixture.EnabledForTestGroups;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.dao.DataAccessResourceFailureException;
-import org.springframework.jdbc.UncategorizedSQLException;
-import org.springframework.jdbc.datasource.ConnectionHolder;
-import org.springframework.jdbc.datasource.ConnectionProxy;
-import org.springframework.jdbc.datasource.DataSourceUtils;
-import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
-import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
-import org.springframework.transaction.CannotCreateTransactionException;
-import org.springframework.transaction.IllegalTransactionStateException;
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.TransactionDefinition;
+import org.springframework.jdbc.datasource.DataSourceTransactionManagerTests;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException;
-import org.springframework.transaction.TransactionTimedOutException;
-import org.springframework.transaction.UnexpectedRollbackException;
-import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
-import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
-import static org.assertj.core.api.Assertions.assertThatRuntimeException;
-import static org.assertj.core.api.Assertions.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
/**
* @author Juergen Hoeller
* @since 5.3
* @see org.springframework.jdbc.datasource.DataSourceTransactionManagerTests
*/
-public class JdbcTransactionManagerTests {
+public class JdbcTransactionManagerTests extends DataSourceTransactionManagerTests {
- private DataSource ds = mock();
-
- private Connection con = mock();
-
- private JdbcTransactionManager tm = new JdbcTransactionManager(ds);
-
-
- @BeforeEach
- public void setup() throws Exception {
- given(ds.getConnection()).willReturn(con);
+ @Override
+ protected JdbcTransactionManager createTransactionManager(DataSource ds) {
+ return new JdbcTransactionManager(ds);
}
- @AfterEach
- public void verifyTransactionSynchronizationManagerState() {
- assertThat(TransactionSynchronizationManager.getResourceMap()).isEmpty();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse();
- }
-
-
- @Test
- public void testTransactionCommitWithAutoCommitTrue() throws Exception {
- doTestTransactionCommitRestoringAutoCommit(true, false, false);
- }
-
- @Test
- public void testTransactionCommitWithAutoCommitFalse() throws Exception {
- doTestTransactionCommitRestoringAutoCommit(false, false, false);
- }
-
- @Test
- public void testTransactionCommitWithAutoCommitTrueAndLazyConnection() throws Exception {
- doTestTransactionCommitRestoringAutoCommit(true, true, false);
- }
-
- @Test
- public void testTransactionCommitWithAutoCommitFalseAndLazyConnection() throws Exception {
- doTestTransactionCommitRestoringAutoCommit(false, true, false);
- }
-
- @Test
- public void testTransactionCommitWithAutoCommitTrueAndLazyConnectionAndStatementCreated() throws Exception {
- doTestTransactionCommitRestoringAutoCommit(true, true, true);
- }
-
- @Test
- public void testTransactionCommitWithAutoCommitFalseAndLazyConnectionAndStatementCreated() throws Exception {
- doTestTransactionCommitRestoringAutoCommit(false, true, true);
- }
-
- private void doTestTransactionCommitRestoringAutoCommit(
- boolean autoCommit, boolean lazyConnection, final boolean createStatement) throws Exception {
-
- if (lazyConnection) {
- given(con.getAutoCommit()).willReturn(autoCommit);
- given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
- given(con.getWarnings()).willThrow(new SQLException());
- }
-
- if (!lazyConnection || createStatement) {
- given(con.getAutoCommit()).willReturn(autoCommit);
- }
-
- final DataSource dsToUse = (lazyConnection ? new LazyConnectionDataSourceProxy(ds) : ds);
- tm = new JdbcTransactionManager(dsToUse);
- TransactionTemplate tt = new TransactionTemplate(tm);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- Connection tCon = DataSourceUtils.getConnection(dsToUse);
- try {
- if (createStatement) {
- tCon.createStatement();
- }
- else {
- tCon.getWarnings();
- tCon.clearWarnings();
- }
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- if (autoCommit && (!lazyConnection || createStatement)) {
- InOrder ordered = inOrder(con);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).commit();
- ordered.verify(con).setAutoCommit(true);
- }
- if (createStatement) {
- verify(con, times(2)).close();
- }
- else {
- verify(con).close();
- }
- }
-
- @Test
- public void testTransactionRollbackWithAutoCommitTrue() throws Exception {
- doTestTransactionRollbackRestoringAutoCommit(true, false, false);
- }
-
- @Test
- public void testTransactionRollbackWithAutoCommitFalse() throws Exception {
- doTestTransactionRollbackRestoringAutoCommit(false, false, false);
- }
-
- @Test
- public void testTransactionRollbackWithAutoCommitTrueAndLazyConnection() throws Exception {
- doTestTransactionRollbackRestoringAutoCommit(true, true, false);
- }
-
- @Test
- public void testTransactionRollbackWithAutoCommitFalseAndLazyConnection() throws Exception {
- doTestTransactionRollbackRestoringAutoCommit(false, true, false);
- }
-
- @Test
- public void testTransactionRollbackWithAutoCommitTrueAndLazyConnectionAndCreateStatement() throws Exception {
- doTestTransactionRollbackRestoringAutoCommit(true, true, true);
- }
-
- @Test
- public void testTransactionRollbackWithAutoCommitFalseAndLazyConnectionAndCreateStatement() throws Exception {
- doTestTransactionRollbackRestoringAutoCommit(false, true, true);
- }
-
- private void doTestTransactionRollbackRestoringAutoCommit(
- boolean autoCommit, boolean lazyConnection, final boolean createStatement) throws Exception {
-
- if (lazyConnection) {
- given(con.getAutoCommit()).willReturn(autoCommit);
- given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
- }
-
- if (!lazyConnection || createStatement) {
- given(con.getAutoCommit()).willReturn(autoCommit);
- }
-
- final DataSource dsToUse = (lazyConnection ? new LazyConnectionDataSourceProxy(ds) : ds);
- tm = new JdbcTransactionManager(dsToUse);
- TransactionTemplate tt = new TransactionTemplate(tm);
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- final RuntimeException ex = new RuntimeException("Application exception");
- assertThatRuntimeException().isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(dsToUse)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- Connection con = DataSourceUtils.getConnection(dsToUse);
- if (createStatement) {
- try {
- con.createStatement();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- throw ex;
- }
- }))
- .isEqualTo(ex);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- if (autoCommit && (!lazyConnection || createStatement)) {
- InOrder ordered = inOrder(con);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).rollback();
- ordered.verify(con).setAutoCommit(true);
- }
- if (createStatement) {
- verify(con, times(2)).close();
- }
- else {
- verify(con).close();
- }
- }
-
- @Test
- public void testTransactionRollbackOnly() throws Exception {
- tm.setTransactionSynchronization(JdbcTransactionManager.SYNCHRONIZATION_NEVER);
- TransactionTemplate tt = new TransactionTemplate(tm);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- ConnectionHolder conHolder = new ConnectionHolder(con, true);
- TransactionSynchronizationManager.bindResource(ds, conHolder);
- final RuntimeException ex = new RuntimeException("Application exception");
- try {
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- throw ex;
- }
- });
- fail("Should have thrown RuntimeException");
- }
- catch (RuntimeException ex2) {
- // expected
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
- assertThat(ex2).as("Correct exception thrown").isEqualTo(ex);
- }
- finally {
- TransactionSynchronizationManager.unbindResource(ds);
- }
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- }
-
- @Test
- public void testParticipatingTransactionWithRollbackOnly() throws Exception {
- doTestParticipatingTransactionWithRollbackOnly(false);
- }
-
- @Test
- public void testParticipatingTransactionWithRollbackOnlyAndFailEarly() throws Exception {
- doTestParticipatingTransactionWithRollbackOnly(true);
- }
-
- private void doTestParticipatingTransactionWithRollbackOnly(boolean failEarly) throws Exception {
- given(con.isReadOnly()).willReturn(false);
- if (failEarly) {
- tm.setFailEarlyOnGlobalRollbackOnly(true);
- }
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- TransactionStatus ts = tm.getTransaction(new DefaultTransactionDefinition());
- TestTransactionSynchronization synch =
- new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_ROLLED_BACK);
- TransactionSynchronizationManager.registerSynchronization(synch);
-
- boolean outerTransactionBoundaryReached = false;
- try {
- assertThat(ts.isNewTransaction()).as("Is new transaction").isTrue();
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- assertThat(status.isRollbackOnly()).as("Is not rollback-only").isFalse();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- status.setRollbackOnly();
- }
- });
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- assertThat(status.isRollbackOnly()).as("Is rollback-only").isTrue();
- }
- });
-
- outerTransactionBoundaryReached = true;
- tm.commit(ts);
-
- fail("Should have thrown UnexpectedRollbackException");
- }
- catch (UnexpectedRollbackException ex) {
- // expected
- if (!outerTransactionBoundaryReached) {
- tm.rollback(ts);
- }
- if (failEarly) {
- assertThat(outerTransactionBoundaryReached).isFalse();
- }
- else {
- assertThat(outerTransactionBoundaryReached).isTrue();
- }
- }
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(synch.beforeCommitCalled).isFalse();
- assertThat(synch.beforeCompletionCalled).isTrue();
- assertThat(synch.afterCommitCalled).isFalse();
- assertThat(synch.afterCompletionCalled).isTrue();
- verify(con).rollback();
- verify(con).close();
- }
-
- @Test
- public void testParticipatingTransactionWithIncompatibleIsolationLevel() throws Exception {
- tm.setValidateExistingTransaction(true);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- assertThatExceptionOfType(IllegalTransactionStateException.class).isThrownBy(() -> {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- final TransactionTemplate tt2 = new TransactionTemplate(tm);
- tt2.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isRollbackOnly()).as("Is not rollback-only").isFalse();
- tt2.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- status.setRollbackOnly();
- }
- });
- assertThat(status.isRollbackOnly()).as("Is rollback-only").isTrue();
- }
- });
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback();
- verify(con).close();
- }
-
- @Test
- public void testParticipatingTransactionWithIncompatibleReadOnly() throws Exception {
- willThrow(new SQLException("read-only not supported")).given(con).setReadOnly(true);
- tm.setValidateExistingTransaction(true);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- assertThatExceptionOfType(IllegalTransactionStateException.class).isThrownBy(() -> {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setReadOnly(true);
- final TransactionTemplate tt2 = new TransactionTemplate(tm);
- tt2.setReadOnly(false);
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isRollbackOnly()).as("Is not rollback-only").isFalse();
- tt2.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- status.setRollbackOnly();
- }
- });
- assertThat(status.isRollbackOnly()).as("Is rollback-only").isTrue();
- }
- });
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback();
- verify(con).close();
- }
-
- @Test
- public void testParticipatingTransactionWithTransactionStartedFromSynch() throws Exception {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
-
- final TestTransactionSynchronization synch =
- new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_COMMITTED) {
- @Override
- protected void doAfterCompletion(int status) {
- super.doAfterCompletion(status);
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- }
- });
- TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {});
- }
- };
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- TransactionSynchronizationManager.registerSynchronization(synch);
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(synch.beforeCommitCalled).isTrue();
- assertThat(synch.beforeCompletionCalled).isTrue();
- assertThat(synch.afterCommitCalled).isTrue();
- assertThat(synch.afterCompletionCalled).isTrue();
- assertThat(synch.afterCompletionException).isInstanceOf(IllegalStateException.class);
- verify(con, times(2)).commit();
- verify(con, times(2)).close();
- }
-
- @Test
- public void testParticipatingTransactionWithDifferentConnectionObtainedFromSynch() throws Exception {
- DataSource ds2 = mock();
- final Connection con2 = mock();
- given(ds2.getConnection()).willReturn(con2);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
-
- final TestTransactionSynchronization synch =
- new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_COMMITTED) {
- @Override
- protected void doAfterCompletion(int status) {
- super.doAfterCompletion(status);
- Connection con = DataSourceUtils.getConnection(ds2);
- DataSourceUtils.releaseConnection(con, ds2);
- }
- };
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- TransactionSynchronizationManager.registerSynchronization(synch);
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(synch.beforeCommitCalled).isTrue();
- assertThat(synch.beforeCompletionCalled).isTrue();
- assertThat(synch.afterCommitCalled).isTrue();
- assertThat(synch.afterCompletionCalled).isTrue();
- assertThat(synch.afterCompletionException).isNull();
- verify(con).commit();
- verify(con).close();
- verify(con2).close();
- }
-
- @Test
- public void testParticipatingTransactionWithRollbackOnlyAndInnerSynch() throws Exception {
- tm.setTransactionSynchronization(JdbcTransactionManager.SYNCHRONIZATION_NEVER);
- JdbcTransactionManager tm2 = new JdbcTransactionManager(ds);
- // tm has no synch enabled (used at outer level), tm2 has synch enabled (inner level)
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- TransactionStatus ts = tm.getTransaction(new DefaultTransactionDefinition());
- final TestTransactionSynchronization synch =
- new TestTransactionSynchronization(ds, TransactionSynchronization.STATUS_UNKNOWN);
-
- assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() -> {
- assertThat(ts.isNewTransaction()).as("Is new transaction").isTrue();
- final TransactionTemplate tt = new TransactionTemplate(tm2);
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- assertThat(status.isRollbackOnly()).as("Is not rollback-only").isFalse();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- status.setRollbackOnly();
- }
- });
- assertThat(status.isNewTransaction()).as("Is existing transaction").isFalse();
- assertThat(status.isRollbackOnly()).as("Is rollback-only").isTrue();
- TransactionSynchronizationManager.registerSynchronization(synch);
- }
- });
-
- tm.commit(ts);
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(synch.beforeCommitCalled).isFalse();
- assertThat(synch.beforeCompletionCalled).isTrue();
- assertThat(synch.afterCommitCalled).isFalse();
- assertThat(synch.afterCompletionCalled).isTrue();
- verify(con).rollback();
- verify(con).close();
- }
-
- @Test
- public void testPropagationRequiresNewWithExistingTransaction() throws Exception {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- status.setRollbackOnly();
- }
- });
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback();
- verify(con).commit();
- verify(con, times(2)).close();
- }
-
- @Test
- public void testPropagationRequiresNewWithExistingTransactionAndUnrelatedDataSource() throws Exception {
- Connection con2 = mock();
- final DataSource ds2 = mock();
- given(ds2.getConnection()).willReturn(con2);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
-
- PlatformTransactionManager tm2 = new JdbcTransactionManager(ds2);
- final TransactionTemplate tt2 = new TransactionTemplate(tm2);
- tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.hasResource(ds2)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- tt2.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- status.setRollbackOnly();
- }
- });
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.hasResource(ds2)).as("Hasn't thread connection").isFalse();
- verify(con).commit();
- verify(con).close();
- verify(con2).rollback();
- verify(con2).close();
- }
-
- @Test
- public void testPropagationRequiresNewWithExistingTransactionAndUnrelatedFailingDataSource() throws Exception {
- final DataSource ds2 = mock();
- SQLException failure = new SQLException();
- given(ds2.getConnection()).willThrow(failure);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
-
- JdbcTransactionManager tm2 = new JdbcTransactionManager(ds2);
- tm2.setTransactionSynchronization(JdbcTransactionManager.SYNCHRONIZATION_NEVER);
- final TransactionTemplate tt2 = new TransactionTemplate(tm2);
- tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.hasResource(ds2)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- assertThatExceptionOfType(CannotCreateTransactionException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- tt2.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- status.setRollbackOnly();
- }
- });
- }
- })).withCause(failure);
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.hasResource(ds2)).as("Hasn't thread connection").isFalse();
- verify(con).rollback();
- verify(con).close();
- }
-
- @Test
- public void testPropagationNotSupportedWithExistingTransaction() throws Exception {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse();
- status.setRollbackOnly();
- }
- });
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testPropagationNeverWithExistingTransaction() throws Exception {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- assertThatExceptionOfType(IllegalTransactionStateException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- fail("Should have thrown IllegalTransactionStateException");
- }
- });
- fail("Should have thrown IllegalTransactionStateException");
- }
- }));
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback();
- verify(con).close();
- }
-
- @Test
- public void testPropagationSupportsAndRequiresNew() throws Exception {
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- TransactionTemplate tt2 = new TransactionTemplate(tm);
- tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- tt2.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con);
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con);
- }
- });
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testPropagationSupportsAndRequiresNewWithEarlyAccess() throws Exception {
- final Connection con1 = mock();
- final Connection con2 = mock();
- given(ds.getConnection()).willReturn(con1, con2);
-
- final
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1);
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1);
- TransactionTemplate tt2 = new TransactionTemplate(tm);
- tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- tt2.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con2);
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con2);
- }
- });
- assertThat(DataSourceUtils.getConnection(ds)).isSameAs(con1);
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con1).close();
- verify(con2).commit();
- verify(con2).close();
- }
-
- @Test
- public void testTransactionWithIsolationAndReadOnly() throws Exception {
- given(con.getTransactionIsolation()).willReturn(Connection.TRANSACTION_READ_COMMITTED);
- given(con.getAutoCommit()).willReturn(true);
-
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
- tt.setReadOnly(true);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isTrue();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- // something transactional
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- InOrder ordered = inOrder(con);
- ordered.verify(con).setReadOnly(true);
- ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).commit();
- ordered.verify(con).setAutoCommit(true);
- ordered.verify(con).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
- ordered.verify(con).setReadOnly(false);
- verify(con).close();
- }
-
- @Test
- public void testTransactionWithEnforceReadOnly() throws Exception {
- tm.setEnforceReadOnly(true);
-
- given(con.getAutoCommit()).willReturn(true);
- Statement stmt = mock();
- given(con.createStatement()).willReturn(stmt);
-
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
- tt.setReadOnly(true);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isTrue();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue();
- // something transactional
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- InOrder ordered = inOrder(con, stmt);
- ordered.verify(con).setReadOnly(true);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(stmt).executeUpdate("SET TRANSACTION READ ONLY");
- ordered.verify(stmt).close();
- ordered.verify(con).commit();
- ordered.verify(con).setAutoCommit(true);
- ordered.verify(con).setReadOnly(false);
- ordered.verify(con).close();
- }
-
- @ParameterizedTest(name = "transaction with {0} second timeout")
- @ValueSource(ints = {1, 10})
- @EnabledForTestGroups(LONG_RUNNING)
- public void transactionWithTimeout(int timeout) throws Exception {
- PreparedStatement ps = mock();
- given(con.getAutoCommit()).willReturn(true);
- given(con.prepareStatement("some SQL statement")).willReturn(ps);
-
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setTimeout(timeout);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
-
- try {
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- try {
- Thread.sleep(1500);
- }
- catch (InterruptedException ex) {
- }
- try {
- Connection con = DataSourceUtils.getConnection(ds);
- PreparedStatement ps = con.prepareStatement("some SQL statement");
- DataSourceUtils.applyTransactionTimeout(ps, ds);
- }
- catch (SQLException ex) {
- throw new DataAccessResourceFailureException("", ex);
- }
- }
- });
- if (timeout <= 1) {
- fail("Should have thrown TransactionTimedOutException");
- }
- }
- catch (TransactionTimedOutException ex) {
- if (timeout <= 1) {
- // expected
- }
- else {
- throw ex;
- }
- }
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- if (timeout > 1) {
- verify(ps).setQueryTimeout(timeout - 1);
- verify(con).commit();
- }
- else {
- verify(con).rollback();
- }
- InOrder ordered = inOrder(con);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).setAutoCommit(true);
- verify(con).close();
- }
-
- @Test
- public void testTransactionAwareDataSourceProxy() throws Exception {
- given(con.getAutoCommit()).willReturn(true);
- given(con.getWarnings()).willThrow(new SQLException());
-
- TransactionTemplate tt = new TransactionTemplate(tm);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- // something transactional
- assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
- TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
- try {
- Connection tCon = dsProxy.getConnection();
- tCon.getWarnings();
- tCon.clearWarnings();
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- InOrder ordered = inOrder(con);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).commit();
- ordered.verify(con).setAutoCommit(true);
- verify(con).close();
- }
-
- @Test
- public void testTransactionAwareDataSourceProxyWithSuspension() throws Exception {
- given(con.getAutoCommit()).willReturn(true);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- // something transactional
- assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
- final TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
- try {
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- // something transactional
- assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
- try {
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- });
-
- try {
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- InOrder ordered = inOrder(con);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).commit();
- ordered.verify(con).setAutoCommit(true);
- verify(con, times(2)).close();
- }
-
- @Test
- public void testTransactionAwareDataSourceProxyWithSuspensionAndReobtaining() throws Exception {
- given(con.getAutoCommit()).willReturn(true);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- // something transactional
- assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
- final TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
- dsProxy.setReobtainTransactionalConnections(true);
- try {
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- // something transactional
- assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
- try {
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- });
-
- try {
- assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
- // should be ignored
- dsProxy.getConnection().close();
- }
- catch (SQLException ex) {
- throw new UncategorizedSQLException("", "", ex);
- }
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- InOrder ordered = inOrder(con);
- ordered.verify(con).setAutoCommit(false);
- ordered.verify(con).commit();
- ordered.verify(con).setAutoCommit(true);
- verify(con, times(2)).close();
- }
-
- /**
- * Test behavior if the first operation on a connection (getAutoCommit) throws SQLException.
- */
- @Test
- public void testTransactionWithExceptionOnBegin() throws Exception {
- willThrow(new SQLException("Cannot begin")).given(con).getAutoCommit();
-
- TransactionTemplate tt = new TransactionTemplate(tm);
- assertThatExceptionOfType(CannotCreateTransactionException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- // something transactional
- }
- }));
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).close();
- }
@Test
public void testTransactionWithExceptionOnCommit() throws Exception {
willThrow(new SQLException("Cannot commit")).given(con).commit();
-
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // plain TransactionSystemException
assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
+ tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// something transactional
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
verify(con).close();
}
@Test
public void testTransactionWithDataAccessExceptionOnCommit() throws Exception {
willThrow(new SQLException("Cannot commit")).given(con).commit();
- tm.setExceptionTranslator((task, sql, ex) -> new ConcurrencyFailureException(task));
-
+ ((JdbcTransactionManager) tm).setExceptionTranslator((task, sql, ex) -> new ConcurrencyFailureException(task));
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // specific ConcurrencyFailureException
assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() ->
tt.execute(new TransactionCallbackWithoutResult() {
@Override
@@ -1160,15 +84,16 @@ public class JdbcTransactionManagerTests {
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
verify(con).close();
}
@Test
public void testTransactionWithDataAccessExceptionOnCommitFromLazyExceptionTranslator() throws Exception {
willThrow(new SQLException("Cannot commit", "40")).given(con).commit();
-
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // specific ConcurrencyFailureException
assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() ->
tt.execute(new TransactionCallbackWithoutResult() {
@Override
@@ -1177,7 +102,7 @@ public class JdbcTransactionManagerTests {
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
verify(con).close();
}
@@ -1187,15 +112,17 @@ public class JdbcTransactionManagerTests {
tm.setRollbackOnCommitFailure(true);
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // plain TransactionSystemException
assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
+ tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// something transactional
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
verify(con).rollback();
verify(con).close();
}
@@ -1204,17 +131,27 @@ public class JdbcTransactionManagerTests {
public void testTransactionWithExceptionOnRollback() throws Exception {
given(con.getAutoCommit()).willReturn(true);
willThrow(new SQLException("Cannot rollback")).given(con).rollback();
-
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // plain TransactionSystemException
assertThatExceptionOfType(TransactionSystemException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
+ tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
+ assertThat(status.getTransactionName()).isEmpty();
+ assertThat(status.hasTransaction()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
+ assertThat(status.isNested()).isFalse();
+ assertThat(status.hasSavepoint()).isFalse();
+ assertThat(status.isReadOnly()).isFalse();
+ assertThat(status.isRollbackOnly()).isFalse();
status.setRollbackOnly();
+ assertThat(status.isRollbackOnly()).isTrue();
+ assertThat(status.isCompleted()).isFalse();
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
InOrder ordered = inOrder(con);
ordered.verify(con).setAutoCommit(false);
ordered.verify(con).rollback();
@@ -1226,9 +163,10 @@ public class JdbcTransactionManagerTests {
public void testTransactionWithDataAccessExceptionOnRollback() throws Exception {
given(con.getAutoCommit()).willReturn(true);
willThrow(new SQLException("Cannot rollback")).given(con).rollback();
- tm.setExceptionTranslator((task, sql, ex) -> new ConcurrencyFailureException(task));
-
+ ((JdbcTransactionManager) tm).setExceptionTranslator((task, sql, ex) -> new ConcurrencyFailureException(task));
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // specific ConcurrencyFailureException
assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() ->
tt.execute(new TransactionCallbackWithoutResult() {
@Override
@@ -1237,7 +175,7 @@ public class JdbcTransactionManagerTests {
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
InOrder ordered = inOrder(con);
ordered.verify(con).setAutoCommit(false);
ordered.verify(con).rollback();
@@ -1249,17 +187,27 @@ public class JdbcTransactionManagerTests {
public void testTransactionWithDataAccessExceptionOnRollbackFromLazyExceptionTranslator() throws Exception {
given(con.getAutoCommit()).willReturn(true);
willThrow(new SQLException("Cannot rollback", "40")).given(con).rollback();
-
TransactionTemplate tt = new TransactionTemplate(tm);
+
+ // specific ConcurrencyFailureException
assertThatExceptionOfType(ConcurrencyFailureException.class).isThrownBy(() ->
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
+ assertThat(status.getTransactionName()).isEmpty();
+ assertThat(status.hasTransaction()).isTrue();
+ assertThat(status.isNewTransaction()).isTrue();
+ assertThat(status.isNested()).isFalse();
+ assertThat(status.hasSavepoint()).isFalse();
+ assertThat(status.isReadOnly()).isFalse();
+ assertThat(status.isRollbackOnly()).isFalse();
status.setRollbackOnly();
+ assertThat(status.isRollbackOnly()).isTrue();
+ assertThat(status.isCompleted()).isFalse();
}
}));
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
+ assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
InOrder ordered = inOrder(con);
ordered.verify(con).setAutoCommit(false);
ordered.verify(con).rollback();
@@ -1267,435 +215,4 @@ public class JdbcTransactionManagerTests {
verify(con).close();
}
- @Test
- public void testTransactionWithPropagationSupports() throws Exception {
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(status.isNewTransaction()).as("Is not new transaction").isFalse();
- assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
- assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- }
-
- @Test
- public void testTransactionWithPropagationNotSupported() throws Exception {
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(status.isNewTransaction()).as("Is not new transaction").isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- }
-
- @Test
- public void testTransactionWithPropagationNever() throws Exception {
- TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(status.isNewTransaction()).as("Is not new transaction").isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- }
-
- @Test
- public void testExistingTransactionWithPropagationNested() throws Exception {
- doTestExistingTransactionWithPropagationNested(1);
- }
-
- @Test
- public void testExistingTransactionWithPropagationNestedTwice() throws Exception {
- doTestExistingTransactionWithPropagationNested(2);
- }
-
- private void doTestExistingTransactionWithPropagationNested(final int count) throws Exception {
- DatabaseMetaData md = mock();
- Savepoint sp = mock();
-
- given(md.supportsSavepoints()).willReturn(true);
- given(con.getMetaData()).willReturn(md);
- for (int i = 1; i <= count; i++) {
- given(con.setSavepoint(ConnectionHolder.SAVEPOINT_NAME_PREFIX + i)).willReturn(sp);
- }
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active")
- .isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- for (int i = 0; i < count; i++) {
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(status.hasSavepoint()).as("Is nested transaction").isTrue();
- }
- });
- }
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con, times(count)).releaseSavepoint(sp);
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testExistingTransactionWithPropagationNestedAndRollback() throws Exception {
- DatabaseMetaData md = mock();
- Savepoint sp = mock();
-
- given(md.supportsSavepoints()).willReturn(true);
- given(con.getMetaData()).willReturn(md);
- given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active")
- .isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(status.hasSavepoint()).as("Is nested transaction").isTrue();
- status.setRollbackOnly();
- }
- });
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback(sp);
- verify(con).releaseSavepoint(sp);
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testExistingTransactionWithPropagationNestedAndRequiredRollback() throws Exception {
- DatabaseMetaData md = mock();
- Savepoint sp = mock();
-
- given(md.supportsSavepoints()).willReturn(true);
- given(con.getMetaData()).willReturn(md);
- given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active")
- .isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- assertThatIllegalStateException().isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(status.hasSavepoint()).as("Is nested transaction").isTrue();
- TransactionTemplate ntt = new TransactionTemplate(tm);
- ntt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(status.hasSavepoint()).as("Is regular transaction").isFalse();
- throw new IllegalStateException();
- }
- });
- }
- }));
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback(sp);
- verify(con).releaseSavepoint(sp);
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testExistingTransactionWithPropagationNestedAndRequiredRollbackOnly() throws Exception {
- DatabaseMetaData md = mock();
- Savepoint sp = mock();
-
- given(md.supportsSavepoints()).willReturn(true);
- given(con.getMetaData()).willReturn(md);
- given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active")
- .isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() ->
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(status.hasSavepoint()).as("Is nested transaction").isTrue();
- TransactionTemplate ntt = new TransactionTemplate(tm);
- ntt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Has thread connection").isTrue();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization active").isTrue();
- assertThat(status.isNewTransaction()).as("Isn't new transaction").isFalse();
- assertThat(status.hasSavepoint()).as("Is regular transaction").isFalse();
- status.setRollbackOnly();
- }
- });
- }
- }));
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- assertThat(status.hasSavepoint()).as("Isn't nested transaction").isFalse();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback(sp);
- verify(con).releaseSavepoint(sp);
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testExistingTransactionWithManualSavepoint() throws Exception {
- DatabaseMetaData md = mock();
- Savepoint sp = mock();
-
- given(md.supportsSavepoints()).willReturn(true);
- given(con.getMetaData()).willReturn(md);
- given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active")
- .isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- Object savepoint = status.createSavepoint();
- status.releaseSavepoint(savepoint);
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).releaseSavepoint(sp);
- verify(con).commit();
- verify(con).close();
- verify(ds).getConnection();
- }
-
- @Test
- public void testExistingTransactionWithManualSavepointAndRollback() throws Exception {
- DatabaseMetaData md = mock();
- Savepoint sp = mock();
-
- given(md.supportsSavepoints()).willReturn(true);
- given(con.getMetaData()).willReturn(md);
- given(con.setSavepoint("SAVEPOINT_1")).willReturn(sp);
-
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- Object savepoint = status.createSavepoint();
- status.rollbackToSavepoint(savepoint);
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback(sp);
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testTransactionWithPropagationNested() throws Exception {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).commit();
- verify(con).close();
- }
-
- @Test
- public void testTransactionWithPropagationNestedAndRollback() throws Exception {
- final TransactionTemplate tt = new TransactionTemplate(tm);
- tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- assertThat(TransactionSynchronizationManager.isSynchronizationActive()).as("Synchronization not active").isFalse();
-
- tt.execute(new TransactionCallbackWithoutResult() {
- @Override
- protected void doInTransactionWithoutResult(TransactionStatus status) throws RuntimeException {
- assertThat(status.isNewTransaction()).as("Is new transaction").isTrue();
- status.setRollbackOnly();
- }
- });
-
- assertThat(TransactionSynchronizationManager.hasResource(ds)).as("Hasn't thread connection").isFalse();
- verify(con).rollback();
- verify(con).close();
- }
-
-
- private static class TestTransactionSynchronization implements TransactionSynchronization {
-
- private DataSource dataSource;
-
- private int status;
-
- public boolean beforeCommitCalled;
-
- public boolean beforeCompletionCalled;
-
- public boolean afterCommitCalled;
-
- public boolean afterCompletionCalled;
-
- public Throwable afterCompletionException;
-
- public TestTransactionSynchronization(DataSource dataSource, int status) {
- this.dataSource = dataSource;
- this.status = status;
- }
-
- @Override
- public void suspend() {
- }
-
- @Override
- public void resume() {
- }
-
- @Override
- public void flush() {
- }
-
- @Override
- public void beforeCommit(boolean readOnly) {
- if (this.status != TransactionSynchronization.STATUS_COMMITTED) {
- fail("Should never be called");
- }
- assertThat(this.beforeCommitCalled).isFalse();
- this.beforeCommitCalled = true;
- }
-
- @Override
- public void beforeCompletion() {
- assertThat(this.beforeCompletionCalled).isFalse();
- this.beforeCompletionCalled = true;
- }
-
- @Override
- public void afterCommit() {
- if (this.status != TransactionSynchronization.STATUS_COMMITTED) {
- fail("Should never be called");
- }
- assertThat(this.afterCommitCalled).isFalse();
- this.afterCommitCalled = true;
- }
-
- @Override
- public void afterCompletion(int status) {
- try {
- doAfterCompletion(status);
- }
- catch (Throwable ex) {
- this.afterCompletionException = ex;
- }
- }
-
- protected void doAfterCompletion(int status) {
- assertThat(this.afterCompletionCalled).isFalse();
- this.afterCompletionCalled = true;
- assertThat(status).isEqualTo(this.status);
- assertThat(TransactionSynchronizationManager.hasResource(this.dataSource)).isTrue();
- }
- }
-
}