-
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 라이브러리 구현하기 - 3단계] 도이(유도영) 미션 제출합니다. #463
Changes from all commits
100729b
3f8aa58
e6fe8a2
c37ab31
fcc4d7a
4c33d88
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 |
---|---|---|
|
@@ -4,11 +4,12 @@ | |
|
||
import com.techcourse.config.DataSourceConfig; | ||
import com.techcourse.domain.User; | ||
import com.techcourse.support.jdbc.init.PooledDataSourceConnectionManager; | ||
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils; | ||
import com.techcourse.support.jdbc.init.PooledDataSourceConnectionManager; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.TransactionManager; | ||
|
||
class UserDaoTest { | ||
|
||
|
@@ -17,8 +18,7 @@ class UserDaoTest { | |
@BeforeEach | ||
void setup() { | ||
DatabasePopulatorUtils.execute(DataSourceConfig.getInstance()); | ||
|
||
userDao = new UserDao(new JdbcTemplate(new PooledDataSourceConnectionManager())); | ||
userDao = new UserDao(new TransactionManager(new PooledDataSourceConnectionManager()), new JdbcTemplate()); | ||
final var user = new User("gugu", "password", "[email protected]"); | ||
userDao.insert(user); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,24 +7,25 @@ | |
import com.techcourse.dao.UserDao; | ||
import com.techcourse.dao.UserHistoryDao; | ||
import com.techcourse.domain.User; | ||
import com.techcourse.support.jdbc.init.PooledDataSourceConnectionManager; | ||
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils; | ||
import com.techcourse.support.jdbc.init.PooledDataSourceConnectionManager; | ||
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 org.springframework.jdbc.core.TransactionManager; | ||
|
||
@Disabled | ||
class UserServiceTest { | ||
|
||
private TransactionManager transactionManager; | ||
private JdbcTemplate jdbcTemplate; | ||
private UserDao userDao; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
this.jdbcTemplate = new JdbcTemplate(new PooledDataSourceConnectionManager()); | ||
this.userDao = new UserDao(jdbcTemplate); | ||
this.transactionManager = new TransactionManager(new PooledDataSourceConnectionManager()); | ||
this.jdbcTemplate = new JdbcTemplate(); | ||
this.userDao = new UserDao(transactionManager, jdbcTemplate); | ||
|
||
DatabasePopulatorUtils.execute(DataSourceConfig.getInstance()); | ||
final var user = new User("gugu", "password", "[email protected]"); | ||
|
@@ -33,8 +34,8 @@ void setUp() { | |
|
||
@Test | ||
void testChangePassword() { | ||
final var userHistoryDao = new UserHistoryDao(jdbcTemplate); | ||
final var userService = new UserService(userDao, userHistoryDao); | ||
final var userHistoryDao = new UserHistoryDao(transactionManager, jdbcTemplate); | ||
final var userService = new UserService(transactionManager, userDao, userHistoryDao); | ||
|
||
final var newPassword = "qqqqq"; | ||
final var createBy = "gugu"; | ||
|
@@ -48,8 +49,8 @@ void testChangePassword() { | |
@Test | ||
void testTransactionRollback() { | ||
// 트랜잭션 롤백 테스트를 위해 mock으로 교체 | ||
final var userHistoryDao = new MockUserHistoryDao(jdbcTemplate); | ||
final var userService = new UserService(userDao, userHistoryDao); | ||
final var userHistoryDao = new MockUserHistoryDao(transactionManager, jdbcTemplate); | ||
final var userService = new UserService(transactionManager, userDao, userHistoryDao); | ||
|
||
final var newPassword = "newPassword"; | ||
final var createBy = "gugu"; | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,32 +14,29 @@ public class JdbcTemplate { | |
|
||
private static final Logger log = LoggerFactory.getLogger(JdbcTemplate.class); | ||
|
||
private final ConnectionManager connectionManager; | ||
|
||
public JdbcTemplate(final ConnectionManager connectionManager) { | ||
this.connectionManager = connectionManager; | ||
} | ||
|
||
public int executeUpdate(final String query, final Object... parameters) { | ||
return execute(query, (connection, preparedStatement) -> preparedStatement.executeUpdate(), parameters); | ||
public int executeUpdate(final Connection connection, final String query, final Object... parameters) { | ||
PreparedStatementCallback<Integer> preparedStatementCallback = PreparedStatement::executeUpdate; | ||
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. 뭔기 이번 단계에서 final var를 주로 사용하신 것 같은데, 이 부분에서는 일부로 타입을 명시해주신건가요? 왠지 함수형 인터페이스라 잘 읽히지 않을 것 같아서 명시적으로 타입을 적으신거 같아서요! 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. var를 쓰려고 하면 그런데 final은 실수로 빼먹었고 / 위 부분은 애초에 변수로 뺄 필요가 없어 보이기도 하네요 ㅎㅎㅎ |
||
return execute(connection, query, preparedStatementCallback, parameters); | ||
} | ||
|
||
public <T> T executeQueryForObject( | ||
final Connection connection, | ||
final String query, | ||
final RowMapper<T> rowMapper, | ||
final Object... parameters | ||
) { | ||
final ResultSetExtractor<T> resultSetExtractor = resultSet -> { | ||
if (resultSet.next()) { | ||
return rowMapper.mapRow(resultSet); | ||
} | ||
final var results = executeQueryForList(connection, query, rowMapper, parameters); | ||
if (results.isEmpty()) { | ||
return null; | ||
}; | ||
|
||
return executeQuery(query, resultSetExtractor, parameters); | ||
} | ||
if (results.size() > 1) { | ||
throw new SqlQueryException(query, "cannot map for single result"); | ||
} | ||
return results.get(0); | ||
} | ||
|
||
public <T> List<T> executeQueryForList( | ||
final Connection connection, | ||
final String query, | ||
final RowMapper<T> rowMapper, | ||
final Object... parameters | ||
|
@@ -52,31 +49,37 @@ public <T> List<T> executeQueryForList( | |
return results; | ||
}; | ||
|
||
return executeQuery(query, resultSetExtractor, parameters); | ||
return executeQuery(connection, query, resultSetExtractor, parameters); | ||
} | ||
|
||
public <T> T executeQuery( | ||
final Connection connection, | ||
final String query, | ||
final ResultSetExtractor<T> resultSetExtractor, | ||
final Object... parameters | ||
) { | ||
return execute(query, (connection, preparedStatement) -> { | ||
try (final ResultSet resultSet = preparedStatement.executeQuery()) { | ||
return resultSetExtractor.extract(resultSet); | ||
} | ||
}, parameters); | ||
return execute( | ||
connection, | ||
query, | ||
preparedStatement -> { | ||
try (final ResultSet resultSet = preparedStatement.executeQuery()) { | ||
return resultSetExtractor.extract(resultSet); | ||
} | ||
}, parameters); | ||
} | ||
|
||
private <T> T execute( | ||
final Connection connection, | ||
final String query, | ||
final ConnectionCallback<T> callback, | ||
final PreparedStatementCallback<T> callback, | ||
final Object... parameters | ||
) { | ||
try (final Connection connection = connectionManager.getConnection(); | ||
final PreparedStatement preparedStatement = connection.prepareStatement(query)) { | ||
try (final Connection conn = connection; | ||
final PreparedStatement preparedStatement = conn.prepareStatement(query)) { | ||
log.info("query: {}", query); | ||
setParameters(preparedStatement, parameters); | ||
return callback.doInConnection(connection, preparedStatement); | ||
|
||
return callback.doInConnection(preparedStatement); | ||
} catch (SQLException exception) { | ||
throw new SqlQueryException(exception.getMessage(), query); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.springframework.jdbc.core; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.sql.SQLException; | ||
|
||
@FunctionalInterface | ||
public interface PreparedStatementCallback<T> { | ||
|
||
T doInConnection(final PreparedStatement preparedStatement) throws SQLException; | ||
|
||
} |
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.
트랜젝션 메니저를 통해 Dao 메서드에 트랜젝션을 구현하셨군요! 기존 API에 큰 변경 없이 잘 설계 해주셨네요! 감동입니다 ㅜㅜ 저는 이부분 고민 많이했거든요.. 도이처럼 JDBCTemplate API 명세를 보니 Callback 함수형 인터페이스를 사용해서 저도 적극 활용을 해봤는데 Transaction이 들어가고 특히나 두 메서드를 동시에 트랜젝션 거는 작업이 어려웠던 것 같습니다.. 제 말이 길었네요! 트랜젝션 메서드로 일관되게 트랜젝션 걸어주셔서 잘하신것 같습니다!