스프링 입문

Ch06. 스프링 DB 접근 기술 - 순수 JDBC

webmaster 2021. 12. 1. 21:15
728x90

오래전에 사용한 JDBC 기술 살펴보기

  • build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가

Build.gradle에 추가

  • application.properties에 DB 관련 내용을 추가

JDBC,H2 DB, USERNAME 명시(PASSWORD는 초기 비어있음)

Spring 2.4 이후 버전부터는 반드시 username을 명시하도록 되어있다( 없을 경우 Wrong user name or password 오류가 발생)

  • 순수 JDBC
public class JdbcMemberRepository implements MemberRepository{

    //DB접근하기위한 DataSource
    private final DataSource dataSource;

    public JdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Member save(Member member) {
        String sql = "insert into member(name) values(?)"; //SQL 쿼리
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection(); // connection 가지고 오기
            pstmt = conn.prepareStatement(sql,
                    Statement.RETURN_GENERATED_KEYS);// DB에 Insert하고 난 뒤, Key값을 리턴 받는다
            pstmt.setString(1, member.getName());
            pstmt.executeUpdate();//쿼리 실행
            rs = pstmt.getGeneratedKeys();//DB의 key값을 가지고 온다.
            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);//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() {
        //DataSourceUtils를 통해서 DataSource를 가지고 와야된다.
        //새로운 자원을 가지고 오는것이 아닌 기존에 쓰이고 있는 dataSource를 가지고 와야되기 때문에
        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);
    }
}
  • Spring의 원리를 확인해 볼 수가 있다.
  • Spring 컨테이너가 다형성을 굉장히 편리하게 지원해 준다.
  • 기존의 코드를 변경하지 않고 Application을 설정하는 클래스 파일만 수정하면 다른 코드를 수정할 필요가 없다

  • 개방-폐쇄 원칙(OCP, Open-Closed Principle)
    • 확장에는 열려있고, 수정, 변경에는 닫혀있다.
  • 스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다.
728x90