From 23f8a48ded90be514694b9cf8de847fc1e344c37 Mon Sep 17 00:00:00 2001 From: kang-hyungu Date: Thu, 21 Sep 2023 14:11:58 +0900 Subject: [PATCH 01/21] =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/techcourse/dao/UserDao.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/techcourse/dao/UserDao.java b/app/src/main/java/com/techcourse/dao/UserDao.java index b9f18f3552..dee6890cf1 100644 --- a/app/src/main/java/com/techcourse/dao/UserDao.java +++ b/app/src/main/java/com/techcourse/dao/UserDao.java @@ -1,6 +1,8 @@ package com.techcourse.dao; import com.techcourse.domain.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -18,6 +20,8 @@ public class UserDao { return new User(id, account, password, email); }; + private static final Logger log = LoggerFactory.getLogger(UserDao.class); + private final JdbcTemplate jdbcTemplate; public UserDao(final DataSource dataSource) { From fd3b51e29ba261c5b457d340636fdcf55057112b Mon Sep 17 00:00:00 2001 From: yujamint Date: Wed, 4 Oct 2023 15:08:57 +0900 Subject: [PATCH 02/21] =?UTF-8?q?docs:=203=EB=8B=A8=EA=B3=84=203=EC=B0=A8?= =?UTF-8?q?=20=EB=A6=AC=EB=B7=B0=20=EB=82=B4=EC=9A=A9=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 879627617d..6589646acd 100644 --- a/README.md +++ b/README.md @@ -46,4 +46,7 @@ - [x] 공유자원 Singleton vs static - [x] JdbcTemplate 테스트 작성 - [x] JdbcTemplate의 executeQuery 중복 제거 -- [x] TransactionTemplate catch 절 final \ No newline at end of file +- [x] TransactionTemplate catch 절 final + +### 3차 +- [ ] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) \ No newline at end of file From 161d0afa61d81d3a8b2830a7189db2b779af3fb3 Mon Sep 17 00:00:00 2001 From: yujamint Date: Wed, 4 Oct 2023 15:09:27 +0900 Subject: [PATCH 03/21] =?UTF-8?q?chore:=20EOF=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../springframework/jdbc/core/PreparedStatementCreatorTest.java | 2 +- .../transaction/support/TransactionTemplateTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6589646acd..ca09e95bf1 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,4 @@ - [x] TransactionTemplate catch 절 final ### 3차 -- [ ] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) \ No newline at end of file +- [x] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) \ No newline at end of file diff --git a/jdbc/src/test/java/org/springframework/jdbc/core/PreparedStatementCreatorTest.java b/jdbc/src/test/java/org/springframework/jdbc/core/PreparedStatementCreatorTest.java index ebf4db8e93..17df80ae60 100644 --- a/jdbc/src/test/java/org/springframework/jdbc/core/PreparedStatementCreatorTest.java +++ b/jdbc/src/test/java/org/springframework/jdbc/core/PreparedStatementCreatorTest.java @@ -45,4 +45,4 @@ void createPreparedStatement() throws SQLException { () -> assertThat(parameterMetaData.getParameterClassName(2)).contains("Integer") ); } -} \ No newline at end of file +} diff --git a/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java b/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java index d91d31b110..84da8dcb35 100644 --- a/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java +++ b/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java @@ -55,4 +55,4 @@ void executeWithTransaction_RuntimeExceptionThrown_RollbackAndSameExceptionThrow } -} \ No newline at end of file +} From 9ce3a8389ceb24de25a1135c029d33f07b41809c Mon Sep 17 00:00:00 2001 From: yujamint Date: Thu, 5 Oct 2023 23:45:07 +0900 Subject: [PATCH 04/21] =?UTF-8?q?test:=20=ED=8A=B8=EB=9E=9C=EC=9E=AD?= =?UTF-8?q?=EC=85=98=20=ED=95=99=EC=8A=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/transaction/stage1/Stage1Test.java | 32 ++++++++-------- .../transaction/stage2/FirstUserService.java | 10 ++--- .../java/transaction/stage2/Stage2Test.java | 37 +++++++++++-------- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index ca09e95bf1..c78ccad0d1 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,4 @@ - [x] TransactionTemplate catch 절 final ### 3차 -- [x] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) \ No newline at end of file +- [x] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) diff --git a/study/src/test/java/transaction/stage1/Stage1Test.java b/study/src/test/java/transaction/stage1/Stage1Test.java index 8c29944d4e..94bd3d8a28 100644 --- a/study/src/test/java/transaction/stage1/Stage1Test.java +++ b/study/src/test/java/transaction/stage1/Stage1Test.java @@ -58,10 +58,10 @@ private void setUp(final DataSource dataSource) { * Read phenomena | Dirty reads * Isolation level | * -----------------|------------- - * Read Uncommitted | - * Read Committed | - * Repeatable Read | - * Serializable | + * Read Uncommitted | + + * Read Committed | - + * Repeatable Read | - + * Serializable | - */ @Test void dirtyReading() throws SQLException { @@ -69,6 +69,7 @@ void dirtyReading() throws SQLException { // db에 새로운 연결(사용자A)을 받아와서 final var connection = dataSource.getConnection(); + System.out.println("connection = " + connection); // 트랜잭션을 시작한다. connection.setAutoCommit(false); @@ -81,7 +82,7 @@ void dirtyReading() throws SQLException { final var subConnection = dataSource.getConnection(); // 적절한 격리 레벨을 찾는다. - final int isolationLevel = Connection.TRANSACTION_NONE; + final int isolationLevel = Connection.TRANSACTION_READ_COMMITTED; // 트랜잭션 격리 레벨을 설정한다. subConnection.setTransactionIsolation(isolationLevel); @@ -111,10 +112,10 @@ void dirtyReading() throws SQLException { * Read phenomena | Non-repeatable reads * Isolation level | * -----------------|--------------------- - * Read Uncommitted | - * Read Committed | - * Repeatable Read | - * Serializable | + * Read Uncommitted | + + * Read Committed | + + * Repeatable Read | - + * Serializable | - */ @Test void noneRepeatable() throws SQLException { @@ -130,7 +131,7 @@ void noneRepeatable() throws SQLException { connection.setAutoCommit(false); // 적절한 격리 레벨을 찾는다. - final int isolationLevel = Connection.TRANSACTION_NONE; + final int isolationLevel = Connection.TRANSACTION_REPEATABLE_READ; // 트랜잭션 격리 레벨을 설정한다. connection.setTransactionIsolation(isolationLevel); @@ -173,16 +174,17 @@ void noneRepeatable() throws SQLException { * Read phenomena | Phantom reads * Isolation level | * -----------------|-------------- - * Read Uncommitted | - * Read Committed | - * Repeatable Read | - * Serializable | + * Read Uncommitted | + + * Read Committed | + + * Repeatable Read | + + * Serializable | - */ @Test void phantomReading() throws SQLException { // testcontainer로 docker를 실행해서 mysql에 연결한다. final var mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0.30")) + .withUrlParam("allowMultiQueries", "true") .withLogConsumer(new Slf4jLogConsumer(log)); mysql.start(); setUp(createMySQLDataSource(mysql)); @@ -197,7 +199,7 @@ void phantomReading() throws SQLException { connection.setAutoCommit(false); // 적절한 격리 레벨을 찾는다. - final int isolationLevel = Connection.TRANSACTION_NONE; + final int isolationLevel = Connection.TRANSACTION_SERIALIZABLE; // 트랜잭션 격리 레벨을 설정한다. connection.setTransactionIsolation(isolationLevel); diff --git a/study/src/test/java/transaction/stage2/FirstUserService.java b/study/src/test/java/transaction/stage2/FirstUserService.java index 9a1d415c18..4ea7fef8b1 100644 --- a/study/src/test/java/transaction/stage2/FirstUserService.java +++ b/study/src/test/java/transaction/stage2/FirstUserService.java @@ -66,7 +66,7 @@ public Set saveAndExceptionWithRequiredNew() { throw new RuntimeException(); } -// @Transactional(propagation = Propagation.REQUIRED) + @Transactional(propagation = Propagation.REQUIRED) public Set saveFirstTransactionWithSupports() { final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); userRepository.save(User.createTest()); @@ -77,7 +77,7 @@ public Set saveFirstTransactionWithSupports() { return of(firstTransactionName, secondTransactionName); } -// @Transactional(propagation = Propagation.REQUIRED) + @Transactional(propagation = Propagation.REQUIRED) public Set saveFirstTransactionWithMandatory() { final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); userRepository.save(User.createTest()); @@ -88,7 +88,7 @@ public Set saveFirstTransactionWithMandatory() { return of(firstTransactionName, secondTransactionName); } - @Transactional(propagation = Propagation.REQUIRED) +// @Transactional(propagation = Propagation.REQUIRED) public Set saveFirstTransactionWithNotSupported() { final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); userRepository.save(User.createTest()); @@ -99,7 +99,7 @@ public Set saveFirstTransactionWithNotSupported() { return of(firstTransactionName, secondTransactionName); } - @Transactional(propagation = Propagation.REQUIRED) +// @Transactional(propagation = Propagation.REQUIRED) public Set saveFirstTransactionWithNested() { final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); userRepository.save(User.createTest()); @@ -110,7 +110,7 @@ public Set saveFirstTransactionWithNested() { return of(firstTransactionName, secondTransactionName); } - @Transactional(propagation = Propagation.REQUIRED) +// @Transactional(propagation = Propagation.REQUIRED) public Set saveFirstTransactionWithNever() { final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); userRepository.save(User.createTest()); diff --git a/study/src/test/java/transaction/stage2/Stage2Test.java b/study/src/test/java/transaction/stage2/Stage2Test.java index e522bf4365..caca6f5a7c 100644 --- a/study/src/test/java/transaction/stage2/Stage2Test.java +++ b/study/src/test/java/transaction/stage2/Stage2Test.java @@ -45,13 +45,14 @@ void testRequired() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(1) + .containsExactly("transaction.stage2.FirstUserService.saveFirstTransactionWithRequired"); } /** * 생성된 트랜잭션이 몇 개인가? * 왜 그런 결과가 나왔을까? + * REQUIRED는 기존 트랜잭션에 합류한다. */ @Test void testRequiredNew() { @@ -59,8 +60,9 @@ void testRequiredNew() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(2) + .containsExactly("transaction.stage2.SecondUserService.saveSecondTransactionWithRequiresNew", + "transaction.stage2.FirstUserService.saveFirstTransactionWithRequiredNew"); } /** @@ -69,17 +71,19 @@ void testRequiredNew() { */ @Test void testRequiredNewWithRollback() { - assertThat(firstUserService.findAll()).hasSize(-1); + assertThat(firstUserService.findAll()).hasSize(0); assertThatThrownBy(() -> firstUserService.saveAndExceptionWithRequiredNew()) .isInstanceOf(RuntimeException.class); - assertThat(firstUserService.findAll()).hasSize(-1); + assertThat(firstUserService.findAll()).hasSize(1); } /** * FirstUserService.saveFirstTransactionWithSupports() 메서드를 보면 @Transactional이 주석으로 되어 있다. * 주석인 상태에서 테스트를 실행했을 때와 주석을 해제하고 테스트를 실행했을 때 어떤 차이점이 있는지 확인해보자. + * 찾아 보니 기존의 트랜잭션이 있으면 참여하고 없으면 트랜잭션 없이 진행한다는데, 기존의 트랜잭션 없이 해도 트랜잭션으로 진행하는 거 같다. + * Javadoc에 의하면 'SUPPORTS is slightly different from no transaction at all'라고 한다. 트랜잭션이 아예 없는 건 아닌듯 */ @Test void testSupports() { @@ -87,14 +91,15 @@ void testSupports() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(1) + .containsExactly("transaction.stage2.FirstUserService.saveFirstTransactionWithSupports"); } /** * FirstUserService.saveFirstTransactionWithMandatory() 메서드를 보면 @Transactional이 주석으로 되어 있다. * 주석인 상태에서 테스트를 실행했을 때와 주석을 해제하고 테스트를 실행했을 때 어떤 차이점이 있는지 확인해보자. * SUPPORTS와 어떤 점이 다른지도 같이 챙겨보자. + * TRANSACTION이 필수로 필요하다. 없으면 IllegalTransactionStateException 발생 */ @Test void testMandatory() { @@ -102,8 +107,8 @@ void testMandatory() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(1) + .containsExactly("transaction.stage2.FirstUserService.saveFirstTransactionWithMandatory"); } /** @@ -119,8 +124,8 @@ void testNotSupported() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(1) + .containsExactly("transaction.stage2.SecondUserService.saveSecondTransactionWithNotSupported"); } /** @@ -133,8 +138,8 @@ void testNested() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(1) + .containsExactly("transaction.stage2.SecondUserService.saveSecondTransactionWithNested"); } /** @@ -146,7 +151,7 @@ void testNever() { log.info("transactions : {}", actual); assertThat(actual) - .hasSize(0) - .containsExactly(""); + .hasSize(1) + .containsExactly("transaction.stage2.SecondUserService.saveSecondTransactionWithNever"); } } From 696a78774c8e672ecc21b303aeadcaf1bb7970c6 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 01:06:34 +0900 Subject: [PATCH 05/21] =?UTF-8?q?refactor:=20TransactionContext=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20TransactionSynchronizationMana?= =?UTF-8?q?ger=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jdbc/core/ConnectionManager.java | 14 +++++------ .../support/TransactionContext.java | 24 ------------------- .../TransactionSynchronizationManager.java | 14 +++++++---- .../support/TransactionTemplate.java | 4 ++-- 4 files changed, 19 insertions(+), 37 deletions(-) delete mode 100644 jdbc/src/main/java/org/springframework/transaction/support/TransactionContext.java diff --git a/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java b/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java index c32e04f9b4..04a059fd91 100644 --- a/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java +++ b/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java @@ -2,7 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.transaction.support.TransactionContext; +import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.sql.DataSource; import java.sql.Connection; @@ -19,10 +19,10 @@ public ConnectionManager(final DataSource dataSource) { } public Connection getConnection() { - if (TransactionContext.isEmpty()) { - return getNewConnection(); + if (TransactionSynchronizationManager.hasResource(dataSource)) { + return TransactionSynchronizationManager.getResource(dataSource); } - return TransactionContext.get(); + return getNewConnection(); } private Connection getNewConnection() { @@ -42,10 +42,10 @@ public void closeNotTransactional(Connection connection) { } private boolean isTransactional(final Connection connection) { - if (TransactionContext.isEmpty()) { - return false; + if (TransactionSynchronizationManager.hasResource(dataSource)) { + return TransactionSynchronizationManager.getResource(dataSource) == connection; } - return TransactionContext.get() == (connection); + return false; } private void close(final Connection connection) { diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionContext.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionContext.java deleted file mode 100644 index c8bd2738d1..0000000000 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionContext.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.springframework.transaction.support; - -import java.sql.Connection; - -public class TransactionContext { - - private static final ThreadLocal status = new ThreadLocal<>(); - - public static void set(final Connection connection) { - status.set(connection); - } - - public static Connection get() { - return status.get(); - } - - public static void remove() { - status.remove(); - } - - public static boolean isEmpty() { - return status.get() == null; - } -} diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java index 715557fc66..1cc2aae880 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java @@ -2,22 +2,28 @@ import javax.sql.DataSource; import java.sql.Connection; +import java.util.HashMap; import java.util.Map; public abstract class TransactionSynchronizationManager { - private static final ThreadLocal> resources = new ThreadLocal<>(); + private static final ThreadLocal> resources = ThreadLocal.withInitial(HashMap::new); private TransactionSynchronizationManager() {} public static Connection getResource(DataSource key) { - return null; + return resources.get().get(key); } public static void bindResource(DataSource key, Connection value) { + resources.get().put(key, value); } - public static Connection unbindResource(DataSource key) { - return null; + public static void unbindResource(DataSource key) { + resources.get().remove(key); + } + + public static boolean hasResource(DataSource dataSource) { + return resources.get().containsKey(dataSource); } } diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index b390ae4b91..327e6316fc 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -22,7 +22,7 @@ public void executeWithTransaction(final TransactionCallback transactionCallback Connection connection = null; try { connection = dataSource.getConnection(); - TransactionContext.set(connection); + TransactionSynchronizationManager.bindResource(dataSource, connection); connection.setAutoCommit(false); transactionCallback.execute(); connection.commit(); @@ -37,7 +37,7 @@ public void executeWithTransaction(final TransactionCallback transactionCallback } throw new UndeclaredThrowableException(e); } finally { - TransactionContext.remove(); + TransactionSynchronizationManager.unbindResource(dataSource); try { if (connection != null) { connection.close(); From 93261f4b898c7bcb5008c609d2dd5298134061f9 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 01:08:08 +0900 Subject: [PATCH 06/21] =?UTF-8?q?refactor:=20DataSourceUtils=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springframework/jdbc/core/ConnectionManager.java | 11 ++--------- .../jdbc/datasource/DataSourceUtils.java | 9 ++++----- .../transaction/support/TransactionTemplate.java | 12 ++++-------- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java b/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java index 04a059fd91..2777958272 100644 --- a/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java +++ b/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.sql.DataSource; @@ -38,7 +39,7 @@ public void closeNotTransactional(Connection connection) { if (isTransactional(connection)) { return; } - close(connection); + DataSourceUtils.releaseConnection(connection); } private boolean isTransactional(final Connection connection) { @@ -48,12 +49,4 @@ private boolean isTransactional(final Connection connection) { return false; } - private void close(final Connection connection) { - try { - connection.close(); - } catch (SQLException e) { - log.error(e.getMessage(), e); - throw new RuntimeException(e); - } - } } diff --git a/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java b/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java index 3c40bfec52..c5e17046a7 100644 --- a/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java +++ b/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java @@ -13,13 +13,12 @@ public abstract class DataSourceUtils { private DataSourceUtils() {} public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { - Connection connection = TransactionSynchronizationManager.getResource(dataSource); - if (connection != null) { - return connection; + if (TransactionSynchronizationManager.hasResource(dataSource)) { + return TransactionSynchronizationManager.getResource(dataSource); } try { - connection = dataSource.getConnection(); + Connection connection = dataSource.getConnection(); TransactionSynchronizationManager.bindResource(dataSource, connection); return connection; } catch (SQLException ex) { @@ -27,7 +26,7 @@ public static Connection getConnection(DataSource dataSource) throws CannotGetJd } } - public static void releaseConnection(Connection connection, DataSource dataSource) { + public static void releaseConnection(Connection connection) { try { connection.close(); } catch (SQLException ex) { diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index 327e6316fc..fcca3fe79a 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.exception.UndeclaredThrowableException; +import org.springframework.jdbc.datasource.DataSourceUtils; import javax.sql.DataSource; import java.sql.Connection; @@ -21,8 +22,7 @@ public TransactionTemplate(final DataSource dataSource) { public void executeWithTransaction(final TransactionCallback transactionCallback) { Connection connection = null; try { - connection = dataSource.getConnection(); - TransactionSynchronizationManager.bindResource(dataSource, connection); + connection = DataSourceUtils.getConnection(dataSource); connection.setAutoCommit(false); transactionCallback.execute(); connection.commit(); @@ -38,12 +38,8 @@ public void executeWithTransaction(final TransactionCallback transactionCallback throw new UndeclaredThrowableException(e); } finally { TransactionSynchronizationManager.unbindResource(dataSource); - try { - if (connection != null) { - connection.close(); - } - } catch (final SQLException e) { - throw new RuntimeException(e); + if (connection != null) { + DataSourceUtils.releaseConnection(connection); } } } From 94d40f9d32a7bde576cef5ed410a57455bd18350 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 02:31:37 +0900 Subject: [PATCH 07/21] =?UTF-8?q?refactor:=20UserService=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../techcourse/service/AppUserService.java | 34 ++++++++++++++++ .../com/techcourse/service/TxUserService.java | 34 ++++++++++++++++ .../com/techcourse/service/UserService.java | 40 ++----------------- 3 files changed, 72 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/com/techcourse/service/AppUserService.java create mode 100644 app/src/main/java/com/techcourse/service/TxUserService.java diff --git a/app/src/main/java/com/techcourse/service/AppUserService.java b/app/src/main/java/com/techcourse/service/AppUserService.java new file mode 100644 index 0000000000..45522c4ec8 --- /dev/null +++ b/app/src/main/java/com/techcourse/service/AppUserService.java @@ -0,0 +1,34 @@ +package com.techcourse.service; + +import com.techcourse.dao.UserDao; +import com.techcourse.dao.UserHistoryDao; +import com.techcourse.domain.User; +import com.techcourse.domain.UserHistory; + +public class AppUserService implements UserService { + + private final UserDao userDao; + private final UserHistoryDao userHistoryDao; + + public AppUserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { + this.userDao = userDao; + this.userHistoryDao = userHistoryDao; + + } + + public User findById(final long id) { + return userDao.findById(id) + .orElseThrow(() -> new IllegalArgumentException("유저가 존재하지 않습니다.")); + } + + public void insert(final User user) { + userDao.insert(user); + } + + public void changePassword(final long id, final String newPassword, final String createBy) { + final var user = findById(id); + user.changePassword(newPassword); + userDao.update(user); + userHistoryDao.log(new UserHistory(user, createBy)); + } +} diff --git a/app/src/main/java/com/techcourse/service/TxUserService.java b/app/src/main/java/com/techcourse/service/TxUserService.java new file mode 100644 index 0000000000..c8a29e3b35 --- /dev/null +++ b/app/src/main/java/com/techcourse/service/TxUserService.java @@ -0,0 +1,34 @@ +package com.techcourse.service; + +import com.techcourse.config.DataSourceConfig; +import com.techcourse.domain.User; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; + +public class TxUserService implements UserService { + + private final UserService userService; + private final TransactionTemplate transactionTemplate; + + public TxUserService(final UserService userService) { + this.userService = userService; + final DataSource dataSource = DataSourceConfig.getInstance(); + this.transactionTemplate = new TransactionTemplate(dataSource); + } + + @Override + public User findById(final long id) { + return null; + } + + @Override + public void insert(final User user) { + transactionTemplate.executeWithTransaction(() -> userService.insert(user)); + } + + @Override + public void changePassword(final long id, final String newPassword, final String createBy) { + transactionTemplate.executeWithTransaction(() -> userService.changePassword(id, newPassword, createBy)); + } +} diff --git a/app/src/main/java/com/techcourse/service/UserService.java b/app/src/main/java/com/techcourse/service/UserService.java index 04304f373e..42d01bf760 100644 --- a/app/src/main/java/com/techcourse/service/UserService.java +++ b/app/src/main/java/com/techcourse/service/UserService.java @@ -1,42 +1,10 @@ package com.techcourse.service; -import com.techcourse.config.DataSourceConfig; -import com.techcourse.dao.UserDao; -import com.techcourse.dao.UserHistoryDao; import com.techcourse.domain.User; -import com.techcourse.domain.UserHistory; -import org.springframework.transaction.support.TransactionTemplate; -import javax.sql.DataSource; +public interface UserService { -public class UserService { - - private final UserDao userDao; - private final UserHistoryDao userHistoryDao; - private final TransactionTemplate transactionTemplate; - - public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { - this.userDao = userDao; - this.userHistoryDao = userHistoryDao; - final DataSource dataSource = DataSourceConfig.getInstance(); - this.transactionTemplate = new TransactionTemplate(dataSource); - } - - public User findById(final long id) { - return userDao.findById(id) - .orElseThrow(() -> new IllegalArgumentException("유저가 존재하지 않습니다.")); - } - - public void insert(final User user) { - userDao.insert(user); - } - - public void changePassword(final long id, final String newPassword, final String createBy) { - transactionTemplate.executeWithTransaction(() -> { - final var user = findById(id); - user.changePassword(newPassword); - userDao.update(user); - userHistoryDao.log(new UserHistory(user, createBy)); - }); - } + User findById(final long id); + void insert(final User user); + void changePassword(final long id, final String newPassword, final String createBy); } From 055eb1b956153ca9be8b69a2348339fba0193272 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 02:42:22 +0900 Subject: [PATCH 08/21] =?UTF-8?q?refactor:=20TransactionCallback=EC=9D=98?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=20=ED=83=80=EC=9E=85=EC=9D=84=20=EC=A0=9C?= =?UTF-8?q?=EB=84=A4=EB=A6=AD=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/techcourse/service/TxUserService.java | 12 +++++++++--- .../java/com/techcourse/service/UserServiceTest.java | 7 +++++-- .../transaction/support/TransactionCallback.java | 4 ++-- .../transaction/support/TransactionTemplate.java | 5 +++-- .../transaction/support/TransactionTemplateTest.java | 4 ++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/techcourse/service/TxUserService.java b/app/src/main/java/com/techcourse/service/TxUserService.java index c8a29e3b35..43466f8942 100644 --- a/app/src/main/java/com/techcourse/service/TxUserService.java +++ b/app/src/main/java/com/techcourse/service/TxUserService.java @@ -19,16 +19,22 @@ public TxUserService(final UserService userService) { @Override public User findById(final long id) { - return null; + return transactionTemplate.executeWithTransaction(() -> userService.findById(id)); } @Override public void insert(final User user) { - transactionTemplate.executeWithTransaction(() -> userService.insert(user)); + transactionTemplate.executeWithTransaction(() -> { + userService.insert(user); + return null; + }); } @Override public void changePassword(final long id, final String newPassword, final String createBy) { - transactionTemplate.executeWithTransaction(() -> userService.changePassword(id, newPassword, createBy)); + transactionTemplate.executeWithTransaction(() -> { + userService.changePassword(id, newPassword, createBy); + return null; + }); } } diff --git a/app/src/test/java/com/techcourse/service/UserServiceTest.java b/app/src/test/java/com/techcourse/service/UserServiceTest.java index dbf94783e6..f5dda6dfe9 100644 --- a/app/src/test/java/com/techcourse/service/UserServiceTest.java +++ b/app/src/test/java/com/techcourse/service/UserServiceTest.java @@ -32,7 +32,7 @@ void setUp() { @Test void testChangePassword() { final var userHistoryDao = new UserHistoryDao(jdbcTemplate); - final var userService = new UserService(userDao, userHistoryDao); + final var userService = new AppUserService(userDao, userHistoryDao); final var newPassword = "qqqqq"; final var createBy = "gugu"; @@ -47,7 +47,10 @@ void testChangePassword() { void testTransactionRollback() { // 트랜잭션 롤백 테스트를 위해 mock으로 교체 final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate); - final var userService = new UserService(userDao, userHistoryDao); + // 애플리케이션 서비스 + final var appUserService = new AppUserService(userDao, userHistoryDao); + // 트랜잭션 서비스 추상화 + final var userService = new TxUserService(appUserService); final User user = userDao.findById(1L).get(); final var oldPassword = user.getPassword(); diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java index c85947e3ee..8864ebfaf2 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java @@ -1,7 +1,7 @@ package org.springframework.transaction.support; @FunctionalInterface -public interface TransactionCallback { +public interface TransactionCallback { - void execute(); + T execute(); } diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index fcca3fe79a..16b34e5d3b 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -19,13 +19,14 @@ public TransactionTemplate(final DataSource dataSource) { this.dataSource = dataSource; } - public void executeWithTransaction(final TransactionCallback transactionCallback) { + public T executeWithTransaction(final TransactionCallback transactionCallback) { Connection connection = null; try { connection = DataSourceUtils.getConnection(dataSource); connection.setAutoCommit(false); - transactionCallback.execute(); + final T result = transactionCallback.execute(); connection.commit(); + return result; } catch (final RuntimeException e) { if (connection != null) { rollback(connection); diff --git a/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java b/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java index 84da8dcb35..17c7734f1f 100644 --- a/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java +++ b/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java @@ -27,7 +27,7 @@ void setUp() throws SQLException { @Test void executeWithTransaction() throws SQLException { // given - TransactionCallback transactionCallback = () -> System.out.println("성공"); + TransactionCallback transactionCallback = () -> "성공"; // when transactionTemplate.executeWithTransaction(transactionCallback); @@ -42,7 +42,7 @@ void executeWithTransaction() throws SQLException { void executeWithTransaction_RuntimeExceptionThrown_RollbackAndSameExceptionThrown() throws SQLException { // given RuntimeException exception = new DataAccessException("실패"); - TransactionCallback transactionCallback = () -> { + TransactionCallback transactionCallback = () -> { throw exception; }; From 34ffba0d7b6bde5bd8d35a840990f11ba1a62272 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 02:43:01 +0900 Subject: [PATCH 09/21] =?UTF-8?q?feat:=20Nullable=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/support/Nullable.java | 13 +++++++++++++ .../transaction/support/TransactionCallback.java | 1 + .../transaction/support/TransactionTemplate.java | 1 + 3 files changed, 15 insertions(+) create mode 100644 jdbc/src/main/java/org/springframework/transaction/support/Nullable.java diff --git a/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java b/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java new file mode 100644 index 0000000000..1dec1c3dc8 --- /dev/null +++ b/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java @@ -0,0 +1,13 @@ +package org.springframework.transaction.support; + +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierNickname; +import javax.annotation.meta.When; +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Nonnull(when = When.MAYBE) +@TypeQualifierNickname +public @interface Nullable { +} diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java index 8864ebfaf2..9f119dd415 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java @@ -3,5 +3,6 @@ @FunctionalInterface public interface TransactionCallback { + @Nullable T execute(); } diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index 16b34e5d3b..9c5cd63919 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -19,6 +19,7 @@ public TransactionTemplate(final DataSource dataSource) { this.dataSource = dataSource; } + @Nullable public T executeWithTransaction(final TransactionCallback transactionCallback) { Connection connection = null; try { From 3c484ec148cf6e021c0d7fe2c20b83a0fb76550f Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 21:54:46 +0900 Subject: [PATCH 10/21] =?UTF-8?q?docs:=204=EB=8B=A8=EA=B3=84=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=82=B4=EC=9A=A9=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index c78ccad0d1..80b1c5b166 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,11 @@ ### 3차 - [x] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) + +## 4단계 리뷰 +- [ ] AppUserService 생성자 빈칸 제거 +- [ ] TxUserService TransactionTemplate 생성자로 받도록 수정 +- [ ] TransactionTemplate 반환값 없는 메서드 만들기 +- [ ] Nullable TypeQualifierNickname 어떤 효과? +- [ ] TransactionSyncrhonizationManager 중복되는 get() 제거 +- [ ] ConnectionManager 제거 리팩토링 From e205b8f4aa81dd3fc6badceddc46457b06c8d17e Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 21:55:08 +0900 Subject: [PATCH 11/21] =?UTF-8?q?style:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B3=B5=EB=B0=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- app/src/main/java/com/techcourse/service/AppUserService.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 80b1c5b166..1f17e25335 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ - [x] EOF 해결(TransactionTemplateTest, PreparedStatementCreatorTest) ## 4단계 리뷰 -- [ ] AppUserService 생성자 빈칸 제거 +- [x] AppUserService 생성자 빈칸 제거 - [ ] TxUserService TransactionTemplate 생성자로 받도록 수정 - [ ] TransactionTemplate 반환값 없는 메서드 만들기 - [ ] Nullable TypeQualifierNickname 어떤 효과? diff --git a/app/src/main/java/com/techcourse/service/AppUserService.java b/app/src/main/java/com/techcourse/service/AppUserService.java index 45522c4ec8..1dfc9bdb08 100644 --- a/app/src/main/java/com/techcourse/service/AppUserService.java +++ b/app/src/main/java/com/techcourse/service/AppUserService.java @@ -13,7 +13,6 @@ public class AppUserService implements UserService { public AppUserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { this.userDao = userDao; this.userHistoryDao = userHistoryDao; - } public User findById(final long id) { From df7e776995daa5357fc071623aee34a02bbf4ccb Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 21:58:06 +0900 Subject: [PATCH 12/21] =?UTF-8?q?refactor:=20TxUserService=EC=9D=98=20Tran?= =?UTF-8?q?sactionTemplate=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/com/techcourse/service/TxUserService.java | 8 ++------ .../java/com/techcourse/service/UserServiceTest.java | 10 ++++++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1f17e25335..f39c387342 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ ## 4단계 리뷰 - [x] AppUserService 생성자 빈칸 제거 -- [ ] TxUserService TransactionTemplate 생성자로 받도록 수정 +- [x] TxUserService TransactionTemplate 생성자로 받도록 수정 - [ ] TransactionTemplate 반환값 없는 메서드 만들기 - [ ] Nullable TypeQualifierNickname 어떤 효과? - [ ] TransactionSyncrhonizationManager 중복되는 get() 제거 diff --git a/app/src/main/java/com/techcourse/service/TxUserService.java b/app/src/main/java/com/techcourse/service/TxUserService.java index 43466f8942..7065ae2261 100644 --- a/app/src/main/java/com/techcourse/service/TxUserService.java +++ b/app/src/main/java/com/techcourse/service/TxUserService.java @@ -1,20 +1,16 @@ package com.techcourse.service; -import com.techcourse.config.DataSourceConfig; import com.techcourse.domain.User; import org.springframework.transaction.support.TransactionTemplate; -import javax.sql.DataSource; - public class TxUserService implements UserService { private final UserService userService; private final TransactionTemplate transactionTemplate; - public TxUserService(final UserService userService) { + public TxUserService(final UserService userService, final TransactionTemplate transactionTemplate) { this.userService = userService; - final DataSource dataSource = DataSourceConfig.getInstance(); - this.transactionTemplate = new TransactionTemplate(dataSource); + this.transactionTemplate = transactionTemplate; } @Override diff --git a/app/src/test/java/com/techcourse/service/UserServiceTest.java b/app/src/test/java/com/techcourse/service/UserServiceTest.java index f5dda6dfe9..835ad51f23 100644 --- a/app/src/test/java/com/techcourse/service/UserServiceTest.java +++ b/app/src/test/java/com/techcourse/service/UserServiceTest.java @@ -9,6 +9,9 @@ import org.junit.jupiter.api.Test; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @@ -18,11 +21,14 @@ class UserServiceTest { private JdbcTemplate jdbcTemplate; private UserDao userDao; + private TransactionTemplate transactionTemplate; @BeforeEach void setUp() { - this.jdbcTemplate = new JdbcTemplate(DataSourceConfig.getInstance()); + DataSource dataSource = DataSourceConfig.getInstance(); + this.jdbcTemplate = new JdbcTemplate(dataSource); this.userDao = new UserDao(jdbcTemplate); + this.transactionTemplate = new TransactionTemplate(dataSource); DatabasePopulatorUtils.execute(DataSourceConfig.getInstance()); final var user = new User("gugu", "password", "hkkang@woowahan.com"); @@ -50,7 +56,7 @@ void testTransactionRollback() { // 애플리케이션 서비스 final var appUserService = new AppUserService(userDao, userHistoryDao); // 트랜잭션 서비스 추상화 - final var userService = new TxUserService(appUserService); + final var userService = new TxUserService(appUserService, transactionTemplate); final User user = userDao.findById(1L).get(); final var oldPassword = user.getPassword(); From b33cfd1fdeefbd4a30f16058d1a9a6ed95c5b5f2 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 22:22:22 +0900 Subject: [PATCH 13/21] =?UTF-8?q?feat(TransactionTemplate):=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EA=B0=92=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../com/techcourse/service/TxUserService.java | 10 ++-------- .../transaction/support/TransactionTemplate.java | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f39c387342..c3e400db23 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ ## 4단계 리뷰 - [x] AppUserService 생성자 빈칸 제거 - [x] TxUserService TransactionTemplate 생성자로 받도록 수정 -- [ ] TransactionTemplate 반환값 없는 메서드 만들기 +- [x] TransactionTemplate 반환값 없는 메서드 만들기 - [ ] Nullable TypeQualifierNickname 어떤 효과? - [ ] TransactionSyncrhonizationManager 중복되는 get() 제거 - [ ] ConnectionManager 제거 리팩토링 diff --git a/app/src/main/java/com/techcourse/service/TxUserService.java b/app/src/main/java/com/techcourse/service/TxUserService.java index 7065ae2261..2ddc473083 100644 --- a/app/src/main/java/com/techcourse/service/TxUserService.java +++ b/app/src/main/java/com/techcourse/service/TxUserService.java @@ -20,17 +20,11 @@ public User findById(final long id) { @Override public void insert(final User user) { - transactionTemplate.executeWithTransaction(() -> { - userService.insert(user); - return null; - }); + transactionTemplate.executeWithoutResult(() -> userService.insert(user)); } @Override public void changePassword(final long id, final String newPassword, final String createBy) { - transactionTemplate.executeWithTransaction(() -> { - userService.changePassword(id, newPassword, createBy); - return null; - }); + transactionTemplate.executeWithoutResult(() -> userService.changePassword(id, newPassword, createBy)); } } diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index 9c5cd63919..d3637c03fe 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -39,9 +39,8 @@ public T executeWithTransaction(final TransactionCallback transactionCall } throw new UndeclaredThrowableException(e); } finally { - TransactionSynchronizationManager.unbindResource(dataSource); if (connection != null) { - DataSourceUtils.releaseConnection(connection); + closeTransactional(connection); } } } @@ -54,4 +53,16 @@ private void rollback(final Connection connection) { throw new RuntimeException(e); } } + + private void closeTransactional(final Connection connection) { + TransactionSynchronizationManager.unbindResource(dataSource); + DataSourceUtils.releaseConnection(connection); + } + + public void executeWithoutResult(final Runnable action) { + executeWithTransaction(() -> { + action.run(); + return null; + }); + } } From 36fbd06db9c0feae41b91dc2db2e06396ab68465 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 22:28:18 +0900 Subject: [PATCH 14/21] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=EB=90=98?= =?UTF-8?q?=EB=8A=94=20resources.get()=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../support/TransactionSynchronizationManager.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c3e400db23..d9080124f1 100644 --- a/README.md +++ b/README.md @@ -56,5 +56,5 @@ - [x] TxUserService TransactionTemplate 생성자로 받도록 수정 - [x] TransactionTemplate 반환값 없는 메서드 만들기 - [ ] Nullable TypeQualifierNickname 어떤 효과? -- [ ] TransactionSyncrhonizationManager 중복되는 get() 제거 +- [x] TransactionSyncrhonizationManager 중복되는 get() 제거 - [ ] ConnectionManager 제거 리팩토링 diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java index 1cc2aae880..c33fec26bc 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java @@ -11,19 +11,23 @@ public abstract class TransactionSynchronizationManager { private TransactionSynchronizationManager() {} + private static Map currentResource() { + return resources.get(); + } + public static Connection getResource(DataSource key) { - return resources.get().get(key); + return currentResource().get(key); } public static void bindResource(DataSource key, Connection value) { - resources.get().put(key, value); + currentResource().put(key, value); } public static void unbindResource(DataSource key) { - resources.get().remove(key); + currentResource().remove(key); } public static boolean hasResource(DataSource dataSource) { - return resources.get().containsKey(dataSource); + return currentResource().containsKey(dataSource); } } From 7e30ceff6d38a3e91269369e6b83a5ea113470d6 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 6 Oct 2023 22:29:29 +0900 Subject: [PATCH 15/21] =?UTF-8?q?style:=20import=20=EC=99=80=EC=9D=BC?= =?UTF-8?q?=EB=93=9C=EC=B9=B4=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../org/springframework/transaction/support/Nullable.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d9080124f1..c407787e3d 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,6 @@ - [x] AppUserService 생성자 빈칸 제거 - [x] TxUserService TransactionTemplate 생성자로 받도록 수정 - [x] TransactionTemplate 반환값 없는 메서드 만들기 -- [ ] Nullable TypeQualifierNickname 어떤 효과? +- [x] Nullable TypeQualifierNickname 어떤 효과? - [x] TransactionSyncrhonizationManager 중복되는 get() 제거 - [ ] ConnectionManager 제거 리팩토링 diff --git a/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java b/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java index 1dec1c3dc8..d500bb2b4c 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java @@ -3,7 +3,10 @@ import javax.annotation.Nonnull; import javax.annotation.meta.TypeQualifierNickname; import javax.annotation.meta.When; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) From 3438d8dd41f8f6158e86f7a2230f0c0330d27669 Mon Sep 17 00:00:00 2001 From: yujamint Date: Mon, 9 Oct 2023 15:42:18 +0900 Subject: [PATCH 16/21] =?UTF-8?q?refactor:=20ConnectionManager=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20DataSourceUtils=EB=A1=9C=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++++- .../jdbc/core/ConnectionManager.java | 52 ------------------- .../jdbc/core/JdbcTemplate.java | 9 ++-- .../jdbc/datasource/DataSourceUtils.java | 15 ++++++ 4 files changed, 36 insertions(+), 57 deletions(-) delete mode 100644 jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java diff --git a/README.md b/README.md index c407787e3d..4ff09043f0 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,19 @@ - [x] TransactionTemplate 반환값 없는 메서드 만들기 - [x] Nullable TypeQualifierNickname 어떤 효과? - [x] TransactionSyncrhonizationManager 중복되는 get() 제거 -- [ ] ConnectionManager 제거 리팩토링 +- [x] ConnectionManager 제거 리팩토링 + - 현재 흐름 + - Transaction + 1. TransactionTemplate에게 요청 + 2. DataSourceUtils -> Connection 생성 및 ThreadLocal 등록 + 3. TransactionTemplate 트랜잭션 시작하며 로직 수행 -> JdbcTemplate 호출 + 4. JdbcTemplate은 ConnectionManager에게 Connection 요청 + 5. ConnectionManager는 ThreadLocal의 Connection 반환 + 6. JdbcTemplate DB 접근 끝 + 7. TransactionTemplate 로직 끝 -> 커밋 + 8. DataSourceUtils 통해 Connection 자원 반환, ThreadLocal에서도 제거 + - Transaction X + 1. JdbcTemplate은 ConnectionManager에게 Connection 요청 + 2. ConnectionManager는 Connection 새로 생성해서 반환 + 3. JdbcTemplate DB 접근 끝 + 4. ConnectionManager가 Connection 자원 반환 diff --git a/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java b/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java deleted file mode 100644 index 2777958272..0000000000 --- a/jdbc/src/main/java/org/springframework/jdbc/core/ConnectionManager.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.springframework.jdbc.core; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jdbc.datasource.DataSourceUtils; -import org.springframework.transaction.support.TransactionSynchronizationManager; - -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.SQLException; - -public class ConnectionManager { - - private static final Logger log = LoggerFactory.getLogger(ConnectionManager.class); - - private final DataSource dataSource; - - public ConnectionManager(final DataSource dataSource) { - this.dataSource = dataSource; - } - - public Connection getConnection() { - if (TransactionSynchronizationManager.hasResource(dataSource)) { - return TransactionSynchronizationManager.getResource(dataSource); - } - return getNewConnection(); - } - - private Connection getNewConnection() { - try { - return dataSource.getConnection(); - } catch (SQLException e) { - log.error(e.getMessage(), e); - throw new RuntimeException(e); - } - } - - public void closeNotTransactional(Connection connection) { - if (isTransactional(connection)) { - return; - } - DataSourceUtils.releaseConnection(connection); - } - - private boolean isTransactional(final Connection connection) { - if (TransactionSynchronizationManager.hasResource(dataSource)) { - return TransactionSynchronizationManager.getResource(dataSource) == connection; - } - return false; - } - -} diff --git a/jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java b/jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java index 9343c3acdb..2aa5ecb886 100644 --- a/jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java +++ b/jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.jdbc.datasource.DataSourceUtils; import javax.sql.DataSource; import java.sql.Connection; @@ -17,10 +18,10 @@ public class JdbcTemplate { private static final Logger log = LoggerFactory.getLogger(JdbcTemplate.class); private final PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator(); - private final ConnectionManager connectionManager; + private final DataSource dataSource; public JdbcTemplate(final DataSource dataSource) { - this.connectionManager = new ConnectionManager(dataSource); + this.dataSource = dataSource; } public Optional queryForObject(final String sql, final RowMapper rowMapper, final Object... args) { @@ -28,14 +29,14 @@ public Optional queryForObject(final String sql, final RowMapper rowMa } private T executeQuery(final String sql, final SqlExecutor executor, final Object... args) { - final Connection connection = connectionManager.getConnection(); + final Connection connection = DataSourceUtils.getConnection(dataSource); try (final PreparedStatement preparedStatement = preparedStatementCreator.createPreparedStatement(connection, sql, args)) { return executor.execute(preparedStatement); } catch (final SQLException e) { log.error(e.getMessage(), e); throw new RuntimeException(e); } finally { - connectionManager.closeNotTransactional(connection); + DataSourceUtils.closeNotTransactional(dataSource, connection); } } diff --git a/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java b/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java index c5e17046a7..02413286af 100644 --- a/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java +++ b/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java @@ -33,4 +33,19 @@ public static void releaseConnection(Connection connection) { throw new CannotGetJdbcConnectionException("Failed to close JDBC Connection"); } } + + public static void closeNotTransactional(final DataSource dataSource, final Connection connection) { + if (isTransactional(dataSource, connection)) { + return; + } + TransactionSynchronizationManager.unbindResource(dataSource); + releaseConnection(connection); + } + + private static boolean isTransactional(final DataSource dataSource, final Connection connection) { + if (TransactionSynchronizationManager.hasResource(dataSource)) { + return TransactionSynchronizationManager.getResource(dataSource) == connection; + } + return false; + } } From 5ad575ea6a36d55797fa42d872a91d8359cdb963 Mon Sep 17 00:00:00 2001 From: yujamint Date: Mon, 9 Oct 2023 16:22:51 +0900 Subject: [PATCH 17/21] =?UTF-8?q?test:=20=ED=95=99=EC=8A=B5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=A1=9C=EA=B9=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/transaction/stage2/FirstUserService.java | 5 ++++- .../src/test/java/transaction/stage2/SecondUserService.java | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/study/src/test/java/transaction/stage2/FirstUserService.java b/study/src/test/java/transaction/stage2/FirstUserService.java index 4ea7fef8b1..1ab86ec269 100644 --- a/study/src/test/java/transaction/stage2/FirstUserService.java +++ b/study/src/test/java/transaction/stage2/FirstUserService.java @@ -110,7 +110,7 @@ public Set saveFirstTransactionWithNested() { return of(firstTransactionName, secondTransactionName); } -// @Transactional(propagation = Propagation.REQUIRED) + @Transactional(propagation = Propagation.REQUIRED) public Set saveFirstTransactionWithNever() { final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); userRepository.save(User.createTest()); @@ -130,7 +130,10 @@ private Set of(final String firstTransactionName, final String secondTra private void logActualTransactionActive() { final var currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); final var actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive(); + final var synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive(); final var emoji = actualTransactionActive ? "✅" : "❌"; + final var emoji2 = synchronizationActive ? "✅" : "❌"; log.info("\n{} is Actual Transaction Active : {} {}", currentTransactionName, emoji, actualTransactionActive); + log.info("\n{} is Synchronization Transaction Active : {} {}", currentTransactionName, emoji2, synchronizationActive); } } diff --git a/study/src/test/java/transaction/stage2/SecondUserService.java b/study/src/test/java/transaction/stage2/SecondUserService.java index 0d240fe854..4c9101daf0 100644 --- a/study/src/test/java/transaction/stage2/SecondUserService.java +++ b/study/src/test/java/transaction/stage2/SecondUserService.java @@ -70,7 +70,10 @@ public String saveSecondTransactionWithNever() { private void logActualTransactionActive() { final var currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); final var actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive(); + final var synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive(); final var emoji = actualTransactionActive ? "✅" : "❌"; + final var emoji2 = synchronizationActive ? "✅" : "❌"; log.info("\n{} is Actual Transaction Active : {} {}", currentTransactionName, emoji, actualTransactionActive); + log.info("\n{} is Synchronization Transaction Active : {} {}", currentTransactionName, emoji2, synchronizationActive); } } From 13f7d0355869122a6f941f4d745a56632a043d63 Mon Sep 17 00:00:00 2001 From: yujamint Date: Mon, 9 Oct 2023 17:38:50 +0900 Subject: [PATCH 18/21] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/springframework/transaction/support/Nullable.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java b/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java index d500bb2b4c..02ce941e66 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/Nullable.java @@ -1,7 +1,6 @@ package org.springframework.transaction.support; import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierNickname; import javax.annotation.meta.When; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -11,6 +10,5 @@ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Nonnull(when = When.MAYBE) -@TypeQualifierNickname public @interface Nullable { } From d0e93ca3d32c827485628aee209a23df06318bb1 Mon Sep 17 00:00:00 2001 From: yujamint Date: Tue, 10 Oct 2023 09:40:59 +0900 Subject: [PATCH 19/21] =?UTF-8?q?docs:=204=EB=8B=A8=EA=B3=84=202=EC=B0=A8?= =?UTF-8?q?=20=EB=A6=AC=EB=B7=B0=20=EB=82=B4=EC=9A=A9=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4ff09043f0..bf0c4f0e45 100644 --- a/README.md +++ b/README.md @@ -73,3 +73,7 @@ 2. ConnectionManager는 Connection 새로 생성해서 반환 3. JdbcTemplate DB 접근 끝 4. ConnectionManager가 Connection 자원 반환 + +### 2차 +- [ ] TransactionCallback 대신 Supplier 사용 +- [ ] isTransactional 문제 해결 From c11f4f496d63848022f37c0287d80dbd2af7c8c6 Mon Sep 17 00:00:00 2001 From: yujamint Date: Tue, 10 Oct 2023 09:47:03 +0900 Subject: [PATCH 20/21] =?UTF-8?q?refactor:=20TransactionCallback=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=ED=9B=84=20=ED=91=9C=EC=A4=80=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=ED=98=95=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../transaction/support/TransactionCallback.java | 8 -------- .../transaction/support/TransactionTemplate.java | 5 +++-- .../transaction/support/TransactionTemplateTest.java | 9 +++++---- 4 files changed, 9 insertions(+), 15 deletions(-) delete mode 100644 jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java diff --git a/README.md b/README.md index bf0c4f0e45..45d9c83057 100644 --- a/README.md +++ b/README.md @@ -75,5 +75,5 @@ 4. ConnectionManager가 Connection 자원 반환 ### 2차 -- [ ] TransactionCallback 대신 Supplier 사용 +- [x] TransactionCallback 대신 Supplier 사용 - [ ] isTransactional 문제 해결 diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java deleted file mode 100644 index 9f119dd415..0000000000 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionCallback.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.springframework.transaction.support; - -@FunctionalInterface -public interface TransactionCallback { - - @Nullable - T execute(); -} diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index d3637c03fe..b24ab46983 100644 --- a/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -8,6 +8,7 @@ import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.util.function.Supplier; public class TransactionTemplate { @@ -20,12 +21,12 @@ public TransactionTemplate(final DataSource dataSource) { } @Nullable - public T executeWithTransaction(final TransactionCallback transactionCallback) { + public T executeWithTransaction(final Supplier supplier) { Connection connection = null; try { connection = DataSourceUtils.getConnection(dataSource); connection.setAutoCommit(false); - final T result = transactionCallback.execute(); + final T result = supplier.get(); connection.commit(); return result; } catch (final RuntimeException e) { diff --git a/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java b/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java index 17c7734f1f..064f8f88ee 100644 --- a/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java +++ b/jdbc/src/test/java/org/springframework/transaction/support/TransactionTemplateTest.java @@ -7,6 +7,7 @@ import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.*; @@ -27,10 +28,10 @@ void setUp() throws SQLException { @Test void executeWithTransaction() throws SQLException { // given - TransactionCallback transactionCallback = () -> "성공"; + Supplier supplier = () -> "성공"; // when - transactionTemplate.executeWithTransaction(transactionCallback); + transactionTemplate.executeWithTransaction(supplier); // then verify(connection, times(1)).setAutoCommit(false); @@ -42,12 +43,12 @@ void executeWithTransaction() throws SQLException { void executeWithTransaction_RuntimeExceptionThrown_RollbackAndSameExceptionThrown() throws SQLException { // given RuntimeException exception = new DataAccessException("실패"); - TransactionCallback transactionCallback = () -> { + Supplier supplier = () -> { throw exception; }; // when, then - assertThatThrownBy(() -> transactionTemplate.executeWithTransaction(transactionCallback)) + assertThatThrownBy(() -> transactionTemplate.executeWithTransaction(supplier)) .isInstanceOf(exception.getClass()); verify(connection, times(1)).setAutoCommit(false); verify(connection, times(1)).rollback(); From c2efb84ea7b67cd2b7fa4a79b6d0f07db931b143 Mon Sep 17 00:00:00 2001 From: yujamint Date: Fri, 13 Oct 2023 01:26:00 +0900 Subject: [PATCH 21/21] =?UTF-8?q?fix:=20autoCommit=20=EB=AA=A8=EB=93=9C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Connection=EC=9D=B4=20=EB=8B=AB=ED=9E=88?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../jdbc/datasource/DataSourceUtils.java | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 45d9c83057..da097f9dcd 100644 --- a/README.md +++ b/README.md @@ -76,4 +76,4 @@ ### 2차 - [x] TransactionCallback 대신 Supplier 사용 -- [ ] isTransactional 문제 해결 +- [x] isTransactional 문제 해결 diff --git a/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java b/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java index 02413286af..95ed9947b8 100644 --- a/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java +++ b/jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java @@ -35,17 +35,14 @@ public static void releaseConnection(Connection connection) { } public static void closeNotTransactional(final DataSource dataSource, final Connection connection) { - if (isTransactional(dataSource, connection)) { - return; + try { + if (connection.getAutoCommit()) { + TransactionSynchronizationManager.unbindResource(dataSource); + releaseConnection(connection); + } + } catch (SQLException e) { + throw new CannotGetJdbcConnectionException("Failed to close JDBC Connection"); } - TransactionSynchronizationManager.unbindResource(dataSource); - releaseConnection(connection); } - private static boolean isTransactional(final DataSource dataSource, final Connection connection) { - if (TransactionSynchronizationManager.hasResource(dataSource)) { - return TransactionSynchronizationManager.getResource(dataSource) == connection; - } - return false; - } }