Spring

JDBC Statement와 PreparedStatement

개발정리 2021. 7. 5. 11:27

→ 이 둘의 가장 큰 차이는 캐시사용 여부의 차이이다.

 

쿼리문을 실행하게 될 때 [문장분석 → 컴파일 → 실행단계] 의 과정을 실행하게 되는데 Statement 의 경우 쿼리가 매번 실행될 때 이러한 과정이 일어나는 반면, PreparedStatement 의 경우 처음만 위 과정을 거친 후에 캐싱된 실행계획을 재사용하므로 DB 부하를 줄일 수 있다.

PreparedStatement

  • 미리 컴파일 한 후 파라미터 값만 동적쿼리로 재사용할 때 주로 사용된다.
  • 개발자 의도에 작성된 쿼리문이 캐싱되어 있으므로 SQL Injection 과 같이 임의적인 SQL문을 주입하여 실행되게 하는 보안상의 이슈도 막을 수 있다.

Statemetn

  • 단 한번의 조회나 create, alter, drop 같은 작업을 할 시에는 Statement 를 사용하는데, 이 때 sql injection 이슈를 막기 위해 변수값에 대한 유효성 검사를 하는것이 필수이다.

[참고]

sql 인젝션의 경우에는 ;(세미콜론) 을 먼저 입력해주어 이전 쿼리문들은 모두 취소시킨 후에 내가 원하는 쿼리를 보내는 입력하는 것이 가장 일반적인 공격방법이다.


(1 ~ 입력받은 수) 를 Statement 방식과 PreparedStatement 방식으로 INSERT

public ResultParam callStatementAndPreparedStatement (Param param) {

        // pool, 타임아웃별 종류 찾아보고 표로 정리해보자.
        String url = "jdbc:mysql://localhost:3306/dev_study?characterEncoding=UTF-8&serverTimezone=UTC&socketTimeout=500";
        Connection conn = null;
        PreparedStatement pstmt = null;
        Statement stmt = null;
        ResultParam result = new ResultParam();

        try {
            conn = DriverManager.getConnection ( url, "root", "123123" );

            // Statement
            stmt = conn.createStatement();
            String query = "";

            long start = System.currentTimeMillis();
            for (int i = 0; i < param.getNumber(); i++) {
                query = "Insert Into testTable (num) Values(" + i + ")";
                stmt.executeUpdate(query);
            }
            long end = System.currentTimeMillis();

            result.setStatementResultTime(String.valueOf((end-start)/1000.0));

            // PreparedStatement
            String preQuery = "Insert Into testTable (num) Values(?)";
            pstmt = conn.prepareStatement (preQuery);

            start = System.currentTimeMillis();
            for (int i = 0; i < param.getNumber(); i++) {
                pstmt.setInt( 1, i);
                pstmt.executeUpdate ();
            }
            end = System.currentTimeMillis();
            
            result.setPreparedStatementResultTime(String.valueOf((end-start)/1000.0));
        } catch(Exception e){
            System.out.println (e.getMessage () );
        } finally{
            try {
                pstmt.close ();
            } catch (Exception ignored) {
            }
            try {
                conn.close ();
            } catch (Exception ignored) {
            }
        }

        return result;
    }

[RESULT]

* 1~10 인경우 

{statementResultTime: "0.016", preparedStatementResultTime: "0.006"}

* 1~100 인 경우 

{statementResultTime: "0.104", preparedStatementResultTime: "0.074"}

* 1~1000 인 경우

{statementResultTime: "0.016", preparedStatementResultTime: "0.006"}

 * 1~10000 인 경우

{statementResultTime: "5.727", preparedStatementResultTime: "5.173"}