Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
import com.interface21.jdbc.core.JdbcTemplate;
import com.interface21.jdbc.core.RowMapper;
import com.techcourse.domain.User;
import java.sql.Connection;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserDao {

private static final Logger log = LoggerFactory.getLogger(UserDao.class);

private final JdbcTemplate jdbcTemplate;

private final RowMapper<User> userRowMapper = (rs) -> new User(
Expand All @@ -35,6 +32,11 @@ 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";
return jdbcTemplate.query(sql, userRowMapper);
Expand All @@ -46,6 +48,12 @@ public Optional<User> findById(final Long id) {
return Optional.ofNullable(user);
}

public Optional<User> findById(final Connection connection, final Long id) {
final var sql = "select id, account, password, email from users where id = ?";
final User user = jdbcTemplate.queryForObject(connection, sql, userRowMapper, id);
return Optional.ofNullable(user);
}

public Optional<User> findByAccount(final String account) {
final var sql = "select id, account, password, email from users where account = ?";
final User user = jdbcTemplate.queryForObject(sql, userRowMapper, account);
Expand Down
69 changes: 24 additions & 45 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,40 @@

import com.techcourse.domain.UserHistory;
import com.interface21.jdbc.core.JdbcTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

public class UserHistoryDao {

private static final Logger log = LoggerFactory.getLogger(UserHistoryDao.class);

private final DataSource dataSource;

public UserHistoryDao(final DataSource dataSource) {
this.dataSource = dataSource;
}
private final JdbcTemplate jdbcTemplate;

public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.dataSource = null;
this.jdbcTemplate = jdbcTemplate;
}

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(
sql,
userHistory.getUserId(),
userHistory.getAccount(),
userHistory.getPassword(),
userHistory.getEmail(),
userHistory.getCreatedAt(),
userHistory.getCreateBy()
);
}

Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(sql);

log.debug("query : {}", sql);

pstmt.setLong(1, userHistory.getUserId());
pstmt.setString(2, userHistory.getAccount());
pstmt.setString(3, userHistory.getPassword());
pstmt.setString(4, userHistory.getEmail());
pstmt.setObject(5, userHistory.getCreatedAt());
pstmt.setString(6, userHistory.getCreateBy());
pstmt.executeUpdate();
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
} finally {
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException ignored) {}

try {
if (conn != null) {
conn.close();
}
} catch (SQLException ignored) {}
}
public void log(final Connection connection, 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(),
userHistory.getPassword(),
userHistory.getEmail(),
userHistory.getCreatedAt(),
userHistory.getCreateBy()
);
}
}
68 changes: 59 additions & 9 deletions app/src/main/java/com/techcourse/service/UserService.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

시소 try-with-resources 처리를 한 번 개선해보는 건 어때요?

이건 2중 try-catch 사용하면 로직이 간소화 되어 메서드 분리도 없어질 거 같아요!

    public void changePassword(final long id, final String newPassword, final String createBy) {

        try (Connection conn = dataSource.getConnection()) {
            conn.setAutoCommit(false);

            try {
                final var user = findById(conn, id);
                user.changePassword(newPassword);
                userDao.update(conn, user);

                userHistoryDao.log(conn, new UserHistory(user, createBy));
                conn.commit();
            } catch (Exception e) {
                conn.rollback();
                throw e;
            }
        } catch (DataAccessException | SQLException e) {
            throw new DataAccessException("비밀번호 변경 실패", e);
        }
    }

Original file line number Diff line number Diff line change
@@ -1,33 +1,83 @@
package com.techcourse.service;

import com.interface21.dao.DataAccessException;
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 java.util.NoSuchElementException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserService {

private static final Logger log = LoggerFactory.getLogger(UserService.class);

private final UserDao userDao;
private final UserHistoryDao userHistoryDao;
private final DataSource dataSource;

public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) {
public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao, final DataSource dataSource) {
this.userDao = userDao;
this.userHistoryDao = userHistoryDao;
}

public User findById(final long id) {
return userDao.findById(id).orElseThrow(NoSuchElementException::new);
this.dataSource = dataSource;
}

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

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

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));
Connection connection = null;

try {
connection = dataSource.getConnection();
connection.setAutoCommit(false);

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

connection.commit();
} catch (Exception e) {
rollback(connection);
throw new DataAccessException(e);
} finally {
closeConnection(connection);
}
}

private User findById(final Connection connection, final long id) {
return userDao.findById(connection, id)
.orElseThrow(NoSuchElementException::new);
}

private void rollback(final Connection connection) {
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e) {
log.error("Failed to rollback transaction", e);
}
}
}

private void closeConnection(final Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
log.error("Failed to close connection", e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.techcourse.domain.UserHistory;
import com.interface21.dao.DataAccessException;
import com.interface21.jdbc.core.JdbcTemplate;
import java.sql.Connection;

public class MockUserHistoryDao extends UserHistoryDao {

Expand All @@ -15,4 +16,9 @@ public MockUserHistoryDao(final JdbcTemplate jdbcTemplate) {
public void log(final UserHistory userHistory) {
throw new DataAccessException();
}

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

@Disabled
class UserServiceTest {

private JdbcTemplate jdbcTemplate;
Expand All @@ -33,7 +31,7 @@ void setUp() {
@Test
void testChangePassword() {
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
final var userService = new UserService(userDao, userHistoryDao, DataSourceConfig.getInstance());

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

final var newPassword = "newPassword";
final var createBy = "gugu";
Expand Down
54 changes: 46 additions & 8 deletions jdbc/src/main/java/com/interface21/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
Expand All @@ -24,10 +25,18 @@ public void update(final String sql, final Object... args) {
update(sql, ps -> setParameters(ps, args));
}

public void update(final Connection connection, final String sql, final Object... args) {
update(connection, sql, ps -> setParameters(ps, args));
}

public <T> List<T> query(final String sql, final RowMapper<T> rowMapper, final Object... args) {
return query(sql, rowMapper, ps -> setParameters(ps, args));
}

public <T> List<T> query(final Connection connection, final String sql, final RowMapper<T> rowMapper, final Object... args) {
return query(connection, sql, rowMapper, ps -> setParameters(ps, args));
}

public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... args) {
final List<T> results = query(sql, rowMapper, args);
if (results.isEmpty()) {
Expand All @@ -36,6 +45,14 @@ public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, fina
return results.getFirst();
}

public <T> T queryForObject(final Connection connection, final String sql, final RowMapper<T> rowMapper, final Object... args) {
final List<T> results = query(connection, sql, rowMapper, args);
if (results.isEmpty()) {
return null;
}
return results.getFirst();
}

private void update(final String sql, final PreparedStatementSetter preparedStatementSetter) {
try (final var connection = dataSource.getConnection();
final var preparedStatement = connection.prepareStatement(sql)) {
Expand All @@ -46,22 +63,43 @@ private void update(final String sql, final PreparedStatementSetter preparedStat
}
}

private void update(final Connection connection, final String sql, final PreparedStatementSetter preparedStatementSetter) {
try (final var preparedStatement = connection.prepareStatement(sql)) {
preparedStatementSetter.setValues(preparedStatement);
preparedStatement.executeUpdate();
} catch (final SQLException e) {
throw new DataAccessException(e);
}
}

private <T> List<T> query(final String sql, final RowMapper<T> rowMapper, final PreparedStatementSetter preparedStatementSetter) {
try (final var connection = dataSource.getConnection();
final var preparedStatement = connection.prepareStatement(sql)) {
preparedStatementSetter.setValues(preparedStatement);
try (final var resultSet = preparedStatement.executeQuery()) {
final List<T> results = new ArrayList<>();
while (resultSet.next()) {
results.add(rowMapper.mapRow(resultSet));
}
return results;
}
return executeQuery(preparedStatement, rowMapper, preparedStatementSetter);
} catch (final SQLException e) {
throw new DataAccessException(e);
}
}

private <T> List<T> query(final Connection connection, final String sql, final RowMapper<T> rowMapper, final PreparedStatementSetter preparedStatementSetter) {
try (final var preparedStatement = connection.prepareStatement(sql)) {
return executeQuery(preparedStatement, rowMapper, preparedStatementSetter);
} catch (final SQLException e) {
throw new DataAccessException(e);
}
}

private <T> List<T> executeQuery(final PreparedStatement preparedStatement, final RowMapper<T> rowMapper, final PreparedStatementSetter preparedStatementSetter) throws SQLException {
preparedStatementSetter.setValues(preparedStatement);
try (final var resultSet = preparedStatement.executeQuery()) {
final List<T> results = new ArrayList<>();
while (resultSet.next()) {
results.add(rowMapper.mapRow(resultSet));
}
return results;
}
}

private void setParameters(final PreparedStatement preparedStatement, final Object... args) throws SQLException {
validateParameterCount(preparedStatement, args.length);
for (int i = 0; i < args.length; i++) {
Expand Down