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단계] 제나(위예나) 미션 제출합니다 #317

Merged
merged 11 commits into from
Sep 30, 2023
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
# JDBC 라이브러리 구현하기

## 1단계
- [x] UserDaoTest 성공시키기
- [x] update()
- [x] findAll()
- [x] findByAccount()
- [x] 중복 코드는 JdbcTemplate 에 추출하기
111 changes: 24 additions & 87 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
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;
Expand All @@ -16,106 +14,45 @@ public class UserDao {

private static final Logger log = LoggerFactory.getLogger(UserDao.class);

private final DataSource dataSource;

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);
public static RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs) throws SQLException {
long id = rs.getLong(1);
String account = rs.getString(2);
String password = rs.getString(3);
String email = rs.getString(4);
return new User(id, account, password, email);
}
};
Comment on lines +23 to +32
Copy link
Member

Choose a reason for hiding this comment

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

람다 표현식을 사용해서 코드를 조금 더 간소화 할 수 있을 것 같아요 !!
UserDao 내부에서만 사용된다면 private으로 관리하는건 어떨까요 !?

Copy link
Author

Choose a reason for hiding this comment

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

접근제어자 신경을 안썼엇네요!! 감사합니당
람다로도 바꿔봤어요 ㅎㅎ


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) {}
}
public void insert(final User user) {
jdbcTemplate.execute("insert into users (account, password, email) values (?, ?, ?)",
user.getAccount(), user.getPassword(), user.getEmail());
}

public void update(final User user) {
// todo
jdbcTemplate.execute("update users set account = ?, password = ?, email = ? where id = ?",
user.getAccount(), user.getPassword(), user.getEmail(), user.getId());
}

public List<User> findAll() {
// todo
return null;
return jdbcTemplate.query("select id, account, password, email from users", 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("select id, account, password, email from users where id = ?",
rowMapper, id);
}

public User findByAccount(final String account) {
// todo
return null;
return jdbcTemplate.queryForObject("select id, account, password, email from users where account = ?"
, rowMapper, account);
}
}
5 changes: 4 additions & 1 deletion app/src/test/java/com/techcourse/dao/UserDaoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
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;

class UserDaoTest {

private UserDao userDao;
private JdbcTemplate jdbcTemplate;

@BeforeEach
void setup() {
DatabasePopulatorUtils.execute(DataSourceConfig.getInstance());
jdbcTemplate = new JdbcTemplate(DataSourceConfig.getInstance());

userDao = new UserDao(DataSourceConfig.getInstance());
userDao = new UserDao(jdbcTemplate);
final var user = new User("gugu", "password", "[email protected]");
userDao.insert(user);
}
Expand Down
197 changes: 197 additions & 0 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
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.ArrayList;
import java.util.List;

public class JdbcTemplate {

Expand All @@ -14,4 +20,195 @@ public class JdbcTemplate {
public JdbcTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}

public void execute(String sql) {
context(new PreparedStrategy() {
@Override
public PreparedStatement createStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
});
Comment on lines +29 to +34
Copy link
Member

Choose a reason for hiding this comment

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

이 부분도 인터페이스에 메소드가 하나밖에 없어서 람다식을 사용할 수 있을 것 같아요 ˙ᵕ˙

}

public void execute(String sql, Object... args) {
context(new PreparedStrategy() {
@Override
public PreparedStatement createStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
}, args);
}

public <T> List<T> query(String sql, RowMapper<T> rm) {
return context(new PreparedStrategy() {
@Override
public PreparedStatement createStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
}, rm);
}

public <T> T queryForObject(String sql, RowMapper<T> rm, Object... args) {
List<T> list = context(new PreparedStrategy() {
@Override
public PreparedStatement createStatement(Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
}, rm, args);

if (list.isEmpty()) {
return null;
}
return list.get(0);
Comment on lines +63 to +66
Copy link
Member

Choose a reason for hiding this comment

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

list에 값이 여러개 들어있는 경우에도 첫 번째 값만 반환하도록 의도한게 맞을까요 ?!

queryForObject 메서드를 사용할 때는 하나의 값만 있다고 생각하고 사용할 것 같은데, 해당 결과로 list에 값이 여러개가 들어가게 된다면 어떤 결과를 주는게 좋을까요 ? 🧐

Copy link
Author

Choose a reason for hiding this comment

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

queryForObject 는 하나의 Object 만 반환하기를 예상하고 사용하는 메서드라서 두개 이상의 값이 list 에 들어가면 예외를 반환하게끔 수정해봤어요!

}

public void context(PreparedStrategy preparedStrategy) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = dataSource.getConnection();
pstmt = preparedStrategy.createStatement(conn);
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) {
}
}
Comment on lines +70 to +93
Copy link
Member

Choose a reason for hiding this comment

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

try-with-resource를 사용해보는건 어떨까요 ?!

Copy link
Author

Choose a reason for hiding this comment

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

적용하니까 코드가 너무 깔끔해졌어요 👍

}

public void context(PreparedStrategy preparedStrategy, Object[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = dataSource.getConnection();
pstmt = preparedStrategy.createStatement(conn);

for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}

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) {
}
}
}

public <T> List<T> context(PreparedStrategy preparedStrategy, RowMapper<T> rm) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
pstmt = preparedStrategy.createStatement(conn);
rs = pstmt.executeQuery();

ArrayList<T> list = new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
ArrayList<T> list = new ArrayList<>();
List<T> list = new ArrayList<>();

인터페이스에 대고 프로그래밍하기 ! 에 대해서는 어떻게 생각하시나용 🧐

Copy link
Author

Choose a reason for hiding this comment

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

오 넘 좋습니다!!

while (rs.next()) {
list.add(rm.mapRow(rs));
}
return list;
} 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) {
}
}
}

public <T> List<T> context(PreparedStrategy preparedStrategy, RowMapper<T> rm, Object[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
pstmt = preparedStrategy.createStatement(conn);

for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}

rs = pstmt.executeQuery();

ArrayList<T> list = new ArrayList<>();
while (rs.next()) {
list.add(rm.mapRow(rs));
}
return list;
} 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) {
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.springframework.jdbc.core;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public interface PreparedStrategy {

PreparedStatement createStatement(Connection connection) throws SQLException;
}
Loading