Skip to content

Commit

Permalink
[JDBC 라이브러리 구현하기 - 3단계] 로지(윤가영) 미션 제출합니다. (#487)
Browse files Browse the repository at this point in the history
* refactor: 메서드 파라미터를 final로 통일

* refactor: 중복되는 row mapper를 상수로 변경

* docs: 3단계 요구사항 추가

* feat(JdbcTemplate): connection 필드 추가

* feat: 트랜잭션 구현

* Revert "feat(JdbcTemplate): connection 필드 추가"

This reverts commit 41669fe.

* fix: 잘못된 테스트 수정

* chore: 불필요한 출력문 제거

* fix: connection 인자 사용

* refactor: findById도 같은 커넥션 사용
  • Loading branch information
kyY00n authored Oct 8, 2023
1 parent 095ae1c commit 6d938b9
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 34 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,11 @@
| 트랜잭션 관리 || |
| Connection, Statement, ResultSet 객체 close || |

## 3단계

- [ ] User 비밀번호 변경 기능
- [ ] 비밀번호 변경 기능을 구현한다. (UserDao.changePassword())
- [ ] 누가, 언제, 어떤 비밀번호로 바꿨는지 이력을 남겨야한다.
- [ ] changePassword 원자성을 보장한다
- 트랜잭션을 설정한다.
- userDao와 userHistoryDao를 한 트랜잭션으로 묶으려면 동일한 Connection 객체를 사용하도록 변경
33 changes: 21 additions & 12 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.techcourse.dao;

import com.techcourse.domain.User;
import java.sql.Connection;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

public class UserDao {

public static final RowMapper<User> USER_ROW_MAPPER = (resultSet, rowNum) -> new User(resultSet.getLong("id"),
resultSet.getString("account"),
resultSet.getString("password"),
resultSet.getString("email"));


private final JdbcTemplate jdbcTemplate;


Expand All @@ -23,28 +31,29 @@ public void update(final User user) {
jdbcTemplate.update(sql, user.getPassword(), user.getEmail(), user.getAccount(), user.getId());
}

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

public List<User> findAll() {
final var sql = "select id, account, password, email from users";
return jdbcTemplate.query(sql, (resultSet, rowNum) -> new User(resultSet.getLong("id"),
resultSet.getString("account"),
resultSet.getString("password"),
resultSet.getString("email")));
return jdbcTemplate.query(sql, USER_ROW_MAPPER);
}

public User findById(final Long id) {
final var sql = "select id, account, password, email from users where id = ?";
return jdbcTemplate.queryForObject(sql, (resultSet, rowNum) -> new User(resultSet.getLong("id"),
resultSet.getString("account"),
resultSet.getString("password"),
resultSet.getString("email")), id);
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, id);
}

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

public User findByAccount(final String account) {
final var sql = "select id, account, password, email from users where account = ?";
return jdbcTemplate.queryForObject(sql, (resultSet, rowNum) -> new User(resultSet.getLong("id"),
resultSet.getString("account"),
resultSet.getString("password"),
resultSet.getString("email")), account);
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, account);
}

}
7 changes: 7 additions & 0 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.techcourse.dao;

import com.techcourse.domain.UserHistory;
import java.sql.Connection;
import org.springframework.jdbc.core.JdbcTemplate;

public class UserHistoryDao {
Expand All @@ -11,6 +12,12 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

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());
}

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(),
Expand Down
30 changes: 25 additions & 5 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
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 org.springframework.dao.DataAccessException;

public class UserService {

Expand All @@ -19,14 +23,30 @@ public User findById(final long id) {
return userDao.findById(id);
}

public User findById(final Connection connection, final long id) {
return userDao.findById(connection, id);
}

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

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));
public void changePassword(final long id, final String newPassword, final String createBy) throws SQLException {
final var connection = DataSourceConfig.getInstance().getConnection();
try {
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 (final Exception e) {
connection.rollback();
throw new DataAccessException(e);
} finally {
connection.close();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.techcourse.dao.UserHistoryDao;
import com.techcourse.domain.UserHistory;
import java.sql.Connection;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

Expand All @@ -12,7 +13,7 @@ public MockUserHistoryDao(final JdbcTemplate jdbcTemplate) {
}

@Override
public void log(final UserHistory userHistory) {
public void log(final Connection connection, final UserHistory userHistory) {
throw new DataAccessException();
}
}
34 changes: 21 additions & 13 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
import com.techcourse.dao.UserHistoryDao;
import com.techcourse.domain.User;
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

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

@Disabled
class UserServiceTest {

private JdbcTemplate jdbcTemplate;
Expand All @@ -31,7 +31,7 @@ void setUp() {
}

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

Expand All @@ -46,18 +46,26 @@ void testChangePassword() {

@Test
void testTransactionRollback() {
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
// given
final var mockUserHistoryDao = new MockUserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, mockUserHistoryDao);

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

final var actual = userService.findById(1L);
// when
try {
userService.changePassword(1L, newPassword, createBy);
} catch (final Exception e) {
// do nothing
}

assertThat(actual.getPassword()).isNotEqualTo(newPassword);
// then
final var actual = userService.findById(1L);
assertAll(
() -> assertThrows(DataAccessException.class,
() -> userService.changePassword(1L, newPassword, createBy)),
() -> assertThat(actual.getPassword()).isNotEqualTo(newPassword)
);
}
}
40 changes: 37 additions & 3 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public JdbcTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... params) {
public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... params) {
List<T> results = query(sql, rowMapper, params);
if (results.size() > 1) {
throw new DataAccessException("too many result. expected 1 but was " + results.size());
Expand All @@ -33,7 +33,18 @@ public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... params
return results.get(0);
}

public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... parameters) {
public <T> T queryForObject(final Connection connection, final String sql, final RowMapper<T> rowMapper, final Object... params) {
List<T> results = query(connection, sql, rowMapper, params);
if (results.size() > 1) {
throw new DataAccessException("too many result. expected 1 but was " + results.size());
}
if (results.isEmpty()) {
throw new DataAccessException("no result");
}
return results.get(0);
}

public <T> List<T> query(final String sql, final RowMapper<T> rowMapper, final Object... parameters) {
try (final Connection conn = dataSource.getConnection();
final PreparedStatement preparedStatement = conn.prepareStatement(sql);
final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {
Expand All @@ -45,6 +56,17 @@ public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... parameter
}
}

public <T> List<T> query(final Connection conn, final String sql, final RowMapper<T> rowMapper, final Object... parameters) {
try (final PreparedStatement preparedStatement = conn.prepareStatement(sql);
final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {
log.debug("query : {}", sql);
return mapResults(rowMapper, resultSet);
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new DataAccessException(e);
}
}

private ResultSet executeQuery(final PreparedStatement preparedStatement, final Object[] parameters)
throws SQLException {
setParameters(preparedStatement, parameters);
Expand All @@ -66,7 +88,7 @@ private <T> List<T> mapResults(final RowMapper<T> rowMapper, final ResultSet res
return results;
}

public void update(String sql, Object... parameters) {
public void update(final String sql, final Object... parameters) {
try (final Connection conn = dataSource.getConnection();
final PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
log.debug("query : {}", sql);
Expand All @@ -79,4 +101,16 @@ public void update(String sql, Object... parameters) {
}
}

public void update(final Connection conn, final String sql, final Object... parameters) {
try (final PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
log.debug("query : {}", sql);

setParameters(preparedStatement, parameters);
preparedStatement.executeUpdate();
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new DataAccessException(e);
}
}

}

0 comments on commit 6d938b9

Please sign in to comment.