Skip to content

Commit

Permalink
[JDBC 라이브러리 구현하기 - 3단계] 디노(신종화) 미션 제출합니다. (#457)
Browse files Browse the repository at this point in the history
* transaction 학습 테스트 Stage1 진행

* transaction 학습 테스트 Stage2 진행

* refactor: UserHistoryDao가 jdbcTemplate을 사용하도록 변경

* refactor: changePassword에 트랜잭션 적용
  • Loading branch information
jjongwa authored Oct 7, 2023
1 parent 037523d commit e7816c9
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 89 deletions.
6 changes: 6 additions & 0 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.techcourse.dao;

import com.techcourse.domain.User;
import java.sql.Connection;
import java.util.List;
import javax.sql.DataSource;
import org.slf4j.Logger;
Expand Down Expand Up @@ -41,6 +42,11 @@ public void update(final User user) {
jdbcTemplate.execute(sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
}

public void update(final Connection conn, final User user) {
final var sql = "update users set account = ?, password = ?, email = ? where id = ?";
jdbcTemplate.execute(conn, sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
}

public List<User> findAll() {
final var sql = "select id, account, password, email from users";
return jdbcTemplate.query(sql, USER_ROW_MAPPER);
Expand Down
69 changes: 26 additions & 43 deletions app/src/main/java/com/techcourse/dao/UserHistoryDao.java
Original file line number Diff line number Diff line change
@@ -1,62 +1,45 @@
package com.techcourse.dao;

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

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) {}
}
public void log(final Connection conn, final UserHistory userHistory) {
final var sql = "insert into user_history ("
+ "user_id, "
+ "account, "
+ "password, "
+ "email, "
+ "created_at, "
+ "created_by) "
+ "values (?, ?, ?, ?, ?, ?)";

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

jdbcTemplate.execute(conn, sql,
userHistory.getUserId(),
userHistory.getAccount(),
userHistory.getPassword(),
userHistory.getEmail(),
userHistory.getCreatedAt(),
userHistory.getCreateBy()
);
}
}
35 changes: 31 additions & 4 deletions app/src/main/java/com/techcourse/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
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.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class UserService {

Expand All @@ -24,9 +30,30 @@ public void insert(final User 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));
final DataSource dataSource = DataSourceConfig.getInstance();
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);

final User user = findById(id);
user.changePassword(newPassword);

userDao.update(conn, user);
userHistoryDao.log(conn, new UserHistory(user, createBy));
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException exception) {
throw new DataAccessException(exception);
}
} finally {
try {
conn.close();
} catch (SQLException e) {
throw new DataAccessException(e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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 @@ -12,7 +13,7 @@ public MockUserHistoryDao(final JdbcTemplate jdbcTemplate) {
}

@Override
public void log(final UserHistory userHistory) {
public void log(final Connection conn, final UserHistory userHistory) {
throw new DataAccessException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

@Disabled
class UserServiceTest {

private JdbcTemplate jdbcTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public void execute(final String sql, final Object... objects) {
}
}

public void execute(final Connection conn, final String sql, final Object... objects) {
try (final PreparedStatement pstmt = preparedStatementAndSetValue(conn, sql, objects)) {
commandQuery(pstmt);
} catch (SQLException e) {
throw new DataAccessException(e);
}
}

public <T> List<T> query(final String sql, final RowMapper<T> rowMapper, final Object... objects) {
try (final Connection conn = dataSource.getConnection();
final PreparedStatement pstmt = preparedStatementAndSetValue(conn, sql, objects)
Expand Down
39 changes: 20 additions & 19 deletions study/src/test/java/transaction/stage1/Stage1Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
* Read phenomena | Dirty reads | Non-repeatable reads | Phantom reads
* Isolation level | | |
* -----------------|-------------|----------------------|--------------
* Read Uncommitted | | |
* Read Committed | | |
* Repeatable Read | | |
* Serializable | | |
* Read Uncommitted | + | + | +
* Read Committed | - | + | +
* Repeatable Read | - | - | +
* Serializable | - | - | -
*/
class Stage1Test {

Expand All @@ -58,10 +58,10 @@ private void setUp(final DataSource dataSource) {
* Read phenomena | Dirty reads
* Isolation level |
* -----------------|-------------
* Read Uncommitted |
* Read Committed |
* Repeatable Read |
* Serializable |
* Read Uncommitted | +
* Read Committed | -
* Repeatable Read | -
* Serializable | -
*/
@Test
void dirtyReading() throws SQLException {
Expand Down Expand Up @@ -111,10 +111,10 @@ void dirtyReading() throws SQLException {
* Read phenomena | Non-repeatable reads
* Isolation level |
* -----------------|---------------------
* Read Uncommitted |
* Read Committed |
* Repeatable Read |
* Serializable |
* Read Uncommitted | +
* Read Committed | +
* Repeatable Read | -
* Serializable | -
*/
@Test
void noneRepeatable() throws SQLException {
Expand All @@ -130,7 +130,7 @@ void noneRepeatable() throws SQLException {
connection.setAutoCommit(false);

// 적절한 격리 레벨을 찾는다.
final int isolationLevel = Connection.TRANSACTION_NONE;
final int isolationLevel = Connection.TRANSACTION_SERIALIZABLE;

// 트랜잭션 격리 레벨을 설정한다.
connection.setTransactionIsolation(isolationLevel);
Expand Down Expand Up @@ -173,17 +173,18 @@ void noneRepeatable() throws SQLException {
* Read phenomena | Phantom reads
* Isolation level |
* -----------------|--------------
* Read Uncommitted |
* Read Committed |
* Repeatable Read |
* Serializable |
* Read Uncommitted | +
* Read Committed | +
* Repeatable Read | +
* Serializable | -
*/
@Test
void phantomReading() throws SQLException {

// testcontainer로 docker를 실행해서 mysql에 연결한다.
final var mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0.30"))
.withLogConsumer(new Slf4jLogConsumer(log));
mysql.withUrlParam("allowMultiQueries", "true");
mysql.start();
setUp(createMySQLDataSource(mysql));

Expand All @@ -197,7 +198,7 @@ void phantomReading() throws SQLException {
connection.setAutoCommit(false);

// 적절한 격리 레벨을 찾는다.
final int isolationLevel = Connection.TRANSACTION_NONE;
final int isolationLevel = Connection.TRANSACTION_SERIALIZABLE;

// 트랜잭션 격리 레벨을 설정한다.
connection.setTransactionIsolation(isolationLevel);
Expand Down Expand Up @@ -249,7 +250,7 @@ private static DataSource createMySQLDataSource(final JdbcDatabaseContainer<?> c
private static DataSource createH2DataSource() {
final var jdbcDataSource = new JdbcDataSource();
// h2 로그를 확인하고 싶을 때 사용
// jdbcDataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT=3;MODE=MYSQL");
jdbcDataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT=3;MODE=MYSQL");
jdbcDataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MYSQL;");
jdbcDataSource.setUser("sa");
jdbcDataSource.setPassword("");
Expand Down
10 changes: 5 additions & 5 deletions study/src/test/java/transaction/stage2/FirstUserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public Set<String> saveAndExceptionWithRequiredNew() {
throw new RuntimeException();
}

// @Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.REQUIRED)
public Set<String> saveFirstTransactionWithSupports() {
final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
userRepository.save(User.createTest());
Expand All @@ -77,7 +77,7 @@ public Set<String> saveFirstTransactionWithSupports() {
return of(firstTransactionName, secondTransactionName);
}

// @Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.REQUIRED)
public Set<String> saveFirstTransactionWithMandatory() {
final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
userRepository.save(User.createTest());
Expand All @@ -88,7 +88,7 @@ public Set<String> saveFirstTransactionWithMandatory() {
return of(firstTransactionName, secondTransactionName);
}

@Transactional(propagation = Propagation.REQUIRED)
// @Transactional(propagation = Propagation.REQUIRED)
public Set<String> saveFirstTransactionWithNotSupported() {
final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
userRepository.save(User.createTest());
Expand All @@ -99,7 +99,7 @@ public Set<String> saveFirstTransactionWithNotSupported() {
return of(firstTransactionName, secondTransactionName);
}

@Transactional(propagation = Propagation.REQUIRED)
// @Transactional(propagation = Propagation.REQUIRED)
public Set<String> saveFirstTransactionWithNested() {
final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
userRepository.save(User.createTest());
Expand All @@ -110,7 +110,7 @@ public Set<String> saveFirstTransactionWithNested() {
return of(firstTransactionName, secondTransactionName);
}

@Transactional(propagation = Propagation.REQUIRED)
// @Transactional(propagation = Propagation.REQUIRED)
public Set<String> saveFirstTransactionWithNever() {
final var firstTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
userRepository.save(User.createTest());
Expand Down
Loading

0 comments on commit e7816c9

Please sign in to comment.