PreparedStatementCreator

PreparedStatementCreator

据我了解,在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,prepareStatementSetter,resultSetExtractor)时,它都会创建新的PreparedStatement,这会降低性能。

这是使用PreparedStatementCreator的正确方法吗?因为在这段代码中我无法重用PreparedStatement。如果这是使用PreparedStatementCreator的正确方法,而不是如何从PreparedStatement的可重用性中受益?

最佳答案

预准备语句通常由基础连接池缓存,因此您不必担心是否每次都会创建一个新语句。

因此,我认为您的实际用法是正确的。

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

}

10-08 13:32