Skip to content

Commit 5db4981

Browse files
authored
[3단계 - Transaction 적용하기] 피글렛(김은수) 미션 제출합니다. (#1098)
* fix: 서비스 레벨 트랜잭션 적용하도록 수정 * test: connection 공유하도록 테스트 수정 * refactor: 롤백 과정 중 발생하는 예외처리 및 콜백 패턴으로 처리
1 parent 035a742 commit 5db4981

File tree

6 files changed

+109
-30
lines changed

6 files changed

+109
-30
lines changed

app/src/main/java/com/techcourse/dao/UserDao.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.interface21.jdbc.core.JdbcTemplate;
44
import com.interface21.jdbc.core.RowMapper;
55
import com.techcourse.domain.User;
6-
import java.sql.SQLException;
6+
import java.sql.Connection;
77
import java.util.List;
88
import org.slf4j.Logger;
99
import org.slf4j.LoggerFactory;
@@ -35,26 +35,26 @@ public void update(final User user) {
3535
jdbcTemplate.update(sql, user.getPassword(), user.getEmail(), user.getId());
3636
}
3737

38+
/*
39+
Connection 공유 버전 overloading
40+
*/
41+
public void update(final Connection connection, final User user) {
42+
final var sql = "update users set password = ?, email = ? where id = ?";
43+
jdbcTemplate.update(connection, sql, user.getPassword(), user.getEmail(), user.getId());
44+
}
45+
3846
public List<User> findAll() {
3947
final var sql = "select id, account, password, email from users";
4048
return jdbcTemplate.query(sql, USER_ROW_MAPPER);
4149
}
4250

4351
public User findById(final Long id) {
4452
final var sql = "select id, account, password, email from users where id = ?";
45-
try {
46-
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, id);
47-
} catch (SQLException e) {
48-
throw new RuntimeException(e);
49-
}
53+
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, id);
5054
}
5155

5256
public User findByAccount(final String account) {
5357
final var sql = "select id, account, password, email from users where account = ?";
54-
try {
55-
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, account);
56-
} catch (SQLException e) {
57-
throw new RuntimeException(e);
58-
}
58+
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, account);
5959
}
6060
}

app/src/main/java/com/techcourse/dao/UserHistoryDao.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.interface21.jdbc.core.JdbcTemplate;
44
import com.techcourse.domain.UserHistory;
5+
import java.sql.Connection;
56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
78

@@ -26,4 +27,20 @@ public void log(final UserHistory userHistory) {
2627
userHistory.getCreateBy()
2728
);
2829
}
30+
31+
/*
32+
Connection 공유 버전 overloading
33+
*/
34+
public void log(final Connection connection, final UserHistory userHistory) {
35+
final var sql = "insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)";
36+
jdbcTemplate.update(
37+
connection, sql,
38+
userHistory.getUserId(),
39+
userHistory.getAccount(),
40+
userHistory.getPassword(),
41+
userHistory.getEmail(),
42+
userHistory.getCreatedAt(),
43+
userHistory.getCreateBy()
44+
);
45+
}
2946
}

app/src/main/java/com/techcourse/service/UserService.java

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,23 @@
44
import com.techcourse.dao.UserHistoryDao;
55
import com.techcourse.domain.User;
66
import com.techcourse.domain.UserHistory;
7+
import java.sql.Connection;
8+
import java.sql.SQLException;
9+
import java.util.function.Consumer;
10+
import javax.sql.DataSource;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
713

814
public class UserService {
915

16+
private static final Logger log = LoggerFactory.getLogger(UserService.class);
17+
18+
private final DataSource dataSource;
1019
private final UserDao userDao;
1120
private final UserHistoryDao userHistoryDao;
1221

13-
public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) {
22+
public UserService(final DataSource dataSource, final UserDao userDao, final UserHistoryDao userHistoryDao) {
23+
this.dataSource = dataSource;
1424
this.userDao = userDao;
1525
this.userHistoryDao = userHistoryDao;
1626
}
@@ -24,9 +34,46 @@ public void insert(final User user) {
2434
}
2535

2636
public void changePassword(final long id, final String newPassword, final String createBy) {
27-
final var user = findById(id);
28-
user.changePassword(newPassword);
29-
userDao.update(user);
30-
userHistoryDao.log(new UserHistory(user, createBy));
37+
doInTransaction(connection -> {
38+
final var user = findById(id);
39+
user.changePassword(newPassword);
40+
userDao.update(connection, user);
41+
userHistoryDao.log(connection, new UserHistory(user, createBy));
42+
});
43+
}
44+
45+
private void doInTransaction(Consumer<Connection> consumer){
46+
Connection connection = null;
47+
try {
48+
connection = dataSource.getConnection();
49+
connection.setAutoCommit(false);
50+
consumer.accept(connection);
51+
connection.commit();
52+
} catch (Exception e) {
53+
log.error("비밀번호 변경 중 예외 발생");
54+
handleTransactionException(e, connection);
55+
} finally {
56+
closeConnection(connection);
57+
}
58+
}
59+
60+
private void handleTransactionException(Exception e, Connection connection) {
61+
if(connection != null) {
62+
try { // 커넥션이 존재할 경우 롤백 시도
63+
connection.rollback();
64+
log.info("Rollback 완료");
65+
} catch (SQLException ex) { // 롤백 도중 예외 발생
66+
log.error("Rollback 실패", ex);
67+
}
68+
}
69+
throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
70+
}
71+
72+
private void closeConnection(Connection connection) {
73+
if(connection != null){ // 커넥션이 존재할 경우 close
74+
try {
75+
connection.close();
76+
} catch (SQLException ignored) {}
77+
}
3178
}
3279
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package com.techcourse.service;
22

3-
import com.techcourse.dao.UserHistoryDao;
4-
import com.techcourse.domain.UserHistory;
53
import com.interface21.dao.DataAccessException;
64
import com.interface21.jdbc.core.JdbcTemplate;
5+
import com.techcourse.dao.UserHistoryDao;
6+
import com.techcourse.domain.UserHistory;
7+
import java.sql.Connection;
78

89
public class MockUserHistoryDao extends UserHistoryDao {
910

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

1415
@Override
15-
public void log(final UserHistory userHistory) {
16+
public void log(final Connection connection, final UserHistory userHistory) {
1617
throw new DataAccessException();
1718
}
1819
}

app/src/test/java/com/techcourse/service/UserServiceTest.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,40 @@
11
package com.techcourse.service;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import com.interface21.dao.DataAccessException;
7+
import com.interface21.jdbc.core.JdbcTemplate;
38
import com.techcourse.config.DataSourceConfig;
49
import com.techcourse.dao.UserDao;
510
import com.techcourse.dao.UserHistoryDao;
611
import com.techcourse.domain.User;
712
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils;
8-
import com.interface21.dao.DataAccessException;
9-
import com.interface21.jdbc.core.JdbcTemplate;
13+
import javax.sql.DataSource;
1014
import org.junit.jupiter.api.BeforeEach;
11-
import org.junit.jupiter.api.Disabled;
1215
import org.junit.jupiter.api.Test;
1316

14-
import static org.assertj.core.api.Assertions.assertThat;
15-
import static org.junit.jupiter.api.Assertions.assertThrows;
16-
17-
@Disabled
1817
class UserServiceTest {
1918

19+
private DataSource dataSource;
2020
private JdbcTemplate jdbcTemplate;
2121
private UserDao userDao;
2222

2323
@BeforeEach
2424
void setUp() {
25-
this.jdbcTemplate = new JdbcTemplate(DataSourceConfig.getInstance());
25+
this.dataSource = DataSourceConfig.getInstance();
26+
this.jdbcTemplate = new JdbcTemplate(dataSource);
2627
this.userDao = new UserDao(jdbcTemplate);
2728

28-
DatabasePopulatorUtils.execute(DataSourceConfig.getInstance());
29+
DatabasePopulatorUtils.execute(dataSource);
2930
final var user = new User("gugu", "password", "[email protected]");
3031
userDao.insert(user);
3132
}
3233

3334
@Test
3435
void testChangePassword() {
3536
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
36-
final var userService = new UserService(userDao, userHistoryDao);
37+
final var userService = new UserService(dataSource, userDao, userHistoryDao);
3738

3839
final var newPassword = "qqqqq";
3940
final var createBy = "gugu";
@@ -48,7 +49,7 @@ void testChangePassword() {
4849
void testTransactionRollback() {
4950
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
5051
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
51-
final var userService = new UserService(userDao, userHistoryDao);
52+
final var userService = new UserService(dataSource, userDao, userHistoryDao);
5253

5354
final var newPassword = "newPassword";
5455
final var createBy = "gugu";

jdbc/src/main/java/com/interface21/jdbc/core/JdbcTemplate.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,20 @@ public JdbcTemplate(final DataSource dataSource) {
2525
public void update(String sql, Object... params){
2626
try (Connection conn = dataSource.getConnection();
2727
PreparedStatement pstmt = conn.prepareStatement(sql)){
28+
log.debug("query : {}", sql);
29+
setParameters(pstmt, params);
30+
pstmt.executeUpdate();
31+
} catch (SQLException e) {
32+
log.error(e.getMessage(), e);
33+
throw new DataAccessException(e.getMessage(), e);
34+
}
35+
}
2836

37+
/*
38+
Connection 공유 버전 오버로딩
39+
*/
40+
public void update(Connection connection, String sql, Object... params){
41+
try (PreparedStatement pstmt = connection.prepareStatement(sql)){
2942
log.debug("query : {}", sql);
3043
setParameters(pstmt, params);
3144
pstmt.executeUpdate();

0 commit comments

Comments
 (0)