Skip to content

Commit

Permalink
refactor: 예외 처리 로직을 TransactionManager에서 수행하도록 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
swonny committed Oct 14, 2023
1 parent f21c08e commit 88ffa92
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 52 deletions.
13 changes: 7 additions & 6 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.SQLException;
import java.util.List;

public class UserDao {
Expand All @@ -25,29 +26,29 @@ public UserDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public int insert(final User user) {
public int insert(final User user) throws SQLException {
final var sql = "insert into users (account, password, email) values (?, ?, ?)";
log.debug("query : {}", sql);

return updateQuery(sql, user.getAccount(), user.getPassword(), user.getEmail());
}

private int updateQuery(final String sql, final Object... objects) {
private int updateQuery(final String sql, final Object... objects) throws SQLException {
final int updatedRows = jdbcTemplate.update(sql, objects);
if (updatedRows < 1) {
throw new RuntimeException("저장된 데이터가 없습니다.");
}
return updatedRows;
}

public int update(final User user) {
public int update(final User user) throws SQLException {
final var sql = "update users set (account, password, email) = (?, ?, ?)";
log.debug("query : {}", sql);

return updateQuery(sql, user.getAccount(), user.getPassword(), user.getEmail());
}

public List<User> findAll() {
public List<User> findAll() throws SQLException {
final var sql = "select id, account, password, email from users";
final List<User> users = jdbcTemplate.query(sql, userRowMapper);

Expand All @@ -56,7 +57,7 @@ public List<User> findAll() {
return users;
}

public User findById(final Long id) {
public User findById(final Long id) throws SQLException {
final var sql = "select id, account, password, email from users where id = ?";
final User user = jdbcTemplate.queryForObject(sql, userRowMapper, id)
.orElseThrow(() -> new RuntimeException("찾는 사용자가 존재하지 않습니다."));
Expand All @@ -66,7 +67,7 @@ public User findById(final Long id) {
return user;
}

public User findByAccount(final String account) {
public User findByAccount(final String account) throws SQLException {
final var sql = "select id, account, password, email from users where account = ?";
final User user = jdbcTemplate.queryForObject(sql, userRowMapper, account)
.orElseThrow(() -> new RuntimeException("찾는 사용자가 존재하지 않습니다."));
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.SQLException;

public class UserHistoryDao {

private static final Logger log = LoggerFactory.getLogger(UserHistoryDao.class);
Expand All @@ -15,7 +17,7 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void log(final UserHistory userHistory) {
public void log(final UserHistory userHistory) throws SQLException {
final var sql = "insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(
sql,
Expand Down
8 changes: 5 additions & 3 deletions app/src/main/java/com/techcourse/service/AppUserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.techcourse.domain.User;
import com.techcourse.domain.UserHistory;

import java.sql.SQLException;

public class AppUserService implements UserService {

private final UserDao userDao;
Expand All @@ -16,17 +18,17 @@ public AppUserService(final UserDao userDao, final UserHistoryDao userHistoryDao
}

@Override
public User findById(final long id) {
public User findById(final long id) throws SQLException {
return userDao.findById(id);
}

@Override
public void insert(final User user) {
public void insert(final User user) throws SQLException {
userDao.insert(user);
}

@Override
public void changePassword(long id, final String newPassword, final String createBy) {
public void changePassword(long id, final String newPassword, final String createBy) throws SQLException {
final var user = findById(id);
user.changePassword(newPassword);
userDao.update(user);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.techcourse.service;

import java.sql.SQLException;

@FunctionalInterface
public interface TransactionCallback<T> {

T execute();
T execute() throws SQLException;
}
14 changes: 9 additions & 5 deletions app/src/main/java/com/techcourse/service/TransactionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ public TransactionManager(final DataSource dataSource) {
}

public <T> T execute(final TransactionCallback<T> callback) {
final Connection connection = TransactionSynchronizationManager.startNewTransaction(dataSource);
try {
final Connection connection = TransactionSynchronizationManager.startNewTransaction(dataSource);

final T result = callback.execute();
final T result = callback.execute();

commitTransaction(connection);
TransactionSynchronizationManager.finishTransaction(dataSource);
commitTransaction(connection);
TransactionSynchronizationManager.finishTransaction(dataSource);

return result;
return result;
} catch (final SQLException ex) {
throw new RuntimeException("실행 중 예외가 발생했습니다.");
}
}

public static void commitTransaction(final Connection connection) {
Expand Down
8 changes: 5 additions & 3 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import com.techcourse.domain.User;

import java.sql.SQLException;

public interface UserService {

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

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

void changePassword(final long id, final String newPassword, final String createBy);
void changePassword(final long id, final String newPassword, final String createBy) throws SQLException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.sql.SQLException;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -25,13 +26,14 @@ void setUp() throws SQLException {
this.jdbcTemplate = new JdbcTemplate(DataSourceConfig.getInstance());
this.userDao = new UserDao(jdbcTemplate);

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

@Test
void testChangePassword() {
void testChangePassword() throws SQLException {
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
final var userService = new AppUserService(userDao, userHistoryDao);

Expand Down
42 changes: 16 additions & 26 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
Expand All @@ -25,20 +25,20 @@ public JdbcTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

public <T> Optional<T> queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... parameters) {
public <T> Optional<T> queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... parameters) throws SQLException {
return execute(sql, preparedStatement -> {
final ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return Optional.of(rowMapper.mapRow(resultSet));
}
if (!resultSet.isLast()) {
throw new RuntimeException("단일 데이터가 아닙니다.");
throw new DataAccessException("단일 데이터가 아닙니다.");
}
return Optional.empty();
}, parameters);
}

public <T> List<T> query(final String sql, final RowMapper<T> rowMapper) {
public <T> List<T> query(final String sql, final RowMapper<T> rowMapper) throws SQLException {
return execute(sql, preparedStatement -> {
final ResultSet resultSet = preparedStatement.executeQuery();
final List<T> objects = new ArrayList<>();
Expand All @@ -50,34 +50,24 @@ public <T> List<T> query(final String sql, final RowMapper<T> rowMapper) {
});
}

public int update(final String sql, final Object... parameters) {
public int update(final String sql, final Object... parameters) throws SQLException {
return execute(sql, PreparedStatement::executeUpdate, parameters);
}

public <T> T execute(final String sql, final ExecuteQueryCallback<T> callBack, final Object... objects) {
try {
final PreparedStatement preparedStatement = createPreparedStatement(sql);
setPreparedStatement(preparedStatement, objects);
public <T> T execute(
final String sql,
final ExecuteQueryCallback<T> callBack,
final Object... objects
) throws SQLException {
final PreparedStatement preparedStatement = createPreparedStatement(sql);
setPreparedStatement(preparedStatement, objects);

final T result = callBack.execute(preparedStatement);
TransactionSynchronizationManager.commitTransaction(dataSource);

return result;
} catch (final SQLException ex) {
throw new RuntimeException("실행 중 예외가 발생했습니다.");
}
return callBack.execute(preparedStatement);
}

private PreparedStatement createPreparedStatement(final String sql) {
try {
final Connection connection = TransactionSynchronizationManager.getResource(dataSource);
return connection.prepareStatement(sql);
} catch (SQLException ex) {
TransactionSynchronizationManager.rollback(dataSource);

log.error(ex.getMessage());
throw new CannotGetJdbcConnectionException("jdbc 연결에 실패했습니다.");
}
private PreparedStatement createPreparedStatement(final String sql) throws SQLException {
final Connection connection = TransactionSynchronizationManager.getResource(dataSource);
return connection.prepareStatement(sql);
}

private void setPreparedStatement(final PreparedStatement preparedStatement, final Object[] parameters) throws SQLException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.springframework.jdbc.datasource;

import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.sql.Connection;
Expand All @@ -14,7 +13,7 @@ private DataSourceUtils() {

public static Connection getConnection(final DataSource dataSource) throws CannotGetJdbcConnectionException, SQLException {
try {
return TransactionSynchronizationManager.getResource(dataSource);
return dataSource.getConnection();
} catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ public abstract class TransactionSynchronizationManager {
private TransactionSynchronizationManager() {
}

public static Connection getResource(final DataSource dataSource) throws SQLException {
if (!isActive.get() || !resources.get().containsKey(dataSource)) {
throw new RuntimeException("시작한 트랜잭션이 없습니다.");
public static Connection getResource(final DataSource dataSource) {
if (!resources.get().containsKey(dataSource)) {
final Connection connection = startNewTransaction(dataSource);
resources.get().put(dataSource, connection);
}
return resources.get().get(dataSource);
}
Expand Down

0 comments on commit 88ffa92

Please sign in to comment.