Skip to content

Commit

Permalink
[JDBC 라이브러리 구현하기 - 4단계] 히이로(문제웅) 미션 제출합니다. (#569)
Browse files Browse the repository at this point in the history
* feat: UserService의 changePassword 로직에 트랜잭션 적용

* refactor: SQLExceptionTranslator static화

* feat: DataSourceUtils 클래스를 통한 커넥션 동기화 기능 구현

* feat: AppService와 TxService 분리

* chore: 윈도우용 테스트 설정 변경

* test: 트랜잭션 학습 테스트 진행

* refactor: JdbcTemplate 가변인자 nullable 어노테이션 추가 선언

* refactor: callback 메서드 checked Exception에서 DataAccessException 제거

* refactor: JdbcTemplate update메서드도 executeQuery를 사용하도록 리팩토링

* refactor: JdbcTemplate 메서드 인자 final 선언

* refactor: TxUserService에 템플릿 콜백 패턴 적용
  • Loading branch information
MoonJeWoong authored Oct 10, 2023
1 parent 400df75 commit abd233c
Show file tree
Hide file tree
Showing 19 changed files with 222 additions and 146 deletions.
21 changes: 5 additions & 16 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.techcourse.dao;

import com.techcourse.domain.User;
import java.sql.Connection;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
Expand All @@ -25,6 +24,7 @@ public void insert(final User user) {
final var sql = "insert into users (account, password, email) values (?, ?, ?)";
jdbcTemplate.update(
sql,
false,
user.getAccount(),
user.getPassword(),
user.getEmail()
Expand All @@ -35,18 +35,7 @@ public void update(final User user) {
String sql = "update users set account = ?, password = ?, email = ? where id = ?";
jdbcTemplate.update(
sql,
user.getAccount(),
user.getPassword(),
user.getPassword(),
user.getId()
);
}

public void update(final Connection conn, final User user) {
String sql = "update users set account = ?, password = ?, email = ? where id = ?";
jdbcTemplate.update(
conn,
sql,
false,
user.getAccount(),
user.getPassword(),
user.getPassword(),
Expand All @@ -56,16 +45,16 @@ public void update(final Connection conn, final User user) {

public List<User> findAll() {
String sql = "select id, account, password, email from users";
return jdbcTemplate.query(sql, USER_ROW_MAPPER);
return jdbcTemplate.query(sql, USER_ROW_MAPPER, false);
}

public User findById(final Long id) {
final var sql = "select id, account, password, email from users where id = ?";
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, id);
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, false, id);
}

public User findByAccount(final String account) {
String sql = "select id, account, password, email from users where account = ?";
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, account);
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, false, account);
}
}
9 changes: 2 additions & 7 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
package com.techcourse.dao;

import com.techcourse.domain.UserHistory;
import java.sql.Connection;
import org.springframework.jdbc.core.JdbcTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserHistoryDao {

private static final Logger log = LoggerFactory.getLogger(UserHistoryDao.class);

private final JdbcTemplate jdbcTemplate;

public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void log(final Connection conn, final UserHistory userHistory) {
public void log(final UserHistory userHistory) {
final var sql = "insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(
conn,
sql,
false,
userHistory.getUserId(),
userHistory.getAccount(),
userHistory.getPassword(),
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/com/techcourse/service/AppUserService.java
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 = 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,9 @@
package com.techcourse.service;

import java.sql.SQLException;

@FunctionalInterface
public interface TransactionCallback<T> {

T doInTransaction() throws SQLException;
}
83 changes: 83 additions & 0 deletions app/src/main/java/com/techcourse/service/TxUserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.techcourse.service;

import com.techcourse.config.DataSourceConfig;
import com.techcourse.domain.User;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLExceptionTranslator;

public class TxUserService implements UserService {

private final DataSource dataSource;
private final UserService userService;

public TxUserService(UserService userService) {
this.dataSource = DataSourceConfig.getInstance();
this.userService = userService;
}

@Override
public User findById(long id) {
TransactionCallback<User> action = () -> userService.findById(id);
return doTransactionWithReturn(action);
}

@Override
public void insert(User user) {
TransactionCallback<Void> action = () -> {
userService.insert(user);
return null;
};
doTransactionWithoutReturn(action);
}

@Override
public void changePassword(final long id, final String newPassword, final String createBy) {
TransactionCallback<Void> action = () -> {
userService.changePassword(id, newPassword, createBy);
return null;
};
doTransactionWithoutReturn(action);
}

private <T> T doTransactionWithReturn(TransactionCallback<T> action) {
Connection conn = DataSourceUtils.getConnection(dataSource);
T result;
try {
try {
conn.setAutoCommit(false);
result = action.doInTransaction();
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
}
} catch (SQLException e) {
throw SQLExceptionTranslator.translate("", e);
} finally {
DataSourceUtils.releaseConnection(dataSource);
}
return result;
}

private <T> void doTransactionWithoutReturn(TransactionCallback<T> action) {
Connection conn = DataSourceUtils.getConnection(dataSource);
try {
try {
conn.setAutoCommit(false);
action.doInTransaction();
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
}
} catch (SQLException e) {
throw SQLExceptionTranslator.translate("", e);
} finally {
DataSourceUtils.releaseConnection(dataSource);
}
}
}

50 changes: 5 additions & 45 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,51 +1,11 @@
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 java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.jdbc.support.SQLExceptionTranslator;

public class UserService {
public interface UserService {

private final UserDao userDao;
private final UserHistoryDao userHistoryDao;
private final DataSource dataSource;

public UserService(final UserDao userDao, final UserHistoryDao userHistoryDao) {
this.userDao = userDao;
this.userHistoryDao = userHistoryDao;
this.dataSource = DataSourceConfig.getInstance();
}

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) {
final var user = findById(id);
user.changePassword(newPassword);

try (final Connection conn = dataSource.getConnection()) {
try {
conn.setAutoCommit(false);
userDao.update(conn, user);
userHistoryDao.log(conn, new UserHistory(user, createBy));
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
}
} catch (SQLException e) {
throw SQLExceptionTranslator.translate("", e);
}
}
User findById(final long id);
void insert(final User user);
void changePassword(final long id, final String newPassword, final String createBy);
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.techcourse.dao.UserHistoryDao;
import com.techcourse.domain.UserHistory;
import java.sql.Connection;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

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

@Override
public void log(Connection conn, final UserHistory userHistory) {
public void log(final UserHistory userHistory) {
throw new DataAccessException();
}
}
7 changes: 5 additions & 2 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void setUp() {
@Test
void testChangePassword() {
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
final var userService = new AppUserService(userDao, userHistoryDao);

final var newPassword = "qqqqq";
final var createBy = "gugu";
Expand All @@ -46,7 +46,10 @@ void testChangePassword() {
void testTransactionRollback() {
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
// 애플리케이션 서비스
final var appUserService = new AppUserService(userDao, userHistoryDao);
// 트랜잭션 서비스 추상화
final var userService = new TxUserService(appUserService);

final var newPassword = "newPassword";
final var createBy = "gugu";
Expand Down
Loading

0 comments on commit abd233c

Please sign in to comment.