executeQuery()나 executeUpdate()를 실행하는 시점에 파라미터로 SQL문을 전달하게 된다. 이때 SQL문은 완성된 형태로 한눈에 무슨 SQL문인지 파악하기 쉽다. 하지만 Statement의 경우 SQL문을 수행하는 과정에서 매번 컴파일을 해야하기 때문에 성능의 이슈가 있다.
String query = 'SELECT id, name, age FROM User WHERE id = ' + userId;
이름에서부터 알 수 있듯이 준비된 Statement이다. 여기서 준비는 컴파일을 의미하고, 이 덕에 성능이 좋다. 첫 수행에만 쿼리문을 수행하고 그 이후에 사용할 때에는 캐시에 담아 재사용한다. 보통 조건절에서 사용되고 ?부분에만 변화를 주어 지속적으로 SQL을 수행하기 때문에 한눈에 무슨 SQL문인지 파악하기는 어렵다.
String query = 'SELECT id, name, age FROM User WHERE id = ?';
가장 큰 차이점은 캐시의 사용 여부라고 할 수 있다. 우선 Statement의 경우에는 쿼리문이 들어오면 그때 그때 컴파일을 하게 된다. 반면에 PreparedStatement는 쿼리문이 들어오면 한번 실행을 하고 이 쿼리문을 캐시에 저장한다. 그 이후에 이 쿼리문에 다시 접근하게 되면 캐시에서 바로 재사용하여 성능이 훨씬 좋다.
(PreparedStatement는 쿼리를 수행하기 전에 이미 컴파일이 되어 있고, 반복 수행의 경우 프리 컴파일된 쿼리를 통해 수행이 이뤄지기 때문이다.)
Statement의 경우 보통 변수를 설정하고 바인딩하는 static SQL이 사용되고, PreparedStatement의 경우 쿼리 자체에 조건이 들어가는 dynamic SQL이 사용된다. PreparedStatement가 파싱 타임을 줄여주는 것은 분명하지만, dynamic SQL에 대한 성능 저하를 고려하지 않을 수는 없다.
그러나 성능을 고려할 때 시간 부분에서 가장 큰 비중을 차지하는 것은 테이블에서 row를 가져오는 것이고 SQL 문을 파싱하는 시간은 이 시간의 1/10밖에 되지 않기 때문에 SQL Injection 등의 문제를 보완해주는 PreparedStatement를 사용하는 것이 좋다.