Skip to content

Commit

Permalink
[JDBC 라이브러리 구현하기 - 3,4단계] 스플릿(박상현) 미션 제출합니다. (#537)
Browse files Browse the repository at this point in the history
* refactor: DataSourceUtils 클래스를 이용하여 Connection 사용하도록 변경

JdbcTemplateBase 의 트랜잭션 관리 책임 제거

* feat: UserService 추상화 및 AppUserService 구현

* refactor: UserHistoryDao JdbcTemplate 사용 코드로 리펙토링

* feat: TransactionService 추상화 및 TransactionUserService 구현

* chore: Exception 패키지로 이동

* study: Proxy 태스트 0

* refactor: TransactionTemplate 을 사용하여 중복 코드 제거
  • Loading branch information
splitCoding authored Oct 17, 2023
1 parent 46b0352 commit 4e270c7
Show file tree
Hide file tree
Showing 18 changed files with 241 additions and 168 deletions.
56 changes: 12 additions & 44 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
@@ -1,62 +1,30 @@
package com.techcourse.dao;

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

import java.time.LocalDateTime;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;

public class UserHistoryDao {

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

private final DataSource dataSource;
private final JdbcTemplate jdbcTemplate;

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

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

public void log(final UserHistory userHistory) {
final var sql = "insert into user_history (user_id, account, password, email, created_at, created_by) values (?, ?, ?, ?, ?, ?)";

Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(sql);

log.debug("query : {}", sql);

pstmt.setLong(1, userHistory.getUserId());
pstmt.setString(2, userHistory.getAccount());
pstmt.setString(3, userHistory.getPassword());
pstmt.setString(4, userHistory.getEmail());
pstmt.setObject(5, userHistory.getCreatedAt());
pstmt.setString(6, userHistory.getCreateBy());
pstmt.executeUpdate();
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
} finally {
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException ignored) {}

try {
if (conn != null) {
conn.close();
}
} catch (SQLException ignored) {}
}
final long userId = userHistory.getUserId();
final String account = userHistory.getAccount();
final String password = userHistory.getPassword();
final String email = userHistory.getEmail();
final LocalDateTime createdAt = userHistory.getCreatedAt();
final String createBy = userHistory.getCreateBy();
jdbcTemplate.update(sql, userId, account, password, email, createdAt, createBy);
}
}
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));
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/techcourse/service/TransactionService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.techcourse.service;

public abstract class TransactionService<T> {

protected T appService;

protected TransactionService(final T appService) {
this.appService = appService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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 java.sql.Connection;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionTemplate;

public class TransactionUserService extends TransactionService<UserService> implements UserService {

public TransactionUserService(final UserDao userDao, final UserHistoryDao userHistoryDao) {
super(new AppUserService(userDao, userHistoryDao));
}

public User findById(final long id) {
final Connection connection = DataSourceUtils.getConnection(DataSourceConfig.getInstance());
final User user = appService.findById(id);
DataSourceUtils.releaseConnection(connection, DataSourceConfig.getInstance());
return user;
}

public void insert(final User user) {
TransactionTemplate.executeWithoutReturn(() -> appService.insert(user), DataSourceConfig.getInstance());
}

public void changePassword(final long id, final String newPassword, final String createBy) {
TransactionTemplate.executeWithoutReturn(
() -> appService.changePassword(id, newPassword, createBy), DataSourceConfig.getInstance()
);
}
}
28 changes: 4 additions & 24 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
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 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 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);
userDao.update(user);
userHistoryDao.log(new UserHistory(user, createBy));
}
void changePassword(final long id, final String newPassword, final String createBy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.JdbcTemplateBase;
import org.springframework.jdbc.core.error.TableNotFoundSqlRuntimeException;
import org.springframework.jdbc.core.error.exception.ColumnSqlRuntimeException;
import org.springframework.jdbc.core.error.exception.DataConversionSqlRuntimeException;
import org.springframework.jdbc.core.error.exception.MethodNotAllowedSqlRuntimeException;
import org.springframework.jdbc.core.error.exception.SyntaxSqlRuntimeException;
import org.springframework.jdbc.core.error.exception.TableNotFoundSqlRuntimeException;
import org.springframework.jdbc.core.mapper.ResultSetObjectMapper;

class SqlRuntimeExceptionTest {
Expand Down Expand Up @@ -61,7 +61,7 @@ void executeQuery_insert() {
//when
//then
assertThatThrownBy(
() -> jdbcTemplateExecutionBase.executionBaseWithNonReturn(sql, PreparedStatement::executeQuery, true)
() -> jdbcTemplateExecutionBase.executionBaseWithNonReturn(sql, PreparedStatement::executeQuery)
).isInstanceOf(MethodNotAllowedSqlRuntimeException.ExecuteQuerySqlRuntimeException.class);
}

Expand Down
18 changes: 8 additions & 10 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package com.techcourse.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.techcourse.config.DataSourceConfig;
import com.techcourse.dao.UserDao;
import com.techcourse.dao.UserHistoryDao;
import com.techcourse.domain.User;
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

@Disabled
class UserServiceTest {

private JdbcTemplate jdbcTemplate;
Expand All @@ -33,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 TransactionUserService(userDao, userHistoryDao);

final var newPassword = "qqqqq";
final var createBy = "gugu";
Expand All @@ -48,13 +46,13 @@ void testChangePassword() {
void testTransactionRollback() {
// 트랜잭션 롤백 테스트를 위해 mock으로 교체
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate);
final var userService = new UserService(userDao, userHistoryDao);
final var userService = new TransactionUserService(userDao, userHistoryDao);

final var newPassword = "newPassword";
final var createBy = "gugu";
// 트랜잭션이 정상 동작하는지 확인하기 위해 의도적으로 MockUserHistoryDao에서 예외를 발생시킨다.
assertThrows(DataAccessException.class,
() -> userService.changePassword(1L, newPassword, createBy));
() -> userService.changePassword(1L, newPassword, createBy));

final var actual = userService.findById(1L);

Expand Down
41 changes: 6 additions & 35 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,30 @@ public JdbcTemplate(final DataSource dataSource) {
}

public <T> T executeQueryForObject(final String sql, final ResultSetObjectMapper<T> mapper) {
return super.executeQueryForObjectBase(sql, mapper, new Object[]{}, TRANSACTION_ENABLE);
return super.executeQueryForObjectBase(sql, mapper, new Object[]{});
}

public <T> T executeQueryForObject(final String sql,
final ResultSetObjectMapper<T> mapper,
final Object... params) {
return super.executeQueryForObjectBase(sql, mapper, params, TRANSACTION_ENABLE);
}

public <T> T executeQueryForObjectWithoutTransaction(final String sql, final ResultSetObjectMapper<T> mapper) {
return super.executeQueryForObjectBase(sql, mapper, new Object[]{}, TRANSACTION_ENABLE);
}

public <T> T executeQueryForObjectWithoutTransaction(final String sql,
final ResultSetObjectMapper<T> mapper,
final Object... params) {
return super.executeQueryForObjectBase(sql, mapper, params, TRANSACTION_DISABLE);
return super.executeQueryForObjectBase(sql, mapper, params);
}

public <T> List<T> executeQueryForObjects(final String sql, final ResultSetObjectMapper<T> mapper) {
return super.executeQueryForObjectsBase(sql, mapper, new Object[]{}, TRANSACTION_ENABLE);
return super.executeQueryForObjectsBase(sql, mapper, new Object[]{});
}

public <T> List<T> executeQueryForObjects(final String sql,
final ResultSetObjectMapper<T> mapper,
final Object... params) {
return super.executeQueryForObjectsBase(sql, mapper, params, TRANSACTION_ENABLE);
}

public <T> List<T> executeQueryForObjectsWithoutTransaction(final String sql,
final ResultSetObjectMapper<T> mapper) {
return super.executeQueryForObjectsBase(sql, mapper, new Object[]{}, TRANSACTION_DISABLE);
}

public <T> List<T> executeQueryForObjectsWithoutTransaction(final String sql,
final ResultSetObjectMapper<T> mapper,
final Object... params) {
return super.executeQueryForObjectsBase(sql, mapper, params, TRANSACTION_DISABLE);
return super.executeQueryForObjectsBase(sql, mapper, params);
}

public void update(final String sql) {
updateBase(sql, new Object[]{}, TRANSACTION_ENABLE);
updateBase(sql, new Object[]{});
}

public void update(final String sql, Object... params) {
updateBase(sql, params, TRANSACTION_ENABLE);
}

public void updateWithoutTransaction(final String sql) {
updateBase(sql, new Object[]{}, TRANSACTION_DISABLE);
}

public void updateWithoutTransaction(final String sql, Object... params) {
updateBase(sql, params, TRANSACTION_DISABLE);
updateBase(sql, params);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,32 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.error.SqlExceptionConverter;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class JdbcTemplateBase {

private static final Logger log = LoggerFactory.getLogger(JdbcTemplateBase.class);
protected static final boolean TRANSACTION_ENABLE = false;
protected static final boolean TRANSACTION_DISABLE = true;
private final DataSource dataSource;

public JdbcTemplateBase(final DataSource dataSource) {
this.dataSource = dataSource;
}

public void executionBaseWithNonReturn(final String sql,
final JdbcTemplateVoidExecution execution,
final boolean enableTransaction) {
try (
final Connection connection = dataSource.getConnection();
final PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
connection.setAutoCommit(!enableTransaction);
public void executionBaseWithNonReturn(final String sql, final JdbcTemplateVoidExecution execution) {
final Connection connection = DataSourceUtils.getConnection(dataSource);
try (final PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
log.debug("query : {}", sql);
execution.execute(preparedStatement);
if (connection.getAutoCommit() == TRANSACTION_ENABLE) {
connection.commit();
}
} catch (SQLException e) {
throw SqlExceptionConverter.convert(e);
}
}

public <T> T executionBaseWithReturn(final String sql,
final JdbcTemplateExecutor<T> execution,
final boolean enableTransaction) {

try (final Connection connection = dataSource.getConnection();
final PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
connection.setAutoCommit(!enableTransaction);
public <T> T executionBaseWithReturn(final String sql, final JdbcTemplateExecutor<T> execution) {
final Connection connection = DataSourceUtils.getConnection(dataSource);
try (final PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
log.debug("query : {}", sql);
final T result = execution.execute(preparedStatement);
if (connection.getAutoCommit() == TRANSACTION_ENABLE) {
connection.commit();
}
return result;
return execution.execute(preparedStatement);
} catch (SQLException e) {
throw SqlExceptionConverter.convert(e);
}
Expand Down
Loading

0 comments on commit 4e270c7

Please sign in to comment.