Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 DB 1편 강좌를 수강하며 정리한 글입니다. *
JDBC (Java Database Connectivity)
클라이언트가 직접 데이터베이스에 접근하지 않고, 애플리케이션 서버를 거쳐 DB의 데이터를 받아오게 된다.
애플리케이션 서버는 DB서버와 연결을 해두고, SQL과 같은 DB 구문을 통해 요청을 한 데이터를 받아오게 된다.
과거의 수많은 DB들은 모두 사용법이 달랐고 애플리케이션 서버는 DB의 구현법에 의존할 수밖에 없었다.
이를 해결하기 위해 JDBC 자바 표준 인터페이스가 등장한다.
JDBC는 DB에 접근하기 위한 API를 제공한다.
JDBC 인터페이스를 각 DataBase 회사들이 구현체인 라이브러를 만들어 제공한다. 이 라이브러리를 JDBC 드라이버라고 한다.
애플리케이션 서버는 JDBC 인터페이스를 사용해 구현하면, 서버 로직의 변경 없이 어떤 DB로든( JDBC 드라이버를 제공하는) 교체할 수 있다. 단, SQL 쿼리 문은 공통 기느을 제외하면 DB마다 사용법이 다르다. 이는 JPA로 어느 정도 해결할 수 있다.
SQLMapper
JDBC는 상당히 오래된 기술로 복잡함을 개선하기위해 SQLMapper를 통해서 JDBC를 사용한다.
하지만 여전히 SQL은 직접 작성해야한다.
대표적으로 스프링 Jdbc Template와 MyBatis가 있다.
ORM
객체를 DB의 테이블과 매핑하여 SQL을 자동으로 만들어 DB와 소통한다.
ORM은 사용하는 DB를 인식해서 DB에 맞는 SQL문을 만들어 주기 때문에 DB마다 다른 SQL문을 사용할 필요가 없다.
대표적으로 JPA라는 자바 표준 인터페이스를 하이퍼네이트와 이클립스링크가 구현하고 있다.
Connection 구현체 요청하기
java.sql.Connection은 인터페이스로, 각 DB의 구현체를 DriverManager.getConnection(URL, USERNAME, PASSWORD)로 가져올 수 있다.
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
h2를 사용하면 구현체는 org.h2.jdbc.JdbcConnection를 제공한다.
DriverManager는 JDBC가 제공하는 기능으로, 라이브러리에 등록된 드라이버 목록에서 커넥션을 요청한다.
여러 DB 드라이버가 있다면 순서대로 URL, USERNAME, PASSWORD를 통해 접근해보고 접근이 되면, 구현체가 반환된다.
PreparedStatement를 사용하면 sql 쿼리 문의 ?값을 원하는 데이터로 대체할 수 있다.(파라미터 바인딩)
String sql = "insert into member(member_id, money) value (?,?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, member.getMemberId()); // 첫번째 ?를 member.getMemberId()로 대체한다.
pstmt.setInt(2,member.getMoney());
pstmt.executeUpdate(); //실제 db로 sql 전달
} catch (SQLException e) {
...
}
PreparedStatement와 Connection은 사용 후 자원을 반환해야 한다.
만약 자원을 반납하지 않으면 연결이 계속 유지되면서 리소스가 낭비될 수 있다.
파라미터 바인딩을 하지 않고 단순히 문자열 +연산으로 sql문을 보내면 심각한 문제를 야기할 수 있다.
변수 값이 sql 쿼리문으로 대체되기 때문에, 해커가 해당 변수 값에 쿼리 문자열을 넣어 보내면 SQL Injection 공격을 할 수 있다.
하지만 PreparedStatement 통한 파라미터 바인딩은 특정 형태의 값만이 대체되도록 강제할 수 있다.
PreparedStatement.executeUpdate()는 값을 DB에 반영할 때 사용하지만,
SELECT와 같이 값을 가져와야 할 때는 PreparedStatement.executeQuery()를 사용하면 ResultSet 객체로 데이터를 반환받을 수 있다.
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) {
...
ResultSet은 select 쿼리문을 통한 반환 데이터를 순서대로 담아주는 자료구조이다.
ResultSet은 iterator와 비슷한, cursor를 통해 데이터를 순서대로 이동시킬 수 있다.
cursor는 next() 메서드를 통해 다음 데이터로 이동하고, 현재 cursor에서 getType()을 통해 데이터를 꺼낼 수 있다.
DELETE와 UPDATE도 동일하게 사용하면서 sql 문만 교체하면 된다.
따라서 CRUD만 메서드 별로 구현하는데도 굉장히 많은 중복이 발생하고 있다.
'개발 > Spring DataBase' 카테고리의 다른 글
[Spring DB] 6. 프로필과 JdbcTemplate (0) | 2022.07.24 |
---|---|
[Spring DB] 5. 런타임 예외의 활용 (0) | 2022.07.21 |
[Spring DB] 4. 스프링의 트랜잭션 매니저 (0) | 2022.07.19 |
[Spring DB] 3. 트랜잭션과 ACID, DB Lock (0) | 2022.07.18 |
[Spring DB] 2. 커넥션 풀과 DataSource (0) | 2022.07.16 |