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,2단계] 준팍(박준현) 미션 제출합니다. #263

Merged
merged 7 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
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);
Copy link
Member

Choose a reason for hiding this comment

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

굿입니다~

}

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;
Copy link
Member

Choose a reason for hiding this comment

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

현재는 rowNumber가 필요없어 보이는데 이렇게 구현하신 이유는 무엇인가요?

}
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");
}

}
Loading