-
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를 제외한 다른 메서드들도 테스트에서 잘 실행된다.
'스프링 > 스프링 기초DB' 카테고리의 다른 글
데이터 접근 기술 - JPA (0) 2023.03.26 db 테스트에서의 트랜잭션 (0) 2023.03.25 스프링 예외 추상화 (0) 2023.03.19 체크 예외, 언체크(런타임) 예외 (0) 2023.03.19 DB 트랜잭션 - 자동 커밋, 수동 커밋 / DB 락 (0) 2023.03.14