From 9c8f7d9246f35c1a2cea543d4aa16e21b9e5efcd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 19 Sep 2014 00:11:44 +0200 Subject: [PATCH] Explicitly release rolled-back database savepoints during (long-running) transaction Issue: SPR-12228 --- .../datasource/JdbcTransactionObjectSupport.java | 12 ++++++++++-- .../DataSourceTransactionManagerTests.java | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java index 31f0fd1452..e5ae9df7ce 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java @@ -120,12 +120,19 @@ public abstract class JdbcTransactionObjectSupport implements SavepointManager, */ @Override public void rollbackToSavepoint(Object savepoint) throws TransactionException { + ConnectionHolder conHolder = getConnectionHolderForSavepoint(); try { - getConnectionHolderForSavepoint().getConnection().rollback((Savepoint) savepoint); + conHolder.getConnection().rollback((Savepoint) savepoint); } catch (Throwable ex) { throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex); } + try { + conHolder.getConnection().releaseSavepoint((Savepoint) savepoint); + } + catch (Throwable ex) { + logger.debug("Could not explicitly release JDBC savepoint after rollback", ex); + } } /** @@ -134,8 +141,9 @@ public abstract class JdbcTransactionObjectSupport implements SavepointManager, */ @Override public void releaseSavepoint(Object savepoint) throws TransactionException { + ConnectionHolder conHolder = getConnectionHolderForSavepoint(); try { - getConnectionHolderForSavepoint().getConnection().releaseSavepoint((Savepoint) savepoint); + conHolder.getConnection().releaseSavepoint((Savepoint) savepoint); } catch (Throwable ex) { logger.debug("Could not explicitly release JDBC savepoint", ex); 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 3c29aa3ae8..e4ecd2fccc 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,13 +21,13 @@ import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Savepoint; - import javax.sql.DataSource; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; + import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.jdbc.UncategorizedSQLException; import org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor; @@ -57,9 +57,12 @@ import static org.mockito.BDDMockito.*; public class DataSourceTransactionManagerTests { private Connection con; + private DataSource ds; + private DataSourceTransactionManager tm; + @Before public void setUp() throws Exception { con = mock(Connection.class); @@ -76,6 +79,7 @@ public class DataSourceTransactionManagerTests { assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); } + @Test public void testTransactionCommitWithAutoCommitTrue() throws Exception { doTestTransactionCommitRestoringAutoCommit(true, false, false); @@ -1290,6 +1294,7 @@ public class DataSourceTransactionManagerTests { assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); verify(con).rollback(sp); + verify(con).releaseSavepoint(sp); verify(con).commit(); verify(con).isReadOnly(); verify(con).close(); @@ -1395,14 +1400,19 @@ public class DataSourceTransactionManagerTests { 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 TestTransactionSynchronization(DataSource dataSource, int status) {