ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • DB - 커넥션풀과 DataSource
    스프링/스프링 기초DB 2023. 3. 14. 16:54

    우리가 지금까지 사용했던 서버들은 창을 닫으면 데이터가 날라간다. 그렇기 때문에 데이터베이스를 만들어서 데이터를 저장할 수 있는 공간을 따로 설정해야한다.

     

     

    • Connection - 데이터베이스와 연결시켜주는 객체, getConnection을 통해 DB를 연결시켜줌
    • ResultSet - 쿼리의 결과를 담은 객체
    • Statement - 정적 쿼리를 전달
    • Preparedstatement - sql문을 전달

     

    private void close(Connection con, Statement stmt, ResultSet rs) {
    
        JdbcUtils.closeResultSet(rs);
        JdbcUtils.closeStatement(stmt);
        JdbcUtils.closeConnection(con);
    	// close는 생성과 반대로 (con부터 생성하고 있으니 rs -> stmt -> con 순으로 닫아주기)
    }
    
    private Connection getConnection() throws SQLException {
        Connection con = dataSoure.getConnection();
        log.info("get connection={}, class={}", con, con.getClass());
        return con;
    }

    dataSource의 getConnection메서드를 이용해서 getConnection을 만들어주고 save, findById, delete, update에 사용할 것이다.

    close 메서드는 Connection, Statement, ResultSet을 사용하고 닫아줘야하는데 닫아줄 때 사용할 용도로 만든 메서드이다.

     

    public Member save(Member member) {
        String sql = "insert into member(member_id, money) values (?, ?)";
        // (?, ?)가 아니라 (memberId, money)라 하면 보안상의 위험
    
        Connection con = null;
        PreparedStatement pstmt = null;
    
        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, member.getMemberId());
            pstmt.setInt(2, member.getMoney());
            pstmt.executeUpdate();
            return member;
        } catch (SQLException e) {
            log.error("db error", e);
            throw new RuntimeException(e);
        } finally {
            close(con, pstmt, null);
            // 항상 close가 되도록 finally에서 호출해야 함
        }
    
    }

     

    save 메서드는 아이디와 가지고 있는 돈을 저장하는 메서드이다. Connection은 아까 만들었던 getConnection 객체를 넣어주고, PreparedStatement를 통해 sql을 입력하고, sql의 ?에 첫 번째는 MemberId를, 두 번째에는 Money를 넣어준다.

    finally에서 무조건 close를 해줘야한다.

     

     

    public Member findById(String memberId) throws SQLException {
        // 조회 기능
        String sql = "select * from member where member_id = ?";
    
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
    
        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, memberId);
    
            rs = pstmt.executeQuery();
            if (rs.next()) { // 최초의 커서는 아무것도 가리키지 않고 next할 때마다 다음 데이터를 가리킴
                Member member = new Member();
                member.setMemberId(rs.getString("member_id"));
                member.setMoney(rs.getInt("money"));
                return member;
            } else {
                throw new NoSuchElementException("member not found memberId=" + memberId);
            }
    
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        } finally {
            close(con, pstmt, rs);
        }
    }

    다음은 조회를 할 수 있는 FindById 메서드이다. 방식은 위와 동일하고, ResultSet이 들어갔는데

    rs=pstmt.executeQuery(); 이후에 조건문을 보면 Member의 id를 확인하면서 맞으면 member를 반환하고 틀리면 다른 커서를 가리키는 것이다.

     

    public void update(String memberId, int money) {
        String sql = "update member set money=? where member_id=?";
    
        Connection con = null;
        PreparedStatement pstmt = null;
    
        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setInt(1, money);
            pstmt.setString(2, memberId);
            int resultSize = pstmt.executeUpdate();
            log.info("resultSize={}", resultSize);
        } catch (SQLException e) {
            log.error("db error", e);
            throw new RuntimeException(e);
        } finally {
            close(con, pstmt, null);
        }
    }

    update에서는 그냥 save와 비슷하게 원래 있던 값들을 교체해주는 방식을 사용했다.

     

    public void delete(String memberId) throws SQLException {
        String sql = "delete from member where member_id=?";
        Connection con = null;
        PreparedStatement pstmt = null;
    
        try {
            con = getConnection();
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, memberId);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            log.error("db error", e);
            throw e;
        } finally {
            close(con, pstmt, null);
        }
    }

    마지막으로 delete 메서드인데, delete는 memberId를 받아와서 없애주는 메서드이다.

     

     

     

     

     

    MemberRepositoryV1Test

    @Slf4j
    class MemberRepositoryV1Test {
    
        MemberRepositoryV1 repository;
    
        @BeforeEach
        void beforeEach() {
           
            // 커넥션 풀 사용 (Hikari)
            HikariDataSource dataSource = new HikariDataSource();
            dataSource.setJdbcUrl(URL);
            dataSource.setUsername(USERNAME);
            dataSource.setPoolName(PASSWORD);
    
            repository = new MemberRepositoryV1(dataSource);
        }
    
    
        @Test
        void crud() throws SQLException, InterruptedException {
            Member member = new Member("memberV1", 10000);
            repository.save(member);
        }
    }

    Hikari를 이용해서 커넥션 풀을 사용하고, memberV1이라는 새로운 Member를 만들어서 실행하면 연결시켜둔 h2 데이터 베이스에

    memberV1이 저장되게 된다.

     

    @Test
    void crud() throws SQLException, InterruptedException {
        Member member = new Member("memberV1", 10000);
        repository.save(member);
    
        //findById
        Member findMember = repository.findById(member.getMemberId());
        log.info("findMember={}", findMember);
        assertThat(findMember).isEqualTo(member);
    
        //update: money : 10000 -> 20000
        repository.update(member.getMemberId(), 20000);
        Member updatedMember = repository.findById(member.getMemberId());
        assertThat(updatedMember.getMoney()).isEqualTo(20000);
    
        //delete
        repository.delete(member.getMemberId());
        assertThatThrownBy(() -> repository.findById(member.getMemberId()))
                .isInstanceOf(NoSuchElementException.class);
    
        Thread.sleep(1000);
    }

    save를 제외한 다른 메서드들도 테스트에서 잘 실행된다.

Designed by Tistory.