-
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단계] 마코(이규성) 미션 제출합니다. #310
Changes from all 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,121 +1,58 @@ | ||
package com.techcourse.dao; | ||
|
||
import com.techcourse.domain.User; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.List; | ||
|
||
public class UserDao { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(UserDao.class); | ||
|
||
private final DataSource dataSource; | ||
private final JdbcTemplate jdbcTemplate; | ||
|
||
private RowMapper<User> rowMapper = rs -> { | ||
Long id = rs.getLong("id"); | ||
String account = rs.getString("account"); | ||
String password = rs.getString("password"); | ||
String email = rs.getString("email"); | ||
return new User(id, account, password, email); | ||
}; | ||
|
||
public UserDao(final DataSource dataSource) { | ||
this.dataSource = dataSource; | ||
this(new JdbcTemplate(dataSource)); | ||
} | ||
|
||
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) {} | ||
|
||
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 var 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 var sql = "select id, account, password, email from users"; | ||
return jdbcTemplate.query(sql, rowMapper); | ||
} | ||
|
||
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); | ||
|
||
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, rowMapper, id); | ||
} | ||
|
||
public User findByAccount(final String account) { | ||
// todo | ||
return null; | ||
final var sql = "select id, account, password, email from users where account = ?"; | ||
return jdbcTemplate.queryForObject(sql, rowMapper, account); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package org.springframework.dao; | ||
|
||
public class EmptyResultAccessException extends RuntimeException { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.springframework.jdbc.core; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.sql.SQLException; | ||
|
||
public class ArgumentPreparedStatementSetter implements PreparedStatementSetter{ | ||
|
||
private final Object[] args; | ||
|
||
public ArgumentPreparedStatementSetter(Object[] args) { | ||
this.args = args; | ||
} | ||
|
||
@Override | ||
public void setValues(PreparedStatement pstmt) throws SQLException { | ||
for (int i = 0; i < args.length; i++) { | ||
pstmt.setObject(i + 1, args[i]); | ||
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. setObject 👍 |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,16 @@ | |
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.dao.DataAccessException; | ||
import org.springframework.dao.EmptyResultAccessException; | ||
|
||
import javax.sql.DataSource; | ||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class JdbcTemplate { | ||
|
||
|
@@ -14,4 +22,44 @@ public class JdbcTemplate { | |
public JdbcTemplate(final DataSource dataSource) { | ||
this.dataSource = dataSource; | ||
} | ||
|
||
public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... args) { | ||
List<T> results = query(sql, rowMapper, args); | ||
if (results.isEmpty()) { | ||
throw new EmptyResultAccessException(); | ||
} | ||
return results.get(0); | ||
} | ||
|
||
public <T> List<T> query(final String sql, final RowMapper<T> rowMapper, final Object... args) { | ||
try (Connection conn = dataSource.getConnection(); | ||
PreparedStatement pstmt = conn.prepareStatement(sql)) { | ||
Comment on lines
+35
to
+36
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. try-with-resources 👍 |
||
log.debug("query : {}", sql); | ||
|
||
PreparedStatementSetter pstSetter = new ArgumentPreparedStatementSetter(args); | ||
pstSetter.setValues(pstmt); | ||
ResultSet resultSet = pstmt.executeQuery(); | ||
|
||
List<T> results = new ArrayList<>(); | ||
while (resultSet.next()) { | ||
results.add(rowMapper.mapRow(resultSet)); | ||
} | ||
return results; | ||
} catch (SQLException e) { | ||
throw new DataAccessException(e); | ||
} | ||
} | ||
|
||
public void update(final String sql, final Object... args) { | ||
try (Connection conn = dataSource.getConnection(); | ||
PreparedStatement pstmt = conn.prepareStatement(sql)) { | ||
log.debug("query : {}", sql); | ||
|
||
PreparedStatementSetter pstSetter = new ArgumentPreparedStatementSetter(args); | ||
pstSetter.setValues(pstmt); | ||
pstmt.executeUpdate(); | ||
} catch (SQLException e) { | ||
throw new DataAccessException(e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.springframework.jdbc.core; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.sql.SQLException; | ||
|
||
@FunctionalInterface | ||
public interface PreparedStatementSetter { | ||
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. 확장성을 위한 추상화 👍 |
||
|
||
void setValues(PreparedStatement preparedStatement) throws SQLException; | ||
} |
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(ResultSet rs) 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.
책임 분리 좋네요!!👍
PreparedStatement 의 코드에 설명에서 Parameter 라는 단어로 해당 값들을 표현하더라구요 Parameter 를 이용한 네이밍을 사용해보면 더 좋을 것 같아요~😁
사소하지만 제가 이해한 PreparedStatement의 Argument를 setting을 하는 책임을 가진 객체가 맞다면 PreparedStatmentArgumentSetter 라는 네이밍이 저는 좀 더 와닿는데 클래스명만으로 PreparedStatementSetter 인터페이스를 구현한 클래스임을 충분히 알수있는 현재 네이밍에도 장점이 있어서 마코의 생각이 궁금합니다!🫡
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.
저는 argument 기반의 PreparedStatementSetter라고 생각을 해서 ArgumentPreparedStatementSetter라고 지었습니다.
확실히 스플릿이 말씀해주신 PreparedStatementArgumentSetter가 의미 전달이 잘 되는 것 같습니다👍
하지만 나중에 PreparedStatementSetter 뿐만 아니라 PreparedStatementCreator와 같이 비슷한 이름의 여러 인터페이스가 생기는 경우 헷갈릴 것 같아 가능하면 인터페이스의 이름을 사용하는 것이 좋다고 생각합니다~