-
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단계] 도치(김동흠) 미션 제출합니다. #324
Changes from all commits
06379e5
ccd9c82
c062a39
87c2e97
697727b
5235221
b269c25
2d5f8e5
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,65 @@ | ||
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; | ||
import java.util.Optional; | ||
|
||
|
||
public class UserDao { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(UserDao.class); | ||
|
||
private final DataSource dataSource; | ||
private final JdbcTemplate jdbcTemplate; | ||
|
||
public UserDao(final DataSource dataSource) { | ||
this.dataSource = dataSource; | ||
this.jdbcTemplate = 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) {} | ||
} | ||
final String sql = "insert into users (account, password, email) values (?, ?, ?)"; | ||
|
||
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 users.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, 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) {} | ||
} | ||
private final RowMapper<User> rowMapper = rs -> ( | ||
new User( | ||
rs.getLong("id"), | ||
rs.getString("account"), | ||
rs.getString("password"), | ||
rs.getString("email") | ||
) | ||
); | ||
|
||
public Optional<User> findById(final Long id) { | ||
final String sql = "select id, account, password, email from users where id = ?"; | ||
|
||
return jdbcTemplate.queryForObject(sql, rowMapper, id); | ||
} | ||
|
||
public User findByAccount(final String account) { | ||
// todo | ||
return null; | ||
public Optional<User> findByAccount(final String account) { | ||
final String sql = "select id, account, password, email from users where users.account = ?"; | ||
|
||
return jdbcTemplate.queryForObject(sql, rowMapper, account); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,15 +30,15 @@ void findAll() { | |
|
||
@Test | ||
void findById() { | ||
final var user = userDao.findById(1L); | ||
final var user = userDao.findById(1L).get(); | ||
|
||
assertThat(user.getAccount()).isEqualTo("gugu"); | ||
} | ||
|
||
@Test | ||
void findByAccount() { | ||
final var account = "gugu"; | ||
final var user = userDao.findByAccount(account); | ||
final var user = userDao.findByAccount(account).get(); | ||
|
||
assertThat(user.getAccount()).isEqualTo(account); | ||
} | ||
|
@@ -49,20 +49,21 @@ void insert() { | |
final var user = new User(account, "password", "[email protected]"); | ||
userDao.insert(user); | ||
|
||
final var actual = userDao.findById(2L); | ||
final var actual = userDao.findById(2L).get(); | ||
|
||
assertThat(actual.getAccount()).isEqualTo(account); | ||
assertThat(actual.getEmail()).isEqualTo("[email protected]"); | ||
} | ||
|
||
@Test | ||
void update() { | ||
final var newPassword = "password99"; | ||
final var user = userDao.findById(1L); | ||
final var user = userDao.findById(1L).get(); | ||
user.changePassword(newPassword); | ||
|
||
userDao.update(user); | ||
|
||
final var actual = userDao.findById(1L); | ||
final var actual = userDao.findById(1L).get(); | ||
|
||
assertThat(actual.getPassword()).isEqualTo(newPassword); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.springframework.jdbc; | ||
|
||
import java.sql.SQLException; | ||
|
||
public class BadGrammarJdbcException extends JdbcException { | ||
public BadGrammarJdbcException(String msg) { | ||
super(msg); | ||
} | ||
|
||
public BadGrammarJdbcException(String msg, SQLException ex) { | ||
super(msg, ex); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.springframework.jdbc; | ||
|
||
public class JdbcException extends RuntimeException { | ||
public JdbcException(String message) { | ||
super(message); | ||
} | ||
|
||
public JdbcException(String message, Throwable cause) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,16 +2,114 @@ | |
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.jdbc.BadGrammarJdbcException; | ||
import org.springframework.jdbc.CannotGetJdbcConnectionException; | ||
import org.springframework.jdbc.JdbcException; | ||
|
||
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; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
|
||
public class JdbcTemplate { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(JdbcTemplate.class); | ||
|
||
private final DataSource dataSource; | ||
private final SqlExceptionTranslator sqlExceptionTranslator = new SqlExceptionTranslator(); | ||
|
||
public JdbcTemplate(final DataSource dataSource) { | ||
this.dataSource = dataSource; | ||
} | ||
|
||
public int update(String sql, Object... arguments) { | ||
try ( | ||
Connection conn = dataSource.getConnection(); | ||
PreparedStatement pstmt = StatementCreator.createStatement(conn, sql, arguments) | ||
) { | ||
log.debug("query : {}", sql); | ||
|
||
return pstmt.executeUpdate(); | ||
} catch (SQLException e) { | ||
log.error(e.getMessage(), e); | ||
throw sqlExceptionTranslator.translateException(e); | ||
} | ||
} | ||
|
||
public <T> Optional<T> queryForObject(String sql, RowMapper<T> rowMapper, Object... arguments) { | ||
try ( | ||
Connection conn = dataSource.getConnection(); | ||
PreparedStatement pstmt = StatementCreator.createStatement(conn, sql, arguments); | ||
ResultSet rs = pstmt.executeQuery() | ||
) { | ||
log.debug("query : {}", sql); | ||
|
||
if (rs.next()) { | ||
return Optional.of(rowMapper.mapRow(rs)); | ||
} | ||
|
||
return Optional.empty(); | ||
} catch (SQLException e) { | ||
log.error(e.getMessage(), e); | ||
throw sqlExceptionTranslator.translateException(e); | ||
} | ||
} | ||
|
||
|
||
public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... arguments) { | ||
try ( | ||
Connection conn = dataSource.getConnection(); | ||
PreparedStatement pstmt = StatementCreator.createStatement(conn, sql, arguments); | ||
ResultSet rs = pstmt.executeQuery() | ||
) { | ||
log.debug("query : {}", sql); | ||
|
||
List<T> result = new ArrayList<>(); | ||
while (rs.next()) { | ||
result.add(rowMapper.mapRow(rs)); | ||
} | ||
|
||
return result; | ||
} catch (SQLException e) { | ||
log.error(e.getMessage(), e); | ||
throw sqlExceptionTranslator.translateException(e); | ||
} | ||
} | ||
|
||
private class SqlExceptionTranslator { | ||
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. 👍 |
||
private final Set<String> BAD_SQL_GRAMMAR_CODES = Set.of( | ||
"07", // Dynamic SQL error | ||
"21", // Cardinality violation | ||
"2A", // Syntax error direct SQL | ||
"37", // Syntax error dynamic SQL | ||
"42", // General SQL syntax error | ||
"65" // Oracle: unknown identifier | ||
); | ||
|
||
private final Set<String> DATA_ACCESS_RESOURCE_FAILURE_CODES = Set.of( | ||
"08", // Connection exception | ||
"53", // PostgreSQL: insufficient resources (e.g. disk full) | ||
"54", // PostgreSQL: program limit exceeded (e.g. statement too complex) | ||
"57", // DB2: out-of-memory exception / database not started | ||
"58" // DB2: unexpected system error | ||
); | ||
|
||
private JdbcException translateException(SQLException e) { | ||
String sqlState = e.getSQLState(); | ||
String classCode = sqlState.substring(0, 2); | ||
if (DATA_ACCESS_RESOURCE_FAILURE_CODES.contains(classCode)) { | ||
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. 사소하지만.. 저한테는 classCode.contains(DATA_ACCESS_RESOURCE_FAILURE_CODES) 같은 순서로 쓰는 게 더 잘 이해되는 것 같아서 커멘트 남겨봅니다! 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. 이 부분 해보려했는데 charSequence로 바꾸기 위한 조작이 더 생길 것 같아서 현재대로 유지하겠습니다! |
||
throw new CannotGetJdbcConnectionException(e.getMessage(), e); | ||
} | ||
if (BAD_SQL_GRAMMAR_CODES.contains(classCode)) { | ||
throw new BadGrammarJdbcException(e.getMessage(), e); | ||
} | ||
|
||
throw new JdbcException(e.getMessage(), e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.springframework.jdbc.core; | ||
|
||
import java.sql.ResultSet; | ||
import java.sql.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. 추상 메서드가 한 개이므로 |
||
@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.
:따봉