Skip to content
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단계] 콩하나(최한빈) 미션 제출합니다. #560

Merged
merged 8 commits into from
Oct 11, 2023
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@
- [x] 트랜잭션 경계 설정을 할 수 있다.
- [x] 서비스에서 커넥션을 가져온다.
- [x] dao의 메소드를 호출할 때 커넥션을 전달한다.

## 4.Transaction synchronization 적용하기
- [x] Transaction synchronization 적용
- [x] DataSourceUtils를 사용해 connection 객체를 가져온다.
- [x] TransactionSynchronizationManager가 잘 동작하도록 구현한다.
- [x] 트랜잭션 서비스 추상화
- [x] 비즈니스 로직과 데이터 액세스 로직 분리
23 changes: 10 additions & 13 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,43 @@ public UserDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void insert(final Connection connection, final User user) {
public void insert(final User user) {
final var sql = "insert into users (account, password, email) values (?, ?, ?)";

jdbcTemplate.execute(connection, sql, user.getAccount(), user.getPassword(), user.getEmail());
jdbcTemplate.execute(sql, user.getAccount(), user.getPassword(), user.getEmail());
}

public void update(final Connection connection, final User user) {
public void update(final User user) {
final var sql = "update users set (account, password, email) = (?, ?, ?) where id = ?";

jdbcTemplate.execute(connection, sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
jdbcTemplate.execute(sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
}

public List<User> findAll(final Connection connection) {
public List<User> findAll() {
final var sql = "select id, account, password, email from users";

return jdbcTemplate.findAll(connection,
sql,
return jdbcTemplate.findAll(sql,
rs -> new User(rs.getLong("id"),
rs.getString("account"),
rs.getString("password"),
rs.getString("email")));
}

public User findById(final Connection connection, final Long id) {
public User findById(final Long id) {
final var sql = "select id, account, password, email from users where id = ?";

return jdbcTemplate.find(connection,
sql,
return jdbcTemplate.find(sql,
rs -> new User(rs.getLong("id"),
rs.getString("account"),
rs.getString("password"),
rs.getString("email")),
id);
}

public User findByAccount(final Connection connection, final String account) {
public User findByAccount(final String account) {
final var sql = "select id, account, password, email from users where account = ?";

return jdbcTemplate.find(connection,
sql,
return jdbcTemplate.find(sql,
rs -> new User(rs.getLong("id"),
rs.getString("account"),
rs.getString("password"),
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ public UserHistoryDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void log(final Connection connection, 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.execute(connection, sql, userHistory.getUserId(), userHistory.getAccount(),
jdbcTemplate.execute(sql, userHistory.getUserId(), userHistory.getAccount(),
userHistory.getPassword(), userHistory.getEmail(), userHistory.getCreatedAt(), userHistory.getCreateBy());
}
}
32 changes: 32 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,32 @@
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;
}

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));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.techcourse.service;

import java.sql.Connection;

@FunctionalInterface
public interface TransactionalDaoExecutor<T> {

T execute(final Connection connection);
T execute();
}
62 changes: 62 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,62 @@
package com.techcourse.service;

import com.techcourse.domain.User;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplateException;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class TxUserService implements UserService {

private final UserService userService;
private final DataSource dataSource;

public TxUserService(final UserService userService, final DataSource dataSource) {
this.userService = userService;
this.dataSource = dataSource;
}

@Override
public User findById(final long id) {
return doService(() -> userService.findById(id));
}

private <T> T doService(final TransactionalDaoExecutor<T> executor) {
try {
return doDaoWithTransaction(executor);
} catch (SQLException e) {
throw new DataBaseAccessException(e.getMessage());
}
}

private <T> T doDaoWithTransaction(final TransactionalDaoExecutor<T> executor) throws SQLException {
Connection connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
try {
return executor.execute();
} catch (JdbcTemplateException e) {
connection.rollback();
throw new DataBaseAccessException(e.getMessage());
} finally {
connection.setAutoCommit(true);
DataSourceUtils.releaseConnection(connection, dataSource);
}
}

@Override
public void insert(final User user) {
doService(() -> {
userService.insert(user);
return null;
});
}

@Override
public void changePassword(final long id, final String newPassword, final String createBy) {
doService(() -> {
userService.changePassword(id, newPassword, createBy);
return null;
});
}
}
63 changes: 4 additions & 59 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,67 +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;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplateException;

public class UserService {
public interface UserService {

private final DataSource dataSource;
private final UserDao userDao;
private final UserHistoryDao userHistoryDao;
User findById(final long id);

public UserService(final DataSource dataSource, final UserDao userDao, final UserHistoryDao userHistoryDao) {
this.dataSource = dataSource;
this.userDao = userDao;
this.userHistoryDao = userHistoryDao;
}
void insert(final User user);

public User findById(final long id) {
return doService(connection -> userDao.findById(connection, id));
}

private <T> T doService(final TransactionalDaoExecutor<T> executor) {
try {
Connection connection = dataSource.getConnection();
return doDaoWithTransaction(executor, connection);
} catch (SQLException e) {
throw new DataBaseAccessException(e.getMessage());
}
}

private <T> T doDaoWithTransaction(final TransactionalDaoExecutor<T> executor,
final Connection connection) throws SQLException {
connection.setAutoCommit(false);
try {
return executor.execute(connection);
} catch (JdbcTemplateException e) {
connection.rollback();
throw new DataBaseAccessException(e.getMessage());
} finally {
connection.setAutoCommit(true);
}
}

public void insert(final User user) {
doService(connection -> {
userDao.insert(connection, user);
return null;
});
}

public void changePassword(final long id, final String newPassword, final String createBy) {
final var user = findById(id);
user.changePassword(newPassword);

doService(connection -> {
userDao.update(connection, user);
userHistoryDao.log(connection, new UserHistory(user, createBy));
return null;
});
}
void changePassword(final long id, final String newPassword, final String createBy);
}
21 changes: 10 additions & 11 deletions app/src/test/java/com/techcourse/dao/UserDaoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void setup() throws SQLException {
connection = dataSource.getConnection();
userDao = new UserDao(dataSource);
final var user = new User("gugu", "password", "[email protected]");
userDao.insert(connection, user);
userDao.insert(user);
}

@AfterEach
Expand All @@ -39,22 +39,22 @@ void initTable() throws SQLException {

@Test
void findAll() throws SQLException {
final var users = userDao.findAll(connection);
final var users = userDao.findAll();

assertThat(users).isNotEmpty();
}

@Test
void findById() {
final var user = userDao.findById(connection, 1L);
final var user = userDao.findById(1L);

assertThat(user.getAccount()).isEqualTo("gugu");
}

@Test
void findByAccount() {
final var account = "gugu";
final var user = userDao.findByAccount(connection, account);
final var user = userDao.findByAccount(account);

assertThat(user.getAccount()).isEqualTo(account);
}
Expand All @@ -63,22 +63,21 @@ void findByAccount() {
void insert() {
final var account = "insert-gugu";
final var user = new User(account, "password", "[email protected]");
userDao.insert(connection, user);
userDao.insert(user);

final var actual = userDao.findById(connection, 2L);

assertThat(actual.getAccount()).isEqualTo(account);
final var actual = userDao.findByAccount("insert-gugu");
assertThat(actual).isNotNull();
}

@Test
void update() {
final var newPassword = "password99";
final var user = userDao.findById(connection, 1L);
final var user = userDao.findById(1L);
user.changePassword(newPassword);

userDao.update(connection, user);
userDao.update(user);

final var actual = userDao.findById(connection, 1L);
final var actual = userDao.findById(1L);

assertThat(actual.getPassword()).isEqualTo(newPassword);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public MockUserHistoryDao(final JdbcTemplate jdbcTemplate) {
}

@Override
public void log(final Connection connection, final UserHistory userHistory) {
public void log(final UserHistory userHistory) {
throw new JdbcTemplateException("공습 경보!!!") {
@Override
public String getMessage() {
Expand Down
11 changes: 7 additions & 4 deletions app/src/test/java/com/techcourse/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ class UserServiceTest {
private DataSource dataSource;

@BeforeEach
void setUp() throws SQLException {
void setUp() {
this.dataSource = DataSourceConfig.getInstance();
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.userDao = new UserDao(jdbcTemplate);

DatabasePopulatorUtils.execute(dataSource);
final var user = new User("gugu", "password", "[email protected]");
userDao.insert(dataSource.getConnection(), user);
userDao.insert(user);
}

@Test
void testChangePassword() {
final var userHistoryDao = new UserHistoryDao(jdbcTemplate);
final var userService = new UserService(dataSource, userDao, userHistoryDao);
final var userService = new AppUserService(userDao, userHistoryDao);

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

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