Skip to content

Commit

Permalink
feat: 트랜잭션 서비스 추상화
Browse files Browse the repository at this point in the history
  • Loading branch information
70825 committed Oct 9, 2023
1 parent e92467e commit d5726e2
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 88 deletions.
5 changes: 0 additions & 5 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ public void insert(final User user) {
jdbcTemplate.update(sql, user.getAccount(), user.getPassword(), user.getEmail());
}

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

public void update(final User user) {
final var sql = "UPDATE users SET account = ?, password = ?, email = ? WHERE id = ?";
jdbcTemplate.update(sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void log(final Connection conn, 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(conn, sql, userHistory.getUserId(), userHistory.getAccount(), userHistory.getPassword(),
jdbcTemplate.update(sql, userHistory.getUserId(), userHistory.getAccount(), userHistory.getPassword(),
userHistory.getEmail(), userHistory.getCreatedAt(), userHistory.getCreateBy());
}
}
37 changes: 37 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,37 @@
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 java.util.NoSuchElementException;

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)
.orElseThrow(NoSuchElementException::new);
}

@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));
}
}
36 changes: 36 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,36 @@
package com.techcourse.service;

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

public class TxUserService implements UserService {

private final TransactionTemplate transactionTemplate;
private final UserService userService;

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

@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;
});
}
}
65 changes: 4 additions & 61 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,69 +1,12 @@
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 java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;

public class UserService {
public interface UserService {

private final UserDao userDao;
private final UserHistoryDao userHistoryDao;
private final DataSource dataSource;
User findById(final Long id);

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

public User findById(final long id) {
return userDao.findById(id)
.orElseThrow(IllegalArgumentException::new);
}

public void insert(final User user) {
userDao.insert(user);
}

public void changePassword(final long id, final String newPassword, final String createBy) {
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);

final var user = findById(id);
user.changePassword(newPassword);
userDao.update(conn, user);
userHistoryDao.log(dataSource.getConnection(), new UserHistory(user, createBy));

conn.commit();
} catch (final SQLException e) {
rollback(conn);
throw new DataAccessException(e);
} finally {
close(conn);
}
}

private void rollback(final Connection conn) {
try {
conn.rollback();
} catch (final SQLException e) {
throw new DataAccessException("fail rollback");
}
}

private void close(final Connection conn) {
try {
conn.close();
} catch (final SQLException e) {
throw new DataAccessException("fail close");
}
}
void changePassword(final Long id, final String newPassword, final String createBy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public MockUserHistoryDao(final JdbcTemplate jdbcTemplate) {
}

@Override
public void log(final Connection conn, final UserHistory userHistory) {
public void log(final UserHistory userHistory) {
throw new DataAccessException();
}
}
9 changes: 7 additions & 2 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionTemplate;

class UserServiceTest {

Expand All @@ -32,7 +33,9 @@ void setUp() {
@Test
void testChangePassword() {
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
final var transactionTemplate = new TransactionTemplate(DataSourceConfig.getInstance());
final var appUserService = new AppUserService(userDao, userHistoryDao);
final var userService = new TxUserService(transactionTemplate, appUserService);

final var newPassword = "qqqqq";
final var createBy = "gugu";
Expand All @@ -47,7 +50,9 @@ void testChangePassword() {
void testTransactionRollback() {
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
final var transactionTemplate = new TransactionTemplate(DataSourceConfig.getInstance());
final var appUserService = new AppUserService(userDao, userHistoryDao);
final var userService = new TxUserService(transactionTemplate, appUserService);

final var newPassword = "newPassword";
final var createBy = "gugu";
Expand Down
18 changes: 3 additions & 15 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class JdbcTemplate {

Expand All @@ -26,10 +27,6 @@ public JdbcTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

public int update(final Connection conn, final String query, final Object... args) {
return execute(conn, this::executeUpdate, query, args);
}

public int update(final String query, final Object... args) {
return execute(this::executeUpdate, query, args);
}
Expand All @@ -42,18 +39,9 @@ private int executeUpdate(final PreparedStatement pstmt) {
}
}

private <T> T execute(final Connection conn, final Function<PreparedStatement, T> function, final String query, final Object... args) {
try (final PreparedStatement pstmt = getPreparedStatement(conn, query, args)) {
return function.apply(pstmt);
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new DataAccessException(e);
}
}

private <T> T execute(final Function<PreparedStatement, T> function, final String query, final Object... args) {
try (final Connection conn = dataSource.getConnection();
final PreparedStatement pstmt = getPreparedStatement(conn, query, args)) {
final Connection conn = DataSourceUtils.getConnection(dataSource);
try (final PreparedStatement pstmt = getPreparedStatement(conn, query, args)) {
return function.apply(pstmt);
} catch (SQLException e) {
log.error(e.getMessage(), e);
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
@@ -1,6 +1,7 @@
package org.springframework.transaction.support;

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

Expand All @@ -12,13 +13,23 @@ private TransactionSynchronizationManager() {
}

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

public static void bindResource(DataSource key, Connection value) {
Map<DataSource, Connection> connections = resources.get();
if (connections == null) {
connections = new HashMap<>();
resources.set(connections);
}
connections.put(key, value);
}

public static Connection unbindResource(DataSource key) {
return null;
return resources.get().remove(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.springframework.transaction.support;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Supplier;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class TransactionTemplate {

private final DataSource dataSource;

public TransactionTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

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

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

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

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

0 comments on commit d5726e2

Please sign in to comment.