Skip to content
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단계] 허브(방대의) 미션 제출합니다. #267

Merged
merged 6 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
# JDBC 라이브러리 구현하기

### 1단계 - JDBC 라이브러리 구현하기

- [x] RowMapper 인터페이스를 추가한다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!!
허브는 기능 명세 작성할 때부터 RowMapper를 사용해야겠다는 걸 알고 계셨나요?!
(저는 처음에 Reflection으로 sql의 파라미터수에 맞는 생성자를 찾아서 일일히 타입 맞춰서 넣어주는 건 줄 알고..;; ㅋ)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 예전에 체스 미션할 때 JdbcTemplate을 간단하게 만들어본 적이 있어서 기억이 났어요!

- [x] JdbcTemplate으로 코드 이동시킨다.
113 changes: 22 additions & 91 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
@@ -1,121 +1,52 @@
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 javax.sql.DataSource;
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 static final RowMapper<User> USER_ROW_MAPPER = (resultSet) -> new User(
resultSet.getLong(1),
resultSet.getString(2),
resultSet.getString(3),
resultSet.getString(4)
);

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 id = ?";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 이렇게도 묶어서 되는거였군요..!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

박스터한테 배웠습니다 ㅋㅋㅋㅋ

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.queryForList(sql, USER_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);

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) {}
}
final String sql = "select id, account, password, email from users where id = ?";
return jdbcTemplate.queryForObject(sql, USER_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, USER_ROW_MAPPER, account);
}
}
66 changes: 64 additions & 2 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
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);
Expand All @@ -14,4 +19,61 @@ public class JdbcTemplate {
public JdbcTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

public void update(final String sql, final Object... parameters) {
try (final Connection connection = dataSource.getConnection();
final PreparedStatement preparedStatement = getPreparedStatement(sql, connection, parameters)) {
log.debug("query : {}", sql);
for (int i = 0; i < parameters.length; i++) {
preparedStatement.setObject(i + 1, parameters[i]);
}
preparedStatement.executeUpdate();
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}

public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... parameters) {
try (final Connection connection = dataSource.getConnection();
final PreparedStatement preparedStatement = getPreparedStatement(sql, connection, parameters);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그저 감탄입니다... 허브센세...ㅠㅠ

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try with resources 정말 깔끔합니다 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

준팍센세.. 반갑습니다 ✋

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와우,,,, 퍼가요@@@@

final ResultSet resultSet = preparedStatement.executeQuery()) {
log.debug("query : {}", sql);
if (resultSet.next()) {
return rowMapper.mapRow(resultSet);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 쿼리 실행 결과가 하나가 아니면 어떻게 되는걸까여?!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하나면 조회 결과 중 제일 처음 부분만 반환될 것 같습니다!
이 부분도 조회 결과가 2개 이상이라면 예외를 던지도록 수정해볼게요 👍

}
return null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

호옥시 null을 반환하는 허브의 기준이 있을까유?!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 UserDaoTest를 수정하지 않고 동작하게 하려고 null을 반환시켰습니다!
null을 반환하기 보다 사용하는 쪽에서 조금 더 명확하게 처리할 수 있도록, 이 부분은 2단계 제출하면서 Optional을 반환하는 방향으로 수정해볼게요!!

} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}

private PreparedStatement getPreparedStatement(
final String sql,
final Connection connection,
final Object[] parameters
) throws SQLException {
final PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
preparedStatement.setObject(i + 1, parameters[i]);
}
return preparedStatement;
}

public <T> List<T> queryForList(final String sql, final RowMapper<T> rowMapper, final Object... parameters) {
try (final Connection connection = dataSource.getConnection();
final PreparedStatement preparedStatement = getPreparedStatement(sql, connection, parameters);
final ResultSet resultSet = preparedStatement.executeQuery()) {
log.debug("query : {}", sql);
final List<T> result = new ArrayList<>();
while (resultSet.next()) {
result.add(rowMapper.mapRow(resultSet));
}
return result;
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
}
10 changes: 10 additions & 0 deletions jdbc/src/main/java/org/springframework/jdbc/core/RowMapper.java
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

public interface RowMapper<T> {

T mapRow(ResultSet resultSet) throws SQLException;
}
Loading