Skip to content

Commit

Permalink
[JDBC 라이브러리 구현하기 - 1,2단계] 준팍(박준현) 미션 제출합니다. (#263)
Browse files Browse the repository at this point in the history
* feat: UserDao 모든 메서드 구현

* feat: update 메서드 분리

* feat: queryForObject 메서드 분리

* feat: query 메서드 분리

* feat: query 메서드 분리

* test: JdbcTemplate 테스트 케이스 추가

* feat: queryForObject의 where 절 다중 조건 대응 구현
  • Loading branch information
junpakPark authored Sep 27, 2023
1 parent 7182a49 commit af46958
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 90 deletions.
114 changes: 28 additions & 86 deletions app/src/main/java/com/techcourse/dao/UserDao.java
Original file line number Diff line number Diff line change
@@ -1,121 +1,63 @@
package com.techcourse.dao;

import com.techcourse.domain.User;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 static final RowMapper<User> USER_ROW_MAPPER = (resultSet, rowNum) ->
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) {}
}
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 = ? ";

jdbcTemplate.update(sql, user.getAccount(), user.getPassword(), user.getEmail());
}

public List<User> findAll() {
// todo
return null;
final var sql = "select id, account, password, email from users ";

return jdbcTemplate.query(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) {}
}
return jdbcTemplate.queryForObject(sql, USER_ROW_MAPPER, id)
.orElseThrow(DataAccessException::new);
}

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, USER_ROW_MAPPER, account)
.orElseThrow(DataAccessException::new);
}

}
4 changes: 2 additions & 2 deletions app/src/test/java/com/techcourse/dao/UserDaoTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.techcourse.dao;

import static org.assertj.core.api.Assertions.assertThat;

import com.techcourse.config.DataSourceConfig;
import com.techcourse.domain.User;
import com.techcourse.support.jdbc.init.DatabasePopulatorUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class UserDaoTest {

private UserDao userDao;
Expand Down
79 changes: 77 additions & 2 deletions jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,92 @@
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 java.util.Optional;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;

public class JdbcTemplate {

private static final Logger log = LoggerFactory.getLogger(JdbcTemplate.class);
public static final String QUERY_FORMAT = "query : {}";

private final DataSource dataSource;

public JdbcTemplate(final DataSource dataSource) {
this.dataSource = dataSource;
}

public void update(final String sql, final Object... args) {
try (
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql)
) {
log.debug(QUERY_FORMAT, sql);

bindStatementWithArgs(args, preparedStatement);
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new DataAccessException(e);
}
}

public <T> Optional<T> queryForObject(final String sql, final RowMapper<T> rowMapper, final Object... args) {
try (
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql)
) {
bindStatementWithArgs(args, preparedStatement);
ResultSet resultSet = preparedStatement.executeQuery();

log.debug(QUERY_FORMAT, sql);

if (resultSet.next()) {
T result = rowMapper.mapRow(resultSet, resultSet.getRow());
resultSet.close();

return Optional.ofNullable(result);
}

resultSet.close();

return Optional.empty();
} catch (SQLException e) {
throw new DataAccessException(e);
}
}

private void bindStatementWithArgs(Object[] args, PreparedStatement preparedStatement) throws SQLException {
for (int i = 1; i <= args.length; i++) {
preparedStatement.setObject(i, args[i - 1]);
}
}

public <T> List<T> query(final String sql, final RowMapper<T> rowMapper) {
try (
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery()
) {
log.debug(QUERY_FORMAT, sql);

List<T> results = new ArrayList<>();
while (resultSet.next()) {
results.add(
rowMapper.mapRow(resultSet, resultSet.getRow())
);
}

return results;
} catch (SQLException e) {
throw new DataAccessException(e);
}
}

}
12 changes: 12 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,12 @@
package org.springframework.jdbc.core;

import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Nullable;

@FunctionalInterface
public interface RowMapper<T> {

@Nullable
T mapRow(ResultSet resultSet, int rowNumber) throws SQLException;
}
99 changes: 99 additions & 0 deletions jdbc/src/test/java/nextstep/jdbc/JdbcTemplateTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,104 @@
package nextstep.jdbc;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

class JdbcTemplateTest {

private JdbcTemplate jdbcTemplate;
@Mock
private DataSource dataSource;
@Mock
private Connection connection;
@Mock
private PreparedStatement preparedStatement;
@Mock
private ResultSet resultSet;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
jdbcTemplate = new JdbcTemplate(dataSource);
}

@Test
void update() throws SQLException {
// given
when(dataSource.getConnection()).thenReturn(connection);
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);

String testSql = "UPDATE some_table SET column1 = ?, column2 = ? WHERE id = ?";
Object[] params = {"value1", "value2", 1};

// when
jdbcTemplate.update(testSql, params);

// then
verify(preparedStatement, times(1)).setObject(1, "value1");
verify(preparedStatement, times(1)).setObject(2, "value2");
verify(preparedStatement, times(1)).setObject(3, 1);
verify(preparedStatement, times(1)).executeUpdate();
}

@Test
void queryForObject() throws SQLException {
// given
when(dataSource.getConnection()).thenReturn(connection);
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
when(preparedStatement.executeQuery()).thenReturn(resultSet);
when(resultSet.next()).thenReturn(true);

RowMapper<String> rowMapper = (rs, rowNum) -> rs.getString("column_name");
when(resultSet.getString("column_name")).thenReturn("mockValue");

// when
String sql = "SELECT * FROM some_table where id = ? ";
Optional<String> result = jdbcTemplate.queryForObject(sql, rowMapper, 1);

// then
assertThat(result).isPresent();
assertThat(result.get()).contains("mockValue");
}

@Test
void query() throws SQLException {
// given
when(dataSource.getConnection()).thenReturn(connection);
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
when(preparedStatement.executeQuery()).thenReturn(resultSet);

when(resultSet.next()).thenReturn(true)
.thenReturn(true)
.thenReturn(false);

when(resultSet.getString("column_name")).thenReturn("value1", "value2");

RowMapper<String> rowMapper = (rs, rowNum) -> rs.getString("column_name");

// when
String sql = "SELECT * FROM some_table ";
List<String> results = jdbcTemplate.query(sql, rowMapper);

// then
assertThat(results).hasSize(2)
.contains("value1", "value2");
}

}

0 comments on commit af46958

Please sign in to comment.