@Override
    public Member save(Member member) {
        String sql = "insert into member(name) values(?)";

        Connection conn = dataSource.getConnection();

        PreparedStatement pstmt = conn.prepareStatement(sql, );
        pstmt.setString(1, member.getName());

        pstmt.executeUpdate();

        return null;
    }

위 코드는 순수 jdbc로 짠 코드다… 일단 제낀다. 설명도 자세히 안해줌

전에 짠 코드중에 리포지 인터페이스를 자바로 구현한 클래스에서

private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

이 부분이 있었다. sequence를 하나씩 증가시켜서, id를 자동으로 생성하게끔 하는 코드였음. member가 추가될때마다 1,2,3… 이런 부분도 추가해줘야 한다.

이런 것들을 실제로 다 구현하는건… 의미가 없다고 판단하신듯. 강의안에서 복사붙여넣기 한다.

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class JdbcMemberRepository implements MemberRepository {
 private final DataSource dataSource;
 public JdbcMemberRepository(DataSource dataSource) {
 this.dataSource = dataSource;
 }
 @Override
 public Member save(Member member) {
 String sql = "insert into member(name) values(?)";
 Connection conn = null;
 PreparedStatement pstmt = null;
 ResultSet rs = null;
 try {
 conn = getConnection();
 pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
 pstmt.setString(1, member.getName());
 pstmt.executeUpdate();
 rs = pstmt.getGeneratedKeys();
 if (rs.next()) {
 member.setId(rs.getLong(1));
 } else {
 throw new SQLException("id 조회 실패");
 }
 return member;
 } catch (Exception e) {
 throw new IllegalStateException(e);
 } finally {
 close(conn, pstmt, rs);
 }
 }
 @Override
 public Optional<Member> findById(Long id) {
 String sql = "select * from member where id = ?";
 Connection conn = null;
 PreparedStatement pstmt = null;
 ResultSet rs = null;
 try {
 conn = getConnection();
 pstmt = conn.prepareStatement(sql);
 pstmt.setLong(1, id);
 rs = pstmt.executeQuery();
 if(rs.next()) {
 Member member = new Member();
 member.setId(rs.getLong("id"));
 member.setName(rs.getString("name"));
 return Optional.of(member);
 } else {
 return Optional.empty();
 }
 } catch (Exception e) {
 throw new IllegalStateException(e);
 } finally {
 close(conn, pstmt, rs);
 }
 }
 @Override
 public List<Member> findAll() {
 String sql = "select * from member";
 Connection conn = null;
 PreparedStatement pstmt = null;
 ResultSet rs = null;
 try {
 conn = getConnection();
 pstmt = conn.prepareStatement(sql);
 rs = pstmt.executeQuery();
 List<Member> members = new ArrayList<>();
 while(rs.next()) {
 Member member = new Member();
 member.setId(rs.getLong("id"));
 member.setName(rs.getString("name"));
 members.add(member);
 }
 return members;
 } catch (Exception e) {
 throw new IllegalStateException(e);
 } finally {
 close(conn, pstmt, rs);
 }
 }
 @Override
 public Optional<Member> findByName(String name) {
 String sql = "select * from member where name = ?";
 Connection conn = null;
 PreparedStatement pstmt = null;
 ResultSet rs = null;
 try {
 conn = getConnection();
 pstmt = conn.prepareStatement(sql);
 pstmt.setString(1, name);
 rs = pstmt.executeQuery();
 if(rs.next()) {
 Member member = new Member();
 member.setId(rs.getLong("id"));
 member.setName(rs.getString("name"));
 return Optional.of(member);
 }
 return Optional.empty();
 } catch (Exception e) {
 throw new IllegalStateException(e);
 } finally {
 close(conn, pstmt, rs);
 }
 }
 private Connection getConnection() {
 return DataSourceUtils.getConnection(dataSource);
 }
 private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
 try {
 if (rs != null) {
 rs.close();
 }
 } catch (SQLException e) {
 e.printStackTrace();
 }
 try {
 if (pstmt != null) {
 pstmt.close();
 }
 } catch (SQLException e) {
 e.printStackTrace();
 }
 try {
 if (conn != null) {
 close(conn);
 }
 } catch (SQLException e) {
 e.printStackTrace();
 }
 }
 private void close(Connection conn) throws SQLException {
 DataSourceUtils.releaseConnection(conn, dataSource);
 }

간단한 기능인데도 어마어마한 길이다.

김영한님은 이 코드를 보니… 여의도에서 SI하던 시절이 떠오른다고 한다… 10년전 그당시에도 스프링프레임워크는 있었는데 스프링을 못쓰게 했다고 한다….

save부터 설명하겠다.

@Override
    public Member save(Member member) {
        String sql = "insert into member(name) values(?)";

        Connection conn = null; 
        PreparedStatement pstmt = null;
        ResultSet rs = null; 
        try {
            conn = getConnection(); //디비와의 커넥션을 가져온다.
						//디비에 sql문을 주고, 생성된 key를 가져온다...?
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
					  //setString매개변수의 1은 sqp의 values(?)와 매칭된다. values(?)에 member.getNaem()으로 가져온 값을 넣는다.
            pstmt.setString(1, member.getName());
            pstmt.executeUpdate(); //디비에 실제 쿼리를 보낸다.
            rs = pstmt.getGeneratedKeys(); //쿼리를 보내서 생성된 키를 가져온다.

            if (rs.next()) {
                member.setId(rs.getLong(1));
            } else {
                throw new SQLException("id 조회 실패");
            }
            return member;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally { //리소스 반환해야 한다. 반환하지 않으면 데이터베이스커넥션이 계속 쌓여서 장애가 생긴다.
            close(conn, pstmt, rs);
        }
    }

리소스 반환부가 중요하다고 한다. close함수를 보면

private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (pstmt != null) {
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                close(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    private void close(Connection conn) throws SQLException {
        DataSourceUtils.releaseConnection(conn, dataSource);
    }
}

try-catch문으로 막 얽힌… 리소스 반환도 복잡하다.

findById(조회)부분이다.

@Override
    public Optional<Member> findById(Long id) {
        String sql = "select * from member where id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setLong(1, id); //여기까진 음

            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            } else {
                return Optional.empty();
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }