11

私の理解によると、JavaでのPreparedStatementの使用は、複数回使用できます。しかし、SpringJDBCの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を使用する正しい方法である場合はどうでしょうか。

4

3 に答える 3

3

あなたはPreparedStatementCreatorを使用する正しい方法にいます。

  1. 新しいトランザクションごとに、まったく新しいPreparedStatementインスタンスを作成する必要があります。これは間違いなく正しいです。PreparedStatementCreatorは、主にコードブロックをラップして、PreparedStatementインスタンスを簡単に作成するように設計されており、各itmeで新しいインスタンスを再起動する必要があるとは言っていません。
  2. PreparedStatementは、主に、テンプレート化およびプリコンパイルされたSQLステートメントDBMSを送信するように設計されています。これにより、SQL実行のためのプリコンパイルされた時間が節約されます。

要約すると、あなたがしたことは正しいです。PreparedStatementを使用すると、 Statementよりもパフォーマンスが向上します。

于 2013-03-05T12:37:23.033 に答える
3

プリペアドステートメントは通常、基盤となる接続プールによってキャッシュされるため、毎回新しいステートメントを作成するかどうかを心配する必要はありません。

ですから、実際の使い方は正しいと思います。

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
        }
    }

}
于 2013-03-08T12:48:31.577 に答える
2

デバッグし、PreparedStatementCreatorとJdbcTemplate内で何が起こるかを調べた後、PreparedStatementCreatorが新しいPreparedStatementを毎回作成することを知りました... !!!

PreparedStatementを呼び出すことによって毎回新しいものを作成するのはあなた自身のコードなので、なぜそれがそれほど衝撃的であるのかわかりませんconnection.prepareStatement(query);。同じものを再利用したい場合は、新しいものを作成しないでください。

于 2013-03-04T05:27:16.733 に答える