Skip to content

Commit

Permalink
refactor: 템플릿 콜백 패턴 사용
Browse files Browse the repository at this point in the history
  • Loading branch information
kiarakim committed Oct 14, 2023
1 parent 52f90a4 commit 5f659c8
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 73 deletions.
67 changes: 15 additions & 52 deletions app/src/main/java/com/techcourse/service/TxUserService.java
Original file line number Diff line number Diff line change
@@ -1,73 +1,36 @@
package com.techcourse.service;

import com.techcourse.config.DataSourceConfig;
import com.techcourse.domain.User;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLTransactionRollbackException;
import org.springframework.transaction.support.TransactionTemplate;

public class TxUserService implements UserService {

private UserService userService;
private final DataSource dataSource = DataSourceConfig.getInstance();
private final TransactionTemplate transactionTemplate;
private final UserService userService;

public TxUserService(UserService userService) {
public TxUserService(TransactionTemplate transactionTemplate, UserService userService) {
this.transactionTemplate = transactionTemplate;
this.userService = userService;
}

@Override
public User findById(long id) throws SQLException {
Connection connection = null;
try {
if (TransactionSynchronizationManager.hasConnection(dataSource)) {
return userService.findById(id);
}
connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
User user = userService.findById(id);
connection.commit();
DataSourceUtils.releaseConnection(connection, dataSource);
return user;
} catch (SQLException e) {
connection.rollback();
throw new SQLTransactionRollbackException(e.getMessage());
}
public User findById(long id) {
return userService.findById(id);
}

@Override
public void insert(User user) throws SQLException {
Connection connection = null;
try {
if (TransactionSynchronizationManager.hasConnection(dataSource)) {
userService.insert(user);
return;
}
connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
public void insert(User user) {
transactionTemplate.execute(() -> {
userService.insert(user);
connection.commit();
} catch (SQLException e) {
connection.rollback();
throw new SQLTransactionRollbackException(e.getMessage());
}
return null;
});
}

@Override
public void changePassword(long id, String newPassword, String createBy) throws SQLException {
Connection connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
try {
public void changePassword(long id, String newPassword, String createBy) {
transactionTemplate.execute(() -> {
userService.changePassword(id, newPassword, createBy);
connection.commit();
} catch (SQLException e) {
connection.rollback();
throw new SQLTransactionRollbackException(e.getMessage());
} finally {
DataSourceUtils.releaseConnection(connection, dataSource);
}
return null;
});
}
}
8 changes: 3 additions & 5 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import com.techcourse.domain.User;

import java.sql.SQLException;

public interface UserService {

User findById(long id) throws SQLException;
User findById(long id);

void insert(User user) throws SQLException;
void insert(User user);

void changePassword(long id, String newPassword, String createBy) throws SQLException;
void changePassword(long id, String newPassword, String createBy);
}
31 changes: 15 additions & 16 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.sql.SQLException;
import org.springframework.transaction.support.TransactionTemplate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand All @@ -20,23 +18,23 @@ class UserServiceTest {

private JdbcTemplate jdbcTemplate;
private UserDao userDao;
private DataSource dataSource;

@BeforeEach
void setUp() {
dataSource = DataSourceConfig.getInstance();
DatabasePopulatorUtils.execute(dataSource);
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.jdbcTemplate = new JdbcTemplate(DataSourceConfig.getInstance());
this.userDao = new UserDao(jdbcTemplate);

DatabasePopulatorUtils.execute(DataSourceConfig.getInstance());
var user = new User("gugu", "password", "[email protected]");
userDao.insert(user);
}

@Test
void testChangePassword() throws SQLException {
void testChangePassword() {
var userHistoryDao = new UserHistoryDao(jdbcTemplate);
var userService = new TxUserService(new AppUserService(userDao, userHistoryDao));
var transactionTemplate = new TransactionTemplate(DataSourceConfig.getInstance());
var appUserService = new AppUserService(userDao, userHistoryDao);
var userService = new TxUserService(transactionTemplate, appUserService);

var newPassword = "qqqqq";
var createBy = "gugu";
Expand All @@ -48,21 +46,22 @@ void testChangePassword() throws SQLException {
}

@Test
void testTransactionRollback() throws SQLException {
void testTransactionRollback() {
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
var transactionTemplate = new TransactionTemplate(DataSourceConfig.getInstance());
// 애플리케이션 서비스
final var appUserService = new AppUserService(userDao, userHistoryDao);
var appUserService = new AppUserService(userDao, userHistoryDao);
// 트랜잭션 서비스 추상화
final var userService = new TxUserService(appUserService);
var userService = new TxUserService(transactionTemplate, appUserService);

final var newPassword = "newPassword";
final var createBy = "gugu";
var newPassword = "newPassword";
var createBy = "gugu";
// 트랜잭션이 정상 동작하는지 확인하기 위해 의도적으로 MockUserHistoryDao에서 예외를 발생시킨다.
assertThrows(DataAccessException.class,
() -> userService.changePassword(1L, newPassword, createBy));

final var actual = userService.findById(1L);
var actual = userService.findById(1L);

assertThat(actual.getPassword()).isNotEqualTo(newPassword);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.springframework.transaction.support;

@FunctionalInterface
public interface TransactionExecutor<T> {

T execute();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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(DataSource dataSource) {
this.dataSource = dataSource;
}

public <T> T execute(TransactionExecutor<T> executor) {
Connection connection = startTransaction();
try {
T result = executor.execute();
commit(connection);
return result;
} catch (DataAccessException e) {
rollback(connection);
throw e;
} finally {
DataSourceUtils.releaseConnection(connection, dataSource);
TransactionSynchronizationManager.unbindResource(dataSource);
}
}

private Connection startTransaction() {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
throw new DataAccessException(e.getMessage(), e);
}
return connection;
}

private void commit(Connection connection) {
try {
connection.commit();
connection.setAutoCommit(true);
} catch (SQLException e) {
throw new DataAccessException(e.getMessage(), e);
}
}

private void rollback(Connection connection) {
try {
connection.rollback();
connection.setAutoCommit(true);
} catch (SQLException e) {
throw new DataAccessException(e.getMessage(), e);
}
}
}

0 comments on commit 5f659c8

Please sign in to comment.