-
Notifications
You must be signed in to change notification settings - Fork 300
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
[JDBC 라이브러리 구현하기 - 4단계] 에단(김석호) 미션 제출합니다. #556
Changes from 9 commits
726b9cc
64187a6
c8e1ab0
3a0570a
dd843d0
0d9275d
72a1a83
9a94de3
cb2d487
9011d46
c3f711c
77cc96e
b9c3ddc
d3c8251
15767e9
169fa2d
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 java.util.NoSuchElementException; | ||
|
||
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) | ||
.orElseThrow(() -> new NoSuchElementException("id 값으로 해당하는 User 를 찾을 수 없습니다.")); | ||
} | ||
|
||
@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 = findById(id); | ||
user.changePassword(newPassword); | ||
|
||
userDao.update(user); | ||
userHistoryDao.log(new UserHistory(user, createBy)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,12 @@ | ||
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 org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.dao.DataAccessException; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.SQLException; | ||
import java.util.NoSuchElementException; | ||
public interface UserService { | ||
|
||
public class UserService { | ||
User findById(final long id); | ||
|
||
private static final Logger log = LoggerFactory.getLogger(UserService.class); | ||
void insert(final User user); | ||
|
||
private final UserDao userDao; | ||
private final UserHistoryDao userHistoryDao; | ||
|
||
UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) { | ||
this.userDao = userDao; | ||
this.userHistoryDao = userHistoryDao; | ||
} | ||
|
||
User findById(final long id) { | ||
return userDao.findById(id) | ||
.orElseThrow(() -> new NoSuchElementException("id 값으로 해당하는 User 를 찾을 수 없습니다.")); | ||
} | ||
|
||
public void insert(final User user) { | ||
userDao.insert(user); | ||
} | ||
|
||
void changePassword(final long id, final String newPassword, final String createBy) { | ||
final var user = findById(id); | ||
user.changePassword(newPassword); | ||
|
||
Connection connection = null; | ||
try { | ||
final DataSource dataSource = DataSourceConfig.getInstance(); | ||
connection = dataSource.getConnection(); | ||
connection.setAutoCommit(false); | ||
|
||
userDao.update(connection, user); | ||
userHistoryDao.log(connection, new UserHistory(user, createBy)); | ||
|
||
connection.commit(); | ||
} catch (final SQLException | DataAccessException e) { | ||
rollback(connection); | ||
log.error("SQLException occurred"); | ||
throw new DataAccessException(e); | ||
} finally { | ||
release(connection); | ||
} | ||
} | ||
|
||
private void rollback(final Connection connection) { | ||
if (connection != null) { | ||
try { | ||
connection.rollback(); | ||
} catch (final SQLException ex) { | ||
log.error("rollback callback"); | ||
throw new DataAccessException(ex); | ||
} | ||
} | ||
} | ||
|
||
private void release(final Connection connection) { | ||
if (connection != null) { | ||
try { | ||
connection.setAutoCommit(true); | ||
connection.close(); | ||
} catch (final SQLException e) { | ||
log.error("Cannot close Connection"); | ||
throw new DataAccessException(e); | ||
} | ||
} | ||
} | ||
void changePassword(final long id, final String newPassword, final String createBy); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.techcourse.service.transaction; | ||
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. 패키지를 프레임워크로 옮기는게 낫지 않을까요? |
||
|
||
@FunctionalInterface | ||
public interface TransactionLogicExecutor { | ||
|
||
void run(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.techcourse.service.transaction; | ||
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. 패키지를 프레임워크로 옮기는게 낫지 않을까요? |
||
|
||
import com.techcourse.config.DataSourceConfig; | ||
import org.springframework.dao.DataAccessException; | ||
import org.springframework.jdbc.datasource.DataSourceUtils; | ||
import org.springframework.transaction.TransactionException; | ||
import org.springframework.transaction.support.TransactionSynchronizationManager; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.SQLException; | ||
|
||
public class TransactionTemplate { | ||
|
||
public void executeWithTransaction(final TransactionLogicExecutor transactionLogicExecutor) { | ||
final DataSource dataSource = DataSourceConfig.getInstance(); | ||
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. DataSource는 주입 받도록 만드는게 좋지 않을까요? 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. 이 부분에 대해서 고민을 많이 해봤는데요. 가장 간단하게는 연관관계를 가지고 있어서 테스트와 같이 DataSource를 변경할 수도 있는 상황에서 변경하기가 어렵습니다. 나아가서 일반적인 DataSource 구현체로 HikariCP를 사용하고 있지만, 더 좋은게 나와서 바꾸려면 어려울 수 있습니다. DataSource를 TransactionTemplate을 만들 때 주입받는 방식으로 변경했습니다. |
||
final Connection connection = DataSourceUtils.getConnection(dataSource); | ||
try { | ||
connection.setAutoCommit(false); | ||
|
||
transactionLogicExecutor.run(); | ||
|
||
connection.commit(); | ||
} catch (final SQLException | DataAccessException e) { | ||
rollback(connection); | ||
throw new DataAccessException(e); | ||
} finally { | ||
release(dataSource, connection); | ||
} | ||
} | ||
|
||
private void rollback(final Connection connection) { | ||
if (connection != null) { | ||
try { | ||
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. 2뎁스를 1뎁스로 줄이시기 바랍니다. |
||
connection.rollback(); | ||
} catch (final SQLException ex) { | ||
throw new DataAccessException(ex); | ||
} | ||
} | ||
} | ||
|
||
private static void release(final DataSource dataSource, final Connection connection) { | ||
try { | ||
connection.setAutoCommit(true); | ||
TransactionSynchronizationManager.unbindResource(dataSource); | ||
DataSourceUtils.releaseConnection(connection, dataSource); | ||
} catch (final SQLException e) { | ||
throw new TransactionException("Failed to change auto commit to true"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.techcourse.service.transaction; | ||
|
||
import com.techcourse.domain.User; | ||
import com.techcourse.service.UserService; | ||
|
||
public class TxUserService implements UserService { | ||
|
||
private final UserService userService; | ||
private final TransactionTemplate transactionTemplate; | ||
|
||
public TxUserService(final UserService userService, final TransactionTemplate transactionTemplate) { | ||
this.userService = userService; | ||
this.transactionTemplate = transactionTemplate; | ||
} | ||
|
||
@Override | ||
public User findById(final long id) { | ||
return userService.findById(id); | ||
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. findById는 왜 트랜잭션을 적용하지 않나요? 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. 수업에서 들은대로 readonly로 트랜잭션 걸어주도록 리팩토링했습니다 |
||
} | ||
|
||
@Override | ||
public void insert(final User user) { | ||
transactionTemplate.executeWithTransaction(() -> userService.insert(user)); | ||
} | ||
|
||
@Override | ||
public void changePassword(final long id, final String newPassword, final String createBy) { | ||
transactionTemplate.executeWithTransaction(() -> userService.changePassword(id, newPassword, 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.
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.