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..eeb6907b0d --- /dev/null +++ b/app/src/main/java/com/techcourse/service/AppUserService.java @@ -0,0 +1,35 @@ +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; + } + + @Override + public User findById(final long id) { + return userDao.findById(id); + } + + @Override + public void insert(final User user) { + userDao.insert(user); + } + + @Override + public void changePassword(final long id, final String newPassword, final String createBy) { + final var findUser = findById(id); + findUser.changePassword(newPassword); + userDao.update(findUser); + userHistoryDao.log(new UserHistory(findUser, 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..dd0bbf7171 --- /dev/null +++ b/app/src/main/java/com/techcourse/service/TxUserService.java @@ -0,0 +1,37 @@ +package com.techcourse.service; + +import com.techcourse.domain.User; +import org.springframework.transaction.support.TransactionTemplate; + +public class TxUserService implements UserService { + + private final AppUserService userService; + + private final TransactionTemplate transactionTemplate; + + public TxUserService(final AppUserService userService, final TransactionTemplate transactionTemplate) { + this.userService = userService; + this.transactionTemplate = transactionTemplate; + } + + @Override + public User findById(final long id) { + return userService.findById(id); + } + + @Override + public void insert(final User user) { + transactionTemplate.execute(() -> { + userService.insert(user); + return null; + }); + } + + @Override + public void changePassword(final long id, final String newPassword, final String createBy) { + transactionTemplate.execute(() -> { + userService.changePassword(id, newPassword, createBy); + return null; + }); + } +} diff --git a/app/src/main/java/com/techcourse/service/UserService.java b/app/src/main/java/com/techcourse/service/UserService.java index af18852c9e..48e1c24e86 100644 --- a/app/src/main/java/com/techcourse/service/UserService.java +++ b/app/src/main/java/com/techcourse/service/UserService.java @@ -1,56 +1,11 @@ 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.dao.DataAccessException; -import org.springframework.jdbc.datasource.DataSourceUtils; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import javax.sql.DataSource; -import java.sql.SQLException; +public interface UserService { + User findById(final long id); -public class UserService { + void insert(final User user); - private final UserDao userDao; - private final UserHistoryDao userHistoryDao; - - public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { - this.userDao = userDao; - this.userHistoryDao = userHistoryDao; - } - - public User findById(final long id) { - return userDao.findById(id); - } - - public void insert(final User user) { - userDao.insert(user); - } - - public void changePassword(final long id, final String newPassword, final String createBy) { - final var findUser = findById(id); - - final DataSource dataSource = DataSourceConfig.getInstance(); - final var connection = DataSourceUtils.getConnection(dataSource); - try { - connection.setAutoCommit(false); - findUser.changePassword(newPassword); - userDao.update(findUser); - userHistoryDao.log(new UserHistory(findUser, createBy)); - connection.commit(); - } catch (SQLException e) { - try { - connection.rollback(); - } catch (SQLException ex) { - throw new DataAccessException(ex); - } - throw new DataAccessException(e); - } finally { - DataSourceUtils.releaseConnection(connection, dataSource); - TransactionSynchronizationManager.unbindResource(dataSource); - } - } + void changePassword(final long id, final String newPassword, final String createBy); } diff --git a/app/src/test/java/com/techcourse/service/UserServiceTest.java b/app/src/test/java/com/techcourse/service/UserServiceTest.java index 3925a8b534..4e700195c3 100644 --- a/app/src/test/java/com/techcourse/service/UserServiceTest.java +++ b/app/src/test/java/com/techcourse/service/UserServiceTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.support.TransactionTemplate; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -31,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"; @@ -46,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, new TransactionTemplate(DataSourceConfig.getInstance())); final var newPassword = "newPassword"; final var createBy = "gugu"; diff --git a/jdbc/src/main/java/org/springframework/transaction/support/TransactionExecutor.java b/jdbc/src/main/java/org/springframework/transaction/support/TransactionExecutor.java new file mode 100644 index 0000000000..db9421c270 --- /dev/null +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionExecutor.java @@ -0,0 +1,7 @@ +package org.springframework.transaction.support; + +@FunctionalInterface +public interface TransactionExecutor { + + 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 new file mode 100644 index 0000000000..de6fbe22ed --- /dev/null +++ b/jdbc/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -0,0 +1,58 @@ +package org.springframework.transaction.support; + +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.datasource.DataSourceUtils; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class TransactionTemplate { + + private final DataSource dataSource; + + public TransactionTemplate(final DataSource dataSource) { + this.dataSource = dataSource; + } + + public T execute(final TransactionExecutor transactionExecutor) { + final var connection = DataSourceUtils.getConnection(dataSource); + try { + connection.setAutoCommit(false); + final T result = transactionExecutor.execute(); + commit(connection); + return result; + } catch (SQLException e) { + rollback(connection); + throw new DataAccessException(e); + } finally { + turnOnAutoCommit(connection); + DataSourceUtils.releaseConnection(connection, dataSource); + TransactionSynchronizationManager.unbindResource(dataSource); + } + } + + private void commit(final Connection connection) { + try { + connection.commit(); + } catch (SQLException e) { + throw new DataAccessException(e); + } + } + + private void rollback(final Connection connection) { + try { + connection.rollback(); + } catch (SQLException ex) { + throw new DataAccessException(ex); + } + } + + private void turnOnAutoCommit(final Connection connection) { + try { + connection.commit(); + } catch (SQLException e) { + throw new DataAccessException(e); + } + } +}