Skip to content

Commit e8af2cd

Browse files
[4단계 - Transaction synchronization 적용하기] 머랭 미션 제출합니다. (#1237)
* DriverManager 오타 수정 (#960) * step4 구현 * PreparedStatementSpecification이 명시적 예외를 반환하지 않도록 변경 * 롤백 실패 시 RuntimeException을 던지도록 변경 * 의미없는 출력문 제거 * 일반적인 테스트에서도 TransactionalUserService를 사용하도록 변경 * PreparedStatementSpecification의 생성자에 throws 명시 --------- Co-authored-by: 슥 <[email protected]>
1 parent c2a887b commit e8af2cd

File tree

12 files changed

+185
-316
lines changed

12 files changed

+185
-316
lines changed

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

Lines changed: 34 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
import org.slf4j.Logger;
1111
import org.slf4j.LoggerFactory;
1212

13-
import java.sql.Connection;
14-
import java.sql.SQLSyntaxErrorException;
1513
import java.util.List;
1614

1715
public class UserDao {
@@ -30,122 +28,54 @@ public UserDao(final JdbcTemplate jdbcTemplate) {
3028
this.jdbcTemplate = jdbcTemplate;
3129
}
3230

33-
public Connection getConnection() {
34-
return jdbcTemplate.getConnection();
35-
}
36-
37-
public void startTransaction(Connection connection) {
38-
jdbcTemplate.startTransaction(connection);
39-
}
40-
41-
public void commitTransaction(Connection connection) {
42-
jdbcTemplate.commitTransaction(connection);
43-
}
44-
45-
public void insertWithTransaction(final User user, final Connection connection) {
46-
CommandSpecification specification = new CommandSpecification(createPreparedStatementSpecification(
47-
"insert into users (account, password, email) values (?, ?, ?)",
48-
List.of(
49-
new PreparedStatementParameter(1, user.getAccount()),
50-
new PreparedStatementParameter(2, user.getPassword()),
51-
new PreparedStatementParameter(3, user.getEmail())
52-
)
53-
));
54-
jdbcTemplate.insertWithTransaction(specification, connection);
55-
}
56-
57-
public void updateWithTransaction(final User user, final Connection connection) {
58-
CommandSpecification specification = new CommandSpecification(createPreparedStatementSpecification(
59-
"update users set account = ?, password = ?, email = ? where id = ?",
60-
List.of(
61-
new PreparedStatementParameter(1, user.getAccount()),
62-
new PreparedStatementParameter(2, user.getPassword()),
63-
new PreparedStatementParameter(3, user.getEmail()),
64-
new PreparedStatementParameter(4, user.getId())
65-
)
66-
));
67-
jdbcTemplate.updateWithTransaction(specification, connection);
68-
}
69-
70-
public List<User> findAllWithTransaction(final Connection connection) {
71-
QuerySpecification<User> specification = new QuerySpecification<>(
72-
USER_ROW_MAPPER,
73-
createPreparedStatementSpecification(
74-
"select id, account, password, email from users",
75-
List.of()
76-
)
77-
);
78-
return jdbcTemplate.findAllWithTransaction(specification, connection);
79-
}
80-
81-
public User findByIdWithTransaction(final Long id, final Connection connection) {
82-
QuerySpecification<User> specification = new QuerySpecification<>(
83-
USER_ROW_MAPPER,
84-
createPreparedStatementSpecification(
85-
"select id, account, password, email from users where id = ?",
86-
List.of(new PreparedStatementParameter(1, id))
87-
)
88-
);
89-
return jdbcTemplate.findOneWithTransaction(specification, connection)
90-
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다."));
91-
}
92-
93-
public User findByAccountWithTransaction(final String account, final Connection connection) {
94-
95-
QuerySpecification<User> specification = new QuerySpecification<>(
96-
USER_ROW_MAPPER,
97-
createPreparedStatementSpecification(
98-
"select id, account, password, email from users where account = ?",
99-
List.of(new PreparedStatementParameter(1, account))
100-
)
101-
);
102-
return jdbcTemplate.findOneWithTransaction(specification, connection)
103-
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다."));
104-
}
105-
10631
public void insert(final User user) {
107-
CommandSpecification specification = new CommandSpecification(createPreparedStatementSpecification(
108-
"insert into users (account, password, email) values (?, ?, ?)",
109-
List.of(
110-
new PreparedStatementParameter(1, user.getAccount()),
111-
new PreparedStatementParameter(2, user.getPassword()),
112-
new PreparedStatementParameter(3, user.getEmail())
113-
)
114-
));
32+
CommandSpecification specification = new CommandSpecification(
33+
PreparedStatementSpecification
34+
.builder("insert into users (account, password, email) values (?, ?, ?)")
35+
.parameters(
36+
List.of(
37+
new PreparedStatementParameter(1, user.getAccount()),
38+
new PreparedStatementParameter(2, user.getPassword()),
39+
new PreparedStatementParameter(3, user.getEmail())
40+
)
41+
).build()
42+
);
11543
jdbcTemplate.insert(specification);
11644
}
11745

11846
public void update(final User user) {
119-
CommandSpecification specification = new CommandSpecification(createPreparedStatementSpecification(
120-
"update users set account = ?, password = ?, email = ? where id = ?",
121-
List.of(
122-
new PreparedStatementParameter(1, user.getAccount()),
123-
new PreparedStatementParameter(2, user.getPassword()),
124-
new PreparedStatementParameter(3, user.getEmail()),
125-
new PreparedStatementParameter(4, user.getId())
126-
)
127-
));
47+
CommandSpecification specification = new CommandSpecification(
48+
PreparedStatementSpecification
49+
.builder("update users set account = ?, password = ?, email = ? where id = ?")
50+
.parameters(
51+
List.of(
52+
new PreparedStatementParameter(1, user.getAccount()),
53+
new PreparedStatementParameter(2, user.getPassword()),
54+
new PreparedStatementParameter(3, user.getEmail()),
55+
new PreparedStatementParameter(4, user.getId())
56+
)
57+
).build()
58+
);
12859
jdbcTemplate.update(specification);
12960
}
13061

13162
public List<User> findAll() {
13263
QuerySpecification<User> specification = new QuerySpecification<>(
13364
USER_ROW_MAPPER,
134-
createPreparedStatementSpecification(
135-
"select id, account, password, email from users",
136-
List.of()
137-
)
65+
PreparedStatementSpecification
66+
.builder("select id, account, password, email from users")
67+
.build()
13868
);
13969
return jdbcTemplate.findAll(specification);
14070
}
14171

14272
public User findById(final Long id) {
14373
QuerySpecification<User> specification = new QuerySpecification<>(
14474
USER_ROW_MAPPER,
145-
createPreparedStatementSpecification(
146-
"select id, account, password, email from users where id = ?",
147-
List.of(new PreparedStatementParameter(1, id))
148-
)
75+
PreparedStatementSpecification
76+
.builder("select id, account, password, email from users where id = ?")
77+
.parameters(List.of(new PreparedStatementParameter(1, id)))
78+
.build()
14979
);
15080
return jdbcTemplate.findOne(specification)
15181
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다."));
@@ -155,26 +85,12 @@ public User findByAccount(final String account) {
15585

15686
QuerySpecification<User> specification = new QuerySpecification<>(
15787
USER_ROW_MAPPER,
158-
createPreparedStatementSpecification(
159-
"select id, account, password, email from users where account = ?",
160-
List.of(new PreparedStatementParameter(1, account))
161-
)
88+
PreparedStatementSpecification
89+
.builder("select id, account, password, email from users where account = ?")
90+
.parameters(List.of(new PreparedStatementParameter(1, account)))
91+
.build()
16292
);
16393
return jdbcTemplate.findOne(specification)
16494
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다."));
16595
}
166-
167-
private PreparedStatementSpecification createPreparedStatementSpecification(
168-
String sql,
169-
List<PreparedStatementParameter> parameters
170-
) {
171-
try {
172-
return PreparedStatementSpecification
173-
.builder(sql)
174-
.parameters(parameters)
175-
.build();
176-
} catch (SQLSyntaxErrorException e) {
177-
throw new IllegalArgumentException(e);
178-
}
179-
}
18096
}

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

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import org.slf4j.Logger;
99
import org.slf4j.LoggerFactory;
1010

11-
import java.sql.Connection;
12-
import java.sql.SQLSyntaxErrorException;
1311
import java.util.List;
1412

1513
public class UserHistoryDao {
@@ -24,49 +22,20 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
2422

2523
public void log(final UserHistory userHistory) {
2624
CommandSpecification commandSpecification = new CommandSpecification(
27-
createPreparedStatementSpecification(
28-
"insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)",
29-
List.of(
30-
new PreparedStatementParameter(1, userHistory.getUserId()),
31-
new PreparedStatementParameter(2, userHistory.getAccount()),
32-
new PreparedStatementParameter(3, userHistory.getPassword()),
33-
new PreparedStatementParameter(4, userHistory.getEmail()),
34-
new PreparedStatementParameter(5, userHistory.getCreatedAt()),
35-
new PreparedStatementParameter(6, userHistory.getCreateBy())
36-
)
37-
)
25+
PreparedStatementSpecification
26+
.builder(
27+
"insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)")
28+
.parameters(
29+
List.of(
30+
new PreparedStatementParameter(1, userHistory.getUserId()),
31+
new PreparedStatementParameter(2, userHistory.getAccount()),
32+
new PreparedStatementParameter(3, userHistory.getPassword()),
33+
new PreparedStatementParameter(4, userHistory.getEmail()),
34+
new PreparedStatementParameter(5, userHistory.getCreatedAt()),
35+
new PreparedStatementParameter(6, userHistory.getCreateBy())
36+
)
37+
).build()
3838
);
3939
jdbcTemplate.insert(commandSpecification);
4040
}
41-
42-
public void logWithTransaction(final UserHistory userHistory, final Connection connection) {
43-
CommandSpecification commandSpecification = new CommandSpecification(
44-
createPreparedStatementSpecification(
45-
"insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)",
46-
List.of(
47-
new PreparedStatementParameter(1, userHistory.getUserId()),
48-
new PreparedStatementParameter(2, userHistory.getAccount()),
49-
new PreparedStatementParameter(3, userHistory.getPassword()),
50-
new PreparedStatementParameter(4, userHistory.getEmail()),
51-
new PreparedStatementParameter(5, userHistory.getCreatedAt()),
52-
new PreparedStatementParameter(6, userHistory.getCreateBy())
53-
)
54-
)
55-
);
56-
jdbcTemplate.insertWithTransaction(commandSpecification, connection);
57-
}
58-
59-
private PreparedStatementSpecification createPreparedStatementSpecification(
60-
String sql,
61-
List<PreparedStatementParameter> parameters
62-
) {
63-
try {
64-
return PreparedStatementSpecification
65-
.builder(sql)
66-
.parameters(parameters)
67-
.build();
68-
} catch (SQLSyntaxErrorException e) {
69-
throw new IllegalArgumentException(e);
70-
}
71-
}
7241
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.techcourse.service;
2+
3+
import com.techcourse.dao.UserDao;
4+
import com.techcourse.dao.UserHistoryDao;
5+
import com.techcourse.domain.User;
6+
import com.techcourse.domain.UserHistory;
7+
8+
public class ApplicationUserService implements UserService {
9+
10+
private final UserDao userDao;
11+
private final UserHistoryDao userHistoryDao;
12+
13+
public ApplicationUserService(final UserDao userDao, final UserHistoryDao userHistoryDao) {
14+
this.userDao = userDao;
15+
this.userHistoryDao = userHistoryDao;
16+
}
17+
18+
@Override
19+
public User findById(final long id) {
20+
return userDao.findById(id);
21+
}
22+
23+
@Override
24+
public void save(final User user) {
25+
userDao.insert(user);
26+
}
27+
28+
@Override
29+
public void changePassword(final long id, final String newPassword, final String createBy) {
30+
final var user = findById(id);
31+
user.changePassword(newPassword);
32+
userDao.update(user);
33+
userHistoryDao.log(new UserHistory(user, createBy));
34+
}
35+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.techcourse.service;
2+
3+
import com.interface21.jdbc.CannotGetJdbcConnectionException;
4+
import com.interface21.jdbc.datasource.DataSourceUtils;
5+
import com.interface21.transaction.support.TransactionSynchronizationManager;
6+
import com.techcourse.config.DataSourceConfig;
7+
import com.techcourse.domain.User;
8+
9+
import javax.sql.DataSource;
10+
import java.sql.Connection;
11+
import java.sql.SQLException;
12+
13+
public class TransactionalUserService implements UserService {
14+
15+
private final DataSource dataSource = DataSourceConfig.getInstance();
16+
private final UserService userService;
17+
18+
public TransactionalUserService(final UserService userService) {
19+
this.userService = userService;
20+
}
21+
22+
@Override
23+
public User findById(final long id) {
24+
return userService.findById(id);
25+
}
26+
27+
@Override
28+
public void save(final User user) {
29+
userService.save(user);
30+
}
31+
32+
@Override
33+
public void changePassword(final long id, final String newPassword, final String createdBy) {
34+
try (Connection connection = DataSourceUtils.getConnection(dataSource)) {
35+
try {
36+
connection.setAutoCommit(false);
37+
userService.changePassword(id, newPassword, createdBy);
38+
connection.commit();
39+
} catch (Exception e) {
40+
try {
41+
connection.rollback();
42+
} catch (SQLException sqlException) {
43+
throw new RuntimeException("트랜잭션 롤백에 실패했습니다.", sqlException);
44+
}
45+
}
46+
} catch (CannotGetJdbcConnectionException | SQLException e) {
47+
throw new RuntimeException(dataSource.toString() + "로부터 커넥션을 획득하는 데에 실패했습니다.", e);
48+
} finally {
49+
TransactionSynchronizationManager.unbindResource(dataSource);
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)