Skip to content

Commit

Permalink
feat: 트랜잭션 서비스 추상화
Browse files Browse the repository at this point in the history
  • Loading branch information
JJ503 committed Oct 9, 2023
1 parent aa29d4d commit 2bfb86c
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 76 deletions.
6 changes: 0 additions & 6 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.springframework.jdbc.core.RowMapper;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.List;

public class UserDao {
Expand Down Expand Up @@ -37,11 +36,6 @@ public void update(final User user) {
jdbcTemplate.update(sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
}

public void update(final Connection connection, final User user) {
final var sql = "update users set account = ?, password = ?, email = ? where id = ?";
jdbcTemplate.update(connection, sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
}

public List<User> findAll() {
final var sql = "select id, account, password, email from users";

Expand Down
5 changes: 1 addition & 4 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import com.techcourse.domain.UserHistory;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.Connection;

public class UserHistoryDao {

private final JdbcTemplate jdbcTemplate;
Expand All @@ -13,10 +11,9 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void log(final Connection connection, final UserHistory userHistory) {
public void log(final UserHistory userHistory) {
final var sql = "insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(
connection,
sql,
userHistory.getUserId(),
userHistory.getAccount(),
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/com/techcourse/service/AppUserService.java
Original file line number Diff line number Diff line change
@@ -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;
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 user = findById(id);
user.changePassword(newPassword);
userDao.update(user);
userHistoryDao.log(new UserHistory(user, createBy));
}
}
37 changes: 37 additions & 0 deletions app/src/main/java/com/techcourse/service/TxUserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.techcourse.service;

import com.techcourse.domain.User;
import org.springframework.transaction.support.TransactionExecutor;

public class TxUserService implements UserService {

private final UserService userService;
private final TransactionExecutor transactionExecutor;

public TxUserService(final AppUserService appUserService, final TransactionExecutor transactionExecutor) {
this.userService = appUserService;
this.transactionExecutor = transactionExecutor;
}

@Override
public User findById(final long id) {
return transactionExecutor.execute(() -> userService.findById(id));
}

@Override
public void insert(final User user) {
transactionExecutor.execute(() -> {
userService.insert(user);
return null;
});

}

@Override
public void changePassword(final long id, final String newPassword, final String createBy) {
transactionExecutor.execute(() -> {
userService.changePassword(id, newPassword, createBy);
return null;
});
}
}
33 changes: 4 additions & 29 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,12 @@
package com.techcourse.service;

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.TransactionExecutor;

public class UserService {
public interface UserService {

private final UserDao userDao;
private final UserHistoryDao userHistoryDao;
private final TransactionExecutor transactionExecutor;
User findById(final long id);

public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao, final TransactionExecutor transactionExecutor) {
this.userDao = userDao;
this.userHistoryDao = userHistoryDao;
this.transactionExecutor = transactionExecutor;
}
void insert(final User user);

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) {
transactionExecutor.execute(connection -> {
final var user = findById(id);
user.changePassword(newPassword);
userDao.update(connection, user);
userHistoryDao.log(connection, new UserHistory(user, createBy));
});
}
void changePassword(final long id, final String newPassword, final String createBy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.Connection;

public class MockUserHistoryDao extends UserHistoryDao {

public MockUserHistoryDao(final JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}

@Override
public void log(final Connection connection, final UserHistory userHistory) {
public void log(final UserHistory userHistory) {
throw new DataAccessException();
}
}
7 changes: 5 additions & 2 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void setUp() {
@Test
void testChangePassword() {
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao, transactionExecutor);
final var userService = new AppUserService(userDao, userHistoryDao);

final var newPassword = "qqqqq";
final var createBy = "gugu";
Expand All @@ -52,7 +52,10 @@ void testChangePassword() {
void testTransactionRollback() {
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao, transactionExecutor);
// 애플리케이션 서비스
final var appUserService = new AppUserService(userDao, userHistoryDao);
// 트랜잭션 서비스 추상화
final var userService = new TxUserService(appUserService, transactionExecutor);

final var newPassword = "newPassword";
final var createBy = "gugu";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.springframework.jdbc.core.exception.IncorrectResultSizeDataAccessException;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand All @@ -26,10 +25,6 @@ public int update(final String sql, final Object... args) {
return preparedStatementExecutor.execute(PreparedStatement::executeUpdate, sql, args);
}

public int update(final Connection connection, final String sql, final Object... args) {
return preparedStatementExecutor.execute(connection, PreparedStatement::executeUpdate, sql, args);
}

public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... args) {
return preparedStatementExecutor.execute(
pstmt -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,7 +24,7 @@ public <T> T execute(
final String sql,
final Object... args
) {
try (final Connection connection = dataSource.getConnection()) {
try (final Connection connection = DataSourceUtils.getConnection(dataSource)) {
return execute(connection, executeStrategy, sql, args);
} catch (SQLException e) {
log.error("exception : {}", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static Connection getConnection(DataSource dataSource) throws CannotGetJd

public static void releaseConnection(Connection connection, DataSource dataSource) {
try {
TransactionSynchronizationManager.unbindResource(dataSource);
connection.close();
} catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Failed to close JDBC Connection");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package org.springframework.transaction.support;

import java.sql.Connection;
import java.sql.SQLException;

@FunctionalInterface
public interface TransactionExecuteStrategy {
public interface TransactionExecuteStrategy<T> {

void strategy(final Connection connection) throws SQLException;
T strategy() throws SQLException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,23 @@ public TransactionExecutor(final DataSource dataSource) {
this.dataSource = dataSource;
}

public void execute(final TransactionExecuteStrategy executeStrategy) {
public <T> T execute(final TransactionExecuteStrategy<T> executeStrategy) {
final Connection connection = DataSourceUtils.getConnection(dataSource);

try {
connection.setAutoCommit(false);

executeStrategy.strategy(connection);
final T result = executeStrategy.strategy();

connection.commit();

return result;
} catch (Exception e) {
log.error("execute exception : {}", e);
rollback(connection);
throw new DataAccessException(e);
} finally {
DataSourceUtils.releaseConnection(connection, dataSource);
TransactionSynchronizationManager.unbindResource(dataSource);
}
}

public Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
log.error("getConnection exception : {}", e);
throw new DataAccessException(e);
}
}

Expand All @@ -55,13 +47,4 @@ public void rollback(final Connection connection) {
throw new DataAccessException(e);
}
}

public void close(final Connection connection) {
try {
connection.close();
} catch (SQLException e) {
log.error("close exception : {}", e);
throw new DataAccessException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;

public abstract class TransactionSynchronizationManager {
Expand All @@ -11,13 +12,33 @@ public abstract class TransactionSynchronizationManager {
private TransactionSynchronizationManager() {}

public static Connection getResource(DataSource key) {
return null;
final Map<DataSource, Connection> connectionHolder = resources.get();

if (connectionHolder == null) {
return null;
}

return connectionHolder.get(key);
}

public static void bindResource(DataSource key, Connection value) {
Map<DataSource, Connection> connectionHolder = resources.get();

if (connectionHolder == null) {
connectionHolder = new HashMap<>();
resources.set(connectionHolder);
}

connectionHolder.put(key, value);
}

public static Connection unbindResource(DataSource key) {
return null;
final Map<DataSource, Connection> connectionHolder = resources.get();

if (connectionHolder == null) {
return null;
}

return connectionHolder.remove(key);
}
}

0 comments on commit 2bfb86c

Please sign in to comment.