Statement와 PreparedStatement의 차이점


222

Prepared Statement는 약간 더 강력한 버전의 Statement이며, 최소한 Statement만큼 빠르고 취급하기 쉬워야합니다.
준비된 진술은 매개 변수화 될 수있다

대부분의 관계형 데이터베이스는 다음 네 단계로 JDBC / SQL 쿼리를 처리합니다.

  1. 들어오는 SQL 쿼리 구문 분석
  2. SQL 쿼리 컴파일
  3. 데이터 수집 경로 계획 / 최적화
  4. 최적화 된 쿼리 실행 / 데이터 수집 및 반환

명령문은 데이터베이스에 전송 된 각 SQL 쿼리에 대해 항상 위의 4 단계를 진행합니다. Prepared Statement는 위의 실행 프로세스에서 단계 (1)-(3)을 미리 실행합니다. 따라서, Prepared Statement를 작성할 때 일부 사전 최적화가 즉시 수행됩니다. 그 결과 실행시 데이터베이스 엔진의로드가 줄어 듭니다.

이제 제 질문은 "준비된 진술을 사용하는 다른 이점이 있습니까?"입니다.


12
나에 따라 가장 효율적인 하나의 쿼리가 동적으로 매개 변수화 할 수 있다는 것입니다
후세인 악 타르 와히드 'Ghouri'

답변:


198

의 장점 PreparedStatement:

  • SQL 문의 사전 컴파일 및 DB 측 캐싱은 전체적으로 더 빠른 실행과 동일한 SQL 문을 일괄 적 으로 재사용 할 수있게 합니다 .

  • 내장 된 따옴표 및 기타 특수 문자를 통해 SQL 삽입 공격 을 자동으로 방지 합니다. 이를 위해서는 모든 PreparedStatement setXxx()방법을 사용하여 값을 설정해야합니다.

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    preparedStatement.setString(1, person.getName());
    preparedStatement.setString(2, person.getEmail());
    preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
    preparedStatement.setBinaryStream(4, person.getPhoto());
    preparedStatement.executeUpdate();

    따라서 문자열 연결로 SQL 문자열의 값을 인라인 하지 마십시오 .

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
    preparedStatement.executeUpdate();
  • 하는 SQL 문자열에 비표준 자바 객체의 설정 예 손쉬운 Date, Time, Timestamp, BigDecimal, InputStream( Blob)와 Reader( Clob). 이러한 유형의 대부분 toString()에서는 단순하게 하는 것처럼 "그냥"할 수 없습니다 Statement. PreparedStatement#setObject()아래의 유틸리티 방법에 설명 된 것처럼 루프 내부 에서 사용하도록 리팩터링 할 수도 있습니다.

    public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
        for (int i = 0; i < values.length; i++) {
            preparedStatement.setObject(i + 1, values[i]);
        }
    }

    다음과 같이 사용할 수 있습니다 :

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
    preparedStatement.executeUpdate();

4
설명과 예제와 함께 설명적이고 설명적인 텍스트가 훌륭한 해답을 제공합니다. +1
XenoRo

1
@RD 준비된 명령문은 데이터베이스에 대해 두 번의 왕복이 필요하기 때문에 사실 일 수 있습니다. 첫 번째는 준비하고 두 번째는 실행해야합니다. 그러나 나는 그것을 테스트 할 것입니다. 계획이 데이터베이스 서버에 여전히 캐시되어 있다고 가정 Statement하지만 테스트 할 가치가 있습니다.
Brandon

2
Java로 확실히 말할 수는 없지만 일반적으로 준비된 문장은 "내장 인용 부호 및 기타 특수 문자를 이스케이프 처리" 하지 않습니다 . 대신, 실행 가능한 SQL과 data 를 분리하여 SQL이 쿼리 계획으로 변환 된 후 매개 변수를 별도의 정보 패킷으로 DBMS에 보냅니다.
IMSoP

@BalusC-자세한 설명 감사합니다.
CodeBee ..

49
  1. 사전 컴파일 (한 번)되므로 동적 SQL을 반복적으로 실행하기 위해 더 빠릅니다 (매개 변수 변경)

  2. 데이터베이스 명령문 캐싱으로 DB 실행 성능 향상

    데이터베이스는 이전에 실행 된 명령문에 대한 실행 계획 캐시를 저장합니다. 이를 통해 데이터베이스 엔진은 이전에 실행 된 명령문에 대한 계획을 재사용 할 수 있습니다. PreparedStatement는 매개 변수를 사용하므로 매개 변수가 실행될 때마다 동일한 SQL로 표시되므로 데이터베이스는 이전 액세스 계획을 재사용하여 처리를 줄입니다. 명령문은 매개 변수를 SQL 문자열에 "인라인"하므로 DB에 동일한 SQL로 표시되지 않으므로 캐시 사용이 금지됩니다.

  3. 이진 통신 프로토콜은 DB 서버에 대한 더 적은 대역폭과 더 빠른 통신 호출을 의미합니다

    준비된 문은 일반적으로 비 SQL 이진 프로토콜을 통해 실행됩니다. 즉, 패킷에 데이터가 적으므로 서버와의 통신 속도가 빠릅니다. 일반적으로 네트워크 작업은 인 메모리 CPU 작업보다 속도가 느린 디스크 작업보다 속도가 느립니다. 따라서 네트워크를 통해 전송되는 데이터 양을 줄이면 전체 성능에 좋은 영향을 미칩니다.

  4. 제공된 모든 매개 변수 값에 대해 텍스트를 이스케이프하여 SQL 삽입을 방지합니다.

  5. 쿼리 코드와 매개 변수 값 (연결된 SQL 문자열과 비교)을보다 강력하게 분리하여 가독성을 높이고 코드 관리자가 쿼리의 입력 및 출력을 빠르게 이해할 수 있도록합니다.

  6. Java에서는 getMetadata () 및 getParameterMetadata ()를 호출하여 각각 결과 세트 필드 및 매개 변수 필드에 반영 할 수 있습니다.

  7. Java에서는 setObject, setBoolean, setByte, setDate, setDouble, setDouble, setFloat, setInt, setLong, setShort, setTime, setTimestamp를 통해 Java 객체를 매개 변수 유형으로 지능적으로 받아들입니다. () 형식).

  8. Java에서는 setArray 메소드를 통해 매개 변수 유형으로 SQL ARRAY를 승인합니다.

  9. Java에서는 setClob / setNClob, setBlob, setBinaryStream, setCharacterStream / setAsciiStream / setNCharacterStream 메소드를 통해 CLOB, BLOB, OutputStream 및 Reader를 매개 변수 "feeds"로 각각 승인합니다.

  10. Java에서는 setURL, setRowId, setSQLXML 및 setNull 메소드를 통해 DB 특정 값을 SQL DATALINK, SQL ROWID, SQL XML 및 NULL에 설정할 수 있습니다.

  11. Java에서는 Statement의 모든 메소드를 상속합니다. addBatch 메소드를 상속하며 addBatch 메소드를 통해 일괄 처리 된 SQL 명령 세트와 일치하도록 매개 변수 값 세트를 추가 할 수 있습니다.

  12. Java에서는 특수 유형의 PreparedStatement (서브 클래스 CallableStatement)를 사용하여 고성능, 캡슐화, 절차 적 프로그래밍 및 SQL 지원, DB 관리 / 유지 보수 / 논리 로직, 독점 DB 로직 및 기능 사용을 지원하는 스토어드 프로 시저를 실행할 수 있습니다.


둘 다 단지 인터페이스 일 때 이러한 모든 불가사의가 어떻게 가능합니까?!?!
Rafael

1
'이적'는 표준 공장 메소드를 통해 가능하게되는 창 (벤더 특정) 인터페이스의 구현 : Connection.createStatementConnection.prepareStatement. 이 디자인은 인터페이스에 대해 작업하도록하여 특정 구현 클래스를 알 필요가 없으며 그러한 구현 클래스와의 불필요한 긴밀한 결합을 피합니다. 모든 것은 Java jdbc docs & Java docs의 예제로 설명되었습니다. :)
Glen Best

귀하의 "엄지 손가락의 규칙으로"부분은 아무 의미가 그것을 주변에 다른 방법 🤔하지 않습니다 확인
bhathiya - 페레라

38

PreparedStatementSQL 인젝션 공격 을 막는 데 아주 좋은 방어책 입니다. 바인딩 매개 변수 값은 "작은 바비 테이블" 을 방지하여 원치 않는 방문을 방지 하는 좋은 방법 입니다.


6
그러면 준비된 명령문을 통해 SQL 삽입을 어떻게 수행합니까?
Michael Borgwardt

2
Michael, 준비된 명령문에 인수로 전달 된 변수는 JDBC 드라이버에서 자동으로 이스케이프됩니다.
CodeBee ..

3
SQL 주입 공격이 준비된 명령문에 대해 어떻게 작동하는지 예를 들어 줄 수 있습니까? 데이터베이스 코드에 버그가 있다고 가정합니까?
피터 Recore

2
예,하지만 "꽤 바보"를 훨씬 뛰어 넘습니다. 어리석은 일이 마음에 든다. 1 온스의 지식을 가진 사람은 그렇게하지 않을 것입니다.
duffymo

2
또한 많은 데이터베이스 공급 업체는 ORDER BY특정 위치 (think 및 기타 페이지 매김 솔루션)에서 열 이름 (think ) 및 / 또는 숫자 상수를 매개 변수화하는 것을 지원하지 않으므로 LIMIT, OFFSETPrepared Statements 및 parameterization이 어디에서나 사용되는 경우에도 SQL 주입으로 공격받을 수 있습니다. 가능한.
dnet

31

Statement에 비해 PreparedStatement의 장점은 다음과 같습니다.

  1. PreparedStatement는 특수 문자를 자동으로 이스케이프하므로 SQL 주입 공격을 방지하는 데 도움이됩니다.
  2. PreparedStatement를 사용하면 매개 변수 입력으로 동적 조회를 실행할 수 있습니다.
  3. PreparedStatement는 조회에 대한 입력 매개 변수를 설정하기 위해 다른 유형의 세터 메소드를 제공합니다.
  4. PreparedStatement가 Statement보다 빠릅니다. PreparedStatement를 재사용하거나 여러 쿼리를 실행하기 위해 일괄 처리 방법을 사용할 때 더 잘 보입니다.
  5. PreparedStatement는 setter 메소드를 사용하여 객체 지향 코드를 작성하는 데 도움이되는 반면 Statement를 사용하면 쿼리를 생성하기 위해 String Concatenation을 사용해야합니다. 설정할 매개 변수가 여러 개인 경우 문자열 연결을 사용하여 쿼리를 작성하면 매우보기 흉하고 오류가 발생하기 쉽습니다.

SQL 주입 문제에 대한 자세한 내용은 http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example참조하십시오.


나는 당신의 기사를 정말로 읽었습니다. 내 질문은 왜 누군가가 Statement를 사용하는 것입니까?! 정적 쿼리조차도!
pedram bashiri

나는 항상 PreparedStatement를 사용하는데, Statement가 더 많은 이점을 가질 수있는 특정 시나리오를 모른다.
Pankaj

13

추가 할 것이 많지 않습니다.

1-루프에서 (1 회 이상) 쿼리를 실행하려는 경우 언급 한 최적화로 인해 준비된 명령문이 더 빠를 수 있습니다.

2-매개 변수화 된 쿼리는 SQL 삽입을 피하는 좋은 방법입니다. 매개 변수화 된 쿼리는 PreparedStatement에서만 사용 가능합니다.


10

진술은 정적이며 준비된 진술은 동적입니다.

명령문은 DDL에 적합하며 DML에 대한 준비된 통계입니다.

준비된 진술은 더 빠르지 만 진술은 느립니다.

더 많은 차이점 (아카이브)


7

명령문에서 CLOB를 수행 할 수 없습니다.

그리고 : (OraclePreparedStatement) ps


7

mattjames에 의해 인용

JDBC에서 명령문 사용은 BIND VARIABLES를 허용 할 수없는 유일한 명령문 유형이므로 DDL (ALTER, CREATE, GRANT 등)에 사용되도록 100 % 현지화되어야합니다. PreparedStatements 또는 CallableStatements는 다른 모든 유형의 명령문 (DML, 쿼리)에 사용해야합니다. 이들은 바인드 변수를 허용하는 명령문 유형입니다.

이것은 사실, 규칙, 법입니다. 어디에서나 준비된 진술을 사용하십시오. 어디에도 진술을 사용하지 마십시오.


5

SQL 주입은 준비된 명령문에서 무시되므로 준비된 명령문의 보안이 향상됩니다.


4
  • 읽기 쉽다
  • 쿼리 문자열을 상수로 쉽게 만들 수 있습니다

4

명령문은 정적 SQL 문을 실행하는 데 사용되며 입력 매개 변수를 승인 할 수 없습니다.

PreparedStatement는 SQL 문을 여러 번 동적으로 실행하는 데 사용됩니다. 입력 매개 변수를 승인합니다.


4

준비 또는 매개 변수화 된 쿼리의 또 다른 특징 : 이 기사에서 가져온 참조.

이 명령문은 동일한 SQL 문이 고효율로 반복적으로 실행되는 데이터베이스 시스템의 기능 중 하나입니다. 준비된 문장은 템플릿의 한 종류이며 다른 매개 변수를 가진 응용 프로그램에서 사용됩니다.

명령문 템플리트가 준비되어 데이터베이스 시스템으로 전송되고 데이터베이스 시스템은이 템플리트에서 구문 분석, 컴파일 및 최적화를 수행하고 실행하지 않고 저장합니다.

템플릿 생성 후 응용 프로그램에서 where 절이 전달되지 않는 일부 매개 변수와 같은 일부 매개 변수는 이러한 매개 변수를 SQL 문의 데이터베이스 시스템 및 데이터베이스 시스템 사용 템플릿으로 보내고 요청에 따라 실행됩니다.

준비된 문은 응용 프로그램이 다른 기술과 프로토콜을 사용하여 매개 변수를 준비 할 수 있으므로 SQL 주입에 매우 유용합니다.

그 때 데이터 수가 증가하고 인덱스가 자주 변경되는 경우이 상황에서는 새로운 쿼리 계획이 필요하므로 준비된 명령문이 실패 할 수 있습니다.


3

Statement 인터페이스는 매개 변수없이 정적 SQL 문을 실행합니다.

PreparedStatement 인터페이스 (확장 명령문)는 매개 변수가 있거나없는 사전 컴파일 된 SQL 문을 실행합니다.

  1. 반복 실행에 효율적

  2. 미리 컴파일되어 있으므로 더 빠릅니다.


2

혼동하지 마십시오 : 단순히 기억하십시오

  1. 명령문은 DDL과 같은 정적 쿼리 (예 : create, drop, alter)에 사용되고 PrepareStatement는 동적 쿼리 (예 : DML 쿼리)에 사용됩니다.
  2. Statement에서는이 PrepareStatement가 시간 효율적이기 때문에 PrepareStatement에서 쿼리가 사전 컴파일되는 동안 쿼리가 사전 컴파일되지 않습니다.
  3. PrepareStatement는 작성시 인수를 사용하지만 Statement는 인수를 사용하지 않습니다. 예를 들어 테이블을 작성하고 요소를 삽입하려면 :: PrepareStatement를 사용하여 명령문을 사용하여 테이블을 작성 (정적)하고 요소를 삽입하십시오 (동적).

1
PrepareStatement는 작성시 인수를 사용하지만 Statement는 인수를 사용하지 않습니다.?

1

이 질문에 대한 모든 대답을 따라- Statement(하지만 SQL 인젝션 사용)을 사용하여 작동하는 레거시 코드를 & PreparedStatement주위의 의미에 대한 이해가 부족하기 때문에 훨씬 느린 코드로 사용하는 솔루션 으로 변경했습니다 .Statement.addBatch(String sql)PreparedStatement.addBatch()

그래서 나는 다른 사람들이 같은 실수를하지 않도록 시나리오를 여기에 나열하고 있습니다.

내 시나리오는

Statement statement = connection.createStatement();

for (Object object : objectList) {
    //Create a query which would be different for each object 
    // Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();

따라서 위의 코드에서 수천 개의 서로 다른 쿼리가 있었으며 모두 동일한 문에 추가되었으며 캐시되지 않은 문이 좋았 으며이 코드는 앱에서 거의 실행되지 않았기 때문에이 코드가 더 빠르게 작동했습니다.

이제 SQL 인젝션을 수정하기 위해이 코드를로 변경했습니다.

List<PreparedStatement> pStatements = new ArrayList<>();    
for (Object object : objectList) {
    //Create a query which would be different for each object 
    PreparedStatement pStatement =connection.prepareStatement(query);
    // This query can't be added to batch because its a different query so I used list. 
    //Set parameter to pStatement using object 
    pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately          
for (PreparedStatement ps : pStatements) {
    ps.executeUpdate();
}

그래서 당신은 수천 개의 PreparedStatement객체를 만들기 시작했고 결국 시나리오를 요구했기 때문에 일괄 처리를 사용할 수 없었습니다. 수천 개의 UPDATE 또는 INSERT 쿼리가 있으며 이러한 쿼리는 모두 다릅니다.

성능 저하 비용없이 SQL 삽입 수정이 필수이며이 PreparedStatement시나리오에서는 가능하지 않다고 생각합니다 .

또한 내장 된 일괄 처리 기능을 사용하는 경우 하나의 명령문 만 닫는 것에 대해 걱정해야하지만이리스트 방식을 사용하면 재사용하기 전에 명령문을 닫아야합니다 . PreparedStatement 재사용

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.