私の理解によると、JavaでPreparedStatementを使用すると、複数回使用できます。しかし、Spring JDBCのPreparedStatementCreatorを使用すると、混乱が生じます。
たとえば、次のコードを検討してください。
public class SpringTest {
JdbcTemplate jdbcTemplate;
PreparedStatementCreator preparedStatementCreator;
ResultSetExtractor<String> resultSetExtractor;
public SpringTest() throws SQLException {
jdbcTemplate = new JdbcTemplate(OracleUtil.getDataSource());
preparedStatementCreator = new PreparedStatementCreator() {
String query = "select NAME from TABLE1 where ID=?";
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
return connection.prepareStatement(query);
}
};
resultSetExtractor = new ResultSetExtractor<String>() {
public String extractData(ResultSet resultSet) throws SQLException,
DataAccessException {
if (resultSet.next()) {
return resultSet.getString(1);
}
return null;
}
};
}
public String getNameFromId(int id){
return jdbcTemplate.query(preparedStatementCreator, new Table1Setter(id), resultSetExtractor);
}
private static class Table1Setter implements PreparedStatementSetter{
private int id;
public Table1Setter(int id) {
this.id =id;
}
@Override
public void setValues(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setInt(1, id);
}
}
public static void main(String[] args) {
try {
SpringTest springTest = new SpringTest();
for(int i=0;i<10;i++){
System.out.println(springTest.getNameFromId(i));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
このコードに従って、springTest.getNameFromId(int id)メソッドを呼び出すと、指定されたIDから名前が返されます。ここでは、PreparedStatementCreatorを使用してPreparedStatementを作成し、PreparedStatementSetterを使用して入力パラメーターを設定し、ResultSetExtractorから結果を取得しました。しかし、パフォーマンスは非常に遅いです。
PreparedStatementCreatorとJdbcTemplateの内部で何が起こっているかをデバッグして調べた後、PreparedStatementCreatorが新しいPreparedStatementを毎回作成することを知りました... !!!
メソッドjdbcTemplate.query(preparedStatementCreator、preparedStatementSetter、resultSetExtractor)を呼び出すたびに、新しいPreparedStatementが作成され、パフォーマンスが低下します。
これはPreparedStatementCreatorを使用する正しい方法ですか?このコードでは、PreparedStatementを再利用できないためです。そして、これがPreparedStatementの再利用性のメリットを享受する方法よりも、PreparedStatementCreatorを使用する正しい方法である場合はどうでしょうか。
プリペアドステートメントは通常、基盤となる接続プールによってキャッシュされるため、毎回新しいステートメントを作成するかどうかを心配する必要はありません。
ですから、実際の使い方は正しいと思います。
JdbcTemplateは、実行後にステートメントを閉じるため、同じプリペアドステートメントを本当に再利用したい場合は、ステートメントをプロキシして、ステートメント作成者のcloseメソッドをインターセプトできます。
例(テストされていません。例としてのみ):
public abstract class ReusablePreparedStatementCreator implements PreparedStatementCreator {
private PreparedStatement statement;
public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
if (statement != null)
return statement;
PreparedStatement ps = doPreparedStatement(conn);
ProxyFactory pf = new ProxyFactory(ps);
MethodInterceptor closeMethodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return null; // don't close statement
}
};
NameMatchMethodPointcutAdvisor closeAdvisor = new NameMatchMethodPointcutAdvisor();
closeAdvisor.setMappedName("close");
closeAdvisor.setAdvice(closeMethodInterceptor);
pf.addAdvisor(closeAdvisor);
statement = (PreparedStatement) pf.getProxy();
return statement;
}
public abstract PreparedStatement doPreparedStatement(Connection conn) throws SQLException;
public void close() {
try {
PreparedStatement ps = (PreparedStatement) ((Advised) statement).getTargetSource().getTarget();
ps.close();
} catch (Exception e) {
// handle exception
}
}
}
あなたはPreparedStatementCreatorを使用する正しい方法にいます。
要約すると、あなたがしたことは正しいです。 PreparedStatementを使用すると、Statement。
PreparedStatementCreatorとJdbcTemplateの内部で何が起こっているかをデバッグして調べた後、PreparedStatementCreatorが新しいPreparedStatementを毎回作成することを知りました... !!!
connection.prepareStatement(query);
を呼び出すたびに新しいPreparedStatement
を作成するのは独自のコードであるため、なぜそれがそれほど衝撃的であるのかわかりません。同じものを再利用したい場合は、新しいものを作成しないでください。