- 
                Notifications
    You must be signed in to change notification settings 
- Fork 379
[4단계 - Transaction synchronization 적용하기] 대니(정구홍) 미션 제출합니다. #1205
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: goohong
Are you sure you want to change the base?
Changes from 10 commits
8657cc1
              be89503
              0ebe511
              f14febf
              385ce9b
              069d404
              ce0f941
              079fb6f
              76c3eb0
              dbc36b2
              048aabe
              41057d7
              f06df39
              4e2c7ba
              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 | 
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.techcourse.service; | ||
|  | ||
| import com.techcourse.dao.UserDao; | ||
| import com.techcourse.dao.UserHistoryDao; | ||
| import com.techcourse.domain.User; | ||
| import com.techcourse.domain.UserHistory; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|  | ||
| public class AppUserService implements UserService { | ||
|  | ||
| private static final Logger log = LoggerFactory.getLogger(AppUserService.class); | ||
|  | ||
| 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 save(final User user) { | ||
| userDao.insert(user); | ||
| } | ||
|  | ||
| @Override | ||
| 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)); | ||
| } | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.techcourse.service; | ||
|  | ||
| import com.interface21.transaction.TransactionTemplate; | ||
| import com.techcourse.config.DataSourceConfig; | ||
| import com.techcourse.domain.User; | ||
|  | ||
| public class TransactionUserService implements UserService { | ||
|  | ||
| private final UserService userService; | ||
| private final TransactionTemplate transactionTemplate; | ||
|  | ||
| public TransactionUserService(final UserService userService) { | ||
| this.userService = userService; | ||
| this.transactionTemplate = new TransactionTemplate(DataSourceConfig.getInstance()); | ||
| } | ||
|  | ||
| @Override | ||
| public User findById(final long id) { | ||
| return transactionTemplate.executeReadOnly(() -> userService.findById(id)); | ||
| } | ||
|  | ||
| @Override | ||
| public void save(final User user) { | ||
| transactionTemplate.execute(() -> { | ||
| userService.save(user); | ||
| return null; | ||
| }); | ||
|          | ||
| } | ||
|  | ||
| @Override | ||
| public void changePassword(final long id, final String newPassword, final String createBy) { | ||
| transactionTemplate.execute(() -> { | ||
| userService.changePassword(id, newPassword, createBy); | ||
| return null; | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -1,70 +1,12 @@ | ||
| package com.techcourse.service; | ||
|  | ||
| import com.interface21.dao.DataAccessException; | ||
| 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 org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|  | ||
| import java.sql.Connection; | ||
| import java.sql.SQLException; | ||
| public interface UserService { | ||
|  | ||
| public class UserService { | ||
| User findById(final long id); | ||
|  | ||
| private static final Logger log = LoggerFactory.getLogger(UserService.class); | ||
| void save(final User user); | ||
|  | ||
| private final UserDao userDao; | ||
| private final UserHistoryDao userHistoryDao; | ||
|  | ||
| public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { | ||
| this.userDao = userDao; | ||
| this.userHistoryDao = userHistoryDao; | ||
| } | ||
|  | ||
| public User findById(final long id) { | ||
| return userDao.findById(id); | ||
| } | ||
|  | ||
| public void insert(final User user) { | ||
| userDao.insert(user); | ||
| } | ||
|  | ||
| public void changePassword(final long id, final String newPassword, final String createBy) { | ||
| Connection conn = null; | ||
| try { | ||
| conn = DataSourceConfig.getInstance().getConnection(); | ||
| conn.setAutoCommit(false); | ||
|  | ||
| final var user = findById(id); | ||
| user.changePassword(newPassword); | ||
|  | ||
| userDao.update(conn, user); | ||
| userHistoryDao.log(conn, new UserHistory(user, createBy)); | ||
|  | ||
| conn.commit(); | ||
| } catch (Exception e) { | ||
| log.atError().log("Transaction is being rolled back", e); | ||
| if (conn != null) { | ||
| try { | ||
| conn.rollback(); | ||
| } catch (SQLException ex) { | ||
| throw new DataAccessException("Rollback failed: " + ex.getMessage(), ex); | ||
| } | ||
| } | ||
| throw new DataAccessException("Failed to change password: " + e.getMessage(), e); | ||
|  | ||
| } finally { | ||
| if (conn != null) { | ||
| try { | ||
| conn.setAutoCommit(true); | ||
| conn.close(); | ||
| } catch (SQLException e) { | ||
| log.atError().log("Error closing connection", e); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| void changePassword(final long id, final String newPassword, final String createdBy); | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -30,6 +30,7 @@ public static Connection getConnection(DataSource dataSource) throws CannotGetJd | |
| public static void releaseConnection(Connection connection, DataSource dataSource) { | ||
| try { | ||
| connection.close(); | ||
| TransactionSynchronizationManager.unbindResource(dataSource); | ||
| 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. DataSourceUtils의 getConnection(), releaseConnection()에서 트랜잭션 바인딩, 언바인딩 코드가 포함되어있는 것에 대해 어떻게 생각하시나요!? 아무런 정보 없이 트랜잭션이 실행되고 있는 것 같지 않나요!? 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. 동의합니다. getConnection, releaseConnection을 호출하는 것만으로 트랜잭션을 bind,unbind하는 것은 불필요하다는 생각이 듭니다. transaction을 요구하는 연결이라면 bind, 아니라면 굳이 bind하지 않는 형태로 분기처리가 필요할 것 같습니다! 실제 스프링에서도 그렇게 적용한 것 같더라고요 👍 | ||
| } catch (SQLException ex) { | ||
| throw new CannotGetJdbcConnectionException("Failed to close JDBC Connection"); | ||
| } | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.interface21.transaction; | ||
|  | ||
| import java.sql.SQLException; | ||
|  | ||
| @FunctionalInterface | ||
| public interface TransactionCallback<T> { | ||
| T doInTransaction() throws SQLException; | ||
| } | 
Uh oh!
There was an error while loading. Please reload this page.