-
Notifications
You must be signed in to change notification settings - Fork 379
[4단계 - Transaction synchronization 적용하기] 모코(송선권) 미션 제출합니다. #1173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: songsunkook
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
package com.techcourse.dao; | ||
|
||
import java.sql.Connection; | ||
import java.util.List; | ||
|
||
import org.slf4j.Logger; | ||
|
@@ -9,6 +8,7 @@ | |
import com.interface21.jdbc.core.JdbcTemplate; | ||
import com.interface21.jdbc.core.jpa.SimpleJpa; | ||
import com.techcourse.config.JpaConfig; | ||
import com.techcourse.config.TransactionManagerConfig; | ||
import com.techcourse.domain.User; | ||
|
||
public class UserDao { | ||
|
@@ -23,35 +23,35 @@ public UserDao(final JdbcTemplate jdbcTemplate) { | |
this.simpleJpa = new SimpleJpa(jdbcTemplate, JpaConfig.BASE_PACKAGE); | ||
} | ||
|
||
public void insert(Connection connection, final User user) { | ||
simpleJpa.insert(connection, user); | ||
public void insert(final User user) { | ||
simpleJpa.insert(TransactionManagerConfig.getCurrentConnection(), user); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TransactionManagerConfig로 트랜잭션 경계를 설정하는 건 정말 좋은데요? DAO 레벨에서는 Connection을 전혀 모르게 해서 simplaJpa에서 TransactionManagerConfig.getCurrentConnection()을 호출하는 방법과 비교했을때, 이렇게 하신 이유는 뭔가요? |
||
} | ||
|
||
public void update(Connection connection, final User user) { | ||
simpleJpa.update(connection, user); | ||
public void update(final User user) { | ||
simpleJpa.update(TransactionManagerConfig.getCurrentConnection(), user); | ||
} | ||
|
||
public List<User> findAll(Connection connection) { | ||
return simpleJpa.selectAll(connection, User.class); | ||
public List<User> findAll() { | ||
return simpleJpa.selectAll(TransactionManagerConfig.getCurrentConnection(), User.class); | ||
} | ||
|
||
public User findById(Connection connection, final Long id) { | ||
List<User> users = simpleJpa.selectById(connection, User.class, id); | ||
public User findById(final Long id) { | ||
List<User> users = simpleJpa.selectById(TransactionManagerConfig.getCurrentConnection(), User.class, id); | ||
if (users.size() > 1) { | ||
throw new IllegalStateException("Multiple users found with id: " + id); | ||
} | ||
return users.isEmpty() ? null : users.getFirst(); | ||
} | ||
Comment on lines
43
to
44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3항 연산자 사용 금지 위반 두 곳의 반환부에서 삼항 연산자를 사용하고 있습니다. 코딩 가이드(추가 프로그래밍 요구사항) 위반이므로, 가드 클로즈나 명시적 분기로 풀어 가독성을 높여주세요. 왜 중요한가: 조건이 늘어날수록 가독성이 급격히 떨어집니다. [As per coding guidelines] Also applies to: 51-52 🤖 Prompt for AI Agents
|
||
|
||
public User findByAccount(Connection connection, final String account) { | ||
List<User> users = simpleJpa.selectByColumn(connection, User.class, "account", account); | ||
public User findByAccount(final String account) { | ||
List<User> users = simpleJpa.selectByColumn(TransactionManagerConfig.getCurrentConnection(), User.class, "account", account); | ||
if (users.size() > 1) { | ||
throw new IllegalStateException("Multiple users found with account: " + account); | ||
} | ||
return users.isEmpty() ? null : users.get(0); | ||
} | ||
|
||
public void delete(Connection connection, User user) { | ||
simpleJpa.deleteById(connection, user.getClass(), user.getId()); | ||
public void delete(User user) { | ||
simpleJpa.deleteById(TransactionManagerConfig.getCurrentConnection(), user.getClass(), user.getId()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금 UserDao는 모든 메서드가 TransactionManagerConfig를 통해 얻은 Connection을 사용하는 것 같아요. 만약 트랜잭션이 전혀 필요 없는 단순 조회 (예: findAll) 같은 경우에도 항상 트랜잭션 Connection을 사용하는 것이 괜찮다고 생각하셨을까요? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,12 @@ | ||
package com.techcourse.dao; | ||
|
||
import java.sql.Connection; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import com.interface21.jdbc.core.JdbcTemplate; | ||
import com.interface21.jdbc.core.jpa.SimpleJpa; | ||
import com.techcourse.config.JpaConfig; | ||
import com.techcourse.config.TransactionManagerConfig; | ||
import com.techcourse.domain.UserHistory; | ||
|
||
public class UserHistoryDao { | ||
|
@@ -22,7 +21,7 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) { | |
this.simpleJpa = new SimpleJpa(jdbcTemplate, JpaConfig.BASE_PACKAGE); | ||
} | ||
|
||
public void log(Connection connection, final UserHistory userHistory) { | ||
simpleJpa.insert(connection, userHistory); | ||
public void log(final UserHistory userHistory) { | ||
simpleJpa.insert(TransactionManagerConfig.getCurrentConnection(), userHistory); | ||
} | ||
Comment on lines
+24
to
26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DAO가 정적 구성요소에 직접 의존 — 테스트 용이성/결합도 이슈 TransactionManagerConfig.getCurrentConnection()에 직접 의존하면 인프라 결합이 커집니다.
🤖 Prompt for AI Agents
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.techcourse.service; | ||
|
||
import com.techcourse.dao.UserDao; | ||
import com.techcourse.dao.UserHistoryDao; | ||
import com.techcourse.domain.User; | ||
import com.techcourse.domain.UserHistory; | ||
|
||
public class AppUserService implements UserService { | ||
|
||
private final UserDao userDao; | ||
private final UserHistoryDao userHistoryDao; | ||
|
||
public AppUserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { | ||
this.userDao = userDao; | ||
this.userHistoryDao = userHistoryDao; | ||
} | ||
|
||
@Override | ||
public User findById(final long id) { | ||
return userDao.findById(id); | ||
} | ||
|
||
@Override | ||
public void insert(final User user) { | ||
userDao.insert(user); | ||
} | ||
|
||
@Override | ||
public void changePassword(final long id, final String newPassword, final String createBy) { | ||
final var user = userDao.findById(id); | ||
user.changePassword(newPassword); | ||
userDao.update(user); | ||
userHistoryDao.log(new UserHistory(user, createBy)); | ||
} | ||
Comment on lines
+28
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사용자 미존재 시 NPE 위험 userDao.findById가 null을 반환할 수 있다면 user.changePassword에서 NPE가 납니다.
🤖 Prompt for AI Agents
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.techcourse.service; | ||
|
||
import com.techcourse.config.TransactionManagerConfig; | ||
import com.techcourse.domain.User; | ||
|
||
public class TxUserService implements UserService { | ||
|
||
private final AppUserService appUserService; | ||
|
||
public TxUserService(AppUserService appUserService) { | ||
this.appUserService = appUserService; | ||
} | ||
|
||
public User findById(final long id) { | ||
return TransactionManagerConfig.executeInTransaction(connection -> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 TxUserService를 통해 트랜잭션 관련 로직이 AppUserService와 분리되니 좋네요! |
||
return appUserService.findById(id); | ||
}); | ||
} | ||
|
||
public void insert(final User user) { | ||
TransactionManagerConfig.executeInTransaction(connection -> { | ||
appUserService.insert(user); | ||
}); | ||
} | ||
|
||
public void changePassword(final long id, final String newPassword, final String createBy) { | ||
TransactionManagerConfig.executeInTransaction(connection -> { | ||
appUserService.changePassword(id, newPassword, createBy); | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,12 @@ | ||
package com.techcourse.service; | ||
|
||
import com.techcourse.config.TransactionManagerConfig; | ||
import com.techcourse.dao.UserDao; | ||
import com.techcourse.dao.UserHistoryDao; | ||
import com.techcourse.domain.User; | ||
import com.techcourse.domain.UserHistory; | ||
|
||
public class UserService { | ||
public interface UserService { | ||
|
||
private final UserDao userDao; | ||
private final UserHistoryDao userHistoryDao; | ||
User findById(final long id); | ||
|
||
public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { | ||
this.userDao = userDao; | ||
this.userHistoryDao = userHistoryDao; | ||
} | ||
void insert(final User user); | ||
|
||
public User findById(final long id) { | ||
return TransactionManagerConfig.executeInTransaction(connection -> { | ||
return userDao.findById(connection, id); | ||
}); | ||
} | ||
|
||
public void insert(final User user) { | ||
TransactionManagerConfig.executeInTransaction(connection -> { | ||
userDao.insert(connection, user); | ||
}); | ||
} | ||
|
||
public void changePassword(final long id, final String newPassword, final String createBy) { | ||
TransactionManagerConfig.executeInTransaction(connection -> { | ||
final var user = userDao.findById(connection, id); | ||
user.changePassword(newPassword); | ||
userDao.update(connection, user); | ||
userHistoryDao.log(connection, new UserHistory(user, createBy)); | ||
}); | ||
} | ||
void changePassword(final long id, final String newPassword, final String createBy); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
싱글턴 초기화 동시성 안전성 보강 필요
멀티스레드 환경에서 getInstance 동시 호출 시 중복 초기화 가능성이 있습니다. 정적 홀더/enum/volatile DCL 등 확실한 초기화 전략을 고려해보셨나요?
🤖 Prompt for AI Agents