Post

PreparedStatement란

PreparedStatement란?

일반 statement

1
2
3
4
String userId = "user123";
String sql = "SELECT * FROM users WHERE id = '" + userId + "'";
Statement stmt = connection.createStatement();  // 구문 분석 & 컴파일
ResultSet rs = stmt.executeQuery(sql);          // 쿼리 실행

prepared statement

1
2
3
4
5
String userId = "user123";
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);  // 구문 분석 & 컴파일
pstmt.setString(1, userId);  // 바인딩
ResultSet rs = pstmt.executeQuery();  // 쿼리 실행
  • 일반 statement는 userId string을 쿼리 중간에 +로 연결하는 방식
  • 반면 PreparedStatement는 쿼리 문장 구조는 미리 확정해두고, ?(placeholder) 파라미터만 특정 값으로 치환하는 방식
  • 이는 2가지 주요한 차이를 만들게 되는데, cache와 SQL injection 방지다.

PreparedStatement cache

statement cache와 parse call

  • statement는 DB에서 실행되는 쿼리 문장을 의미한다.
  • DB가 statement를 받으면, 우선 문장을 parsing하면서 (parse call) 문법을 검사하고, 쿼리 플랜을 계산한다. (구문 분석)
  • 매번 받아서 parse 하기에는 자원 소모가 크니까, DB 단에서 statement cache를 가지고 있다.
  • sql_text가 동일하면 statement cache에서 꺼내서 parse call을 스킵한다.
1
2
3
select name from tbl where id = 1 -- miss, parse 후 캐싱
select name from tbl where id = 1 -- hit
select name from tbl where id = 2 -- miss, parse 후 캐싱
  • 위와 같이 일반 statement는 파라미터가 달라지면 sql_text도 달라지기 때문에, 다시 parse 후 캐싱해야 한다.
  • 반면 preparedStatement를 사용하면 아래와 같이 param 부분이 ? 로 처리 된 상태로 parse 후 caching 하기 때문에, 파라미터가 변경되어도 cache를 재활용 할 수 있게 된다.
  • == 성능 상의 이점이 있다.
1
2
select name from tbl where id = ? -- miss, parse 후 캐싱 (id = 1)
select name from tbl where id = ? -- hit, (id = 2)

[!info] PreparedStatement의 생명 주기와 cache

  • PreparedStatement는 session 내에서 생성되고 관리된다.
    • 동일 세션 내에서 재활용 가능하고
    • 세션이 종료되면 PreparedStatement도 함께 소멸한다.
  • 보통 1 DB connection = 1 session이다.
    • CP를 사용하면, connection이 유지되기 때문에, PreparedStatement도 유지된다.?

SQL injection 방지

  • userId에 어떤 값이 들어오느냐에 따라 query 문장 구조 자체가 변경 될 수 있는 일반 statement와 달리,
  • preparedStatement는 문장 구조는 이미 확정이 된 상태라, ? 부분에 들어가는 string은 어디까지나 파라미터로만 인식하게 된다. 따라서 user input으로 문장 구조가 바뀔 가능성이 원천 차단된다.
    • 즉, userId에 어떤 값이 들어오든 문장 구조는 이미 확정이 된 상태다.
This post is licensed under CC BY 4.0 by the author.