-
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 라이브러리 구현하기 - 1단계] 바론(이소민) 미션 제출합니다. #305
Changes from 4 commits
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 |
---|---|---|
@@ -1 +1,6 @@ | ||
# JDBC 라이브러리 구현하기 | ||
|
||
### 1단계 | ||
- [x] JdbcTemplate insert, update, delete, select 쿼리 메서드 구현 | ||
- [x] UserDao rowmapper 구현 | ||
- [x] UserDao todo 쿼리 로직 구현 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,121 +1,53 @@ | ||
package com.techcourse.dao; | ||
|
||
import com.techcourse.domain.User; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.List; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
|
||
public class UserDao { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(UserDao.class); | ||
|
||
private final DataSource dataSource; | ||
private static final RowMapper<User> ROW_MAPPER = (rs, count) -> | ||
new User( | ||
rs.getLong("id"), | ||
rs.getString("account"), | ||
rs.getString("password"), | ||
rs.getString("email") | ||
); | ||
|
||
public UserDao(final DataSource dataSource) { | ||
this.dataSource = dataSource; | ||
} | ||
private final JdbcTemplate jdbcTemplate; | ||
|
||
public UserDao(final JdbcTemplate jdbcTemplate) { | ||
this.dataSource = null; | ||
this.jdbcTemplate = jdbcTemplate; | ||
} | ||
|
||
public void insert(final User user) { | ||
final var sql = "insert into users (account, password, email) values (?, ?, ?)"; | ||
|
||
Connection conn = null; | ||
PreparedStatement pstmt = null; | ||
try { | ||
conn = dataSource.getConnection(); | ||
pstmt = conn.prepareStatement(sql); | ||
|
||
log.debug("query : {}", sql); | ||
|
||
pstmt.setString(1, user.getAccount()); | ||
pstmt.setString(2, user.getPassword()); | ||
pstmt.setString(3, user.getEmail()); | ||
pstmt.executeUpdate(); | ||
} catch (SQLException e) { | ||
log.error(e.getMessage(), e); | ||
throw new RuntimeException(e); | ||
} finally { | ||
try { | ||
if (pstmt != null) { | ||
pstmt.close(); | ||
} | ||
} catch (SQLException ignored) {} | ||
final String sql = "insert into users (account, password, email) values (?, ?, ?)"; | ||
|
||
try { | ||
if (conn != null) { | ||
conn.close(); | ||
} | ||
} catch (SQLException ignored) {} | ||
} | ||
jdbcTemplate.update(sql, user.getAccount(), user.getPassword(), user.getEmail()); | ||
} | ||
|
||
public void update(final User user) { | ||
// todo | ||
final String sql = "update users set account = ?, password = ?, email = ? where id = ?"; | ||
|
||
jdbcTemplate.update(sql, user.getAccount(), user.getPassword(), user.getEmail(), user.getId()); | ||
} | ||
|
||
public List<User> findAll() { | ||
// todo | ||
return null; | ||
final String sql = "select id, account, email, password from users"; | ||
|
||
return jdbcTemplate.query(sql, ROW_MAPPER); | ||
} | ||
|
||
public User findById(final Long id) { | ||
final var sql = "select id, account, password, email from users where id = ?"; | ||
|
||
Connection conn = null; | ||
PreparedStatement pstmt = null; | ||
ResultSet rs = null; | ||
try { | ||
conn = dataSource.getConnection(); | ||
pstmt = conn.prepareStatement(sql); | ||
pstmt.setLong(1, id); | ||
rs = pstmt.executeQuery(); | ||
|
||
log.debug("query : {}", sql); | ||
final String sql = "select id, account, password, email from users where id = ?"; | ||
|
||
if (rs.next()) { | ||
return new User( | ||
rs.getLong(1), | ||
rs.getString(2), | ||
rs.getString(3), | ||
rs.getString(4)); | ||
} | ||
return null; | ||
} catch (SQLException e) { | ||
log.error(e.getMessage(), e); | ||
throw new RuntimeException(e); | ||
} finally { | ||
try { | ||
if (rs != null) { | ||
rs.close(); | ||
} | ||
} catch (SQLException ignored) {} | ||
|
||
try { | ||
if (pstmt != null) { | ||
pstmt.close(); | ||
} | ||
} catch (SQLException ignored) {} | ||
|
||
try { | ||
if (conn != null) { | ||
conn.close(); | ||
} | ||
} catch (SQLException ignored) {} | ||
} | ||
return jdbcTemplate.queryForObject(sql, ROW_MAPPER, id); | ||
} | ||
|
||
public User findByAccount(final String account) { | ||
// todo | ||
return null; | ||
final String sql = "select id, account, password, email from users where account = ?"; | ||
|
||
return jdbcTemplate.queryForObject(sql, ROW_MAPPER, account); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
|
@@ -16,7 +17,7 @@ class UserDaoTest { | |
void setup() { | ||
DatabasePopulatorUtils.execute(DataSourceConfig.getInstance()); | ||
|
||
userDao = new UserDao(DataSourceConfig.getInstance()); | ||
userDao = new UserDao(new JdbcTemplate(DataSourceConfig.getInstance())); | ||
final var user = new User("gugu", "password", "[email protected]"); | ||
userDao.insert(user); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,15 @@ | ||
package org.springframework.jdbc.core; | ||
|
||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import javax.sql.DataSource; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.sql.DataSource; | ||
|
||
public class JdbcTemplate { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(JdbcTemplate.class); | ||
|
@@ -14,4 +19,61 @@ public class JdbcTemplate { | |
public JdbcTemplate(final DataSource dataSource) { | ||
this.dataSource = dataSource; | ||
} | ||
|
||
public <T> List<T> query(final String sql, final RowMapper<T> rowMapper, final Object... args) { | ||
try (final Connection connection = dataSource.getConnection(); | ||
final PreparedStatement pstmt = connection.prepareStatement(sql)) { | ||
log.debug("query : {}", sql); | ||
|
||
setParamsToPreparedStatement(pstmt, args); | ||
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 ResultSet resultSet = pstmt.executeQuery(); | ||
final List<T> results = new ArrayList<>(); | ||
if (resultSet.next()) { | ||
results.add(rowMapper.mapRow(resultSet, resultSet.getRow())); | ||
} | ||
|
||
return results; | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private void setParamsToPreparedStatement(final PreparedStatement pstmt, final Object[] args) | ||
throws SQLException | ||
{ | ||
for (int i = 0; i < args.length; i++) { | ||
pstmt.setObject(i + 1, args[i]); | ||
} | ||
} | ||
|
||
public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... args) { | ||
try (final Connection connection = dataSource.getConnection(); | ||
final PreparedStatement pstmt = connection.prepareStatement(sql)) { | ||
log.debug("query : {}", sql); | ||
|
||
setParamsToPreparedStatement(pstmt, args); | ||
|
||
final ResultSet resultSet = pstmt.executeQuery(); | ||
if (resultSet.next()) { | ||
return rowMapper.mapRow(resultSet, resultSet.getRow()); | ||
} | ||
return null; | ||
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. 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. 오.. 사실 2단계 리팩터링 때 저도 Optional로 리팩터링을 할 계획이었는데요 ㅎㅎ 이레 말씀을 들어보니
에는 예외를 발생시키는 것이 맞을 것 같아요! 위 상황을 모두 고려하면 이 때는 이레는 어떻게 생각하시나요? 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. 코드에서는 |
||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
Comment on lines
+62
to
+64
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. resultSet을 닫아주지 않은 것 같아요! 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. 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. 안녕하세요 이레 ㅎㅎ 이레의 말씀을 듣고
의 순서로 로직이 흘러가는 것 같더라구요! PreparedStatement에서 가지고 있던 커넥션을 먼저 해제한 후, 커넥션 자원을 해제하는 순서로 흘러가는 것 같아요. |
||
} | ||
|
||
public int update(final String sql, final Object... args) { | ||
try (final Connection connection = dataSource.getConnection(); | ||
final PreparedStatement pstmt = connection.prepareStatement(sql)) { | ||
log.debug("query : {}", sql); | ||
|
||
setParamsToPreparedStatement(pstmt, args); | ||
|
||
return pstmt.executeUpdate(); | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.springframework.jdbc.core; | ||
|
||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
|
||
@FunctionalInterface | ||
public interface RowMapper<T> { | ||
|
||
T mapRow(final ResultSet resultSet, final int count) throws SQLException; | ||
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. mapRow에 count를 사용하신 이유가 있나요? 실제로 Dao에서 count를 유의미하게 사용하는 것 같지 않아서요. 어디에 사용되는 것인지 궁금합니다! 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. 실제로는 사용하지 않는 부분인 것 같아요!! rownum은 rownum 수 만큼 매핑 과정을 반복한다 라는 용도로 사용되는 것으로 알고 있는데, 실제로 제가 구현한 JdbcTemplate을 보면 rowNum을 전혀 사용하지 않고 while문을 돌면서 resultSet을 매핑하고 있네용 이 부분은 rowNum을 제거한 상태로 테스트 해보고 다시 Dm 또는 답변으로 전달 드리겠습니다! 🙇 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. 코드 내부적으로도 while문으로 돌고 rownum은 몇개의 행을 update했는지 count를 세기 위한 것으로 알고 있습니다. |
||
} |
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.
Test는 최대한 건드리지 말라고 했었으니
생성자를 아래와 같이 해도 될것 같아요.
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.
헉 이런 요구사항이 있었군요!!!
감사합니다 안그래도 생성자를 어떻게 구성할지 고민하고 있었는데 그렇다면 이레가 제안해주신 방법이 더 좋을 것 같아요 👍 😗
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.
바론 이 요구사항은 안지켜도 될것 같아요...