使用Spring JDBC的PreparedStatementCreator的正确方法是什么?

时间:2013-02-27 13:43:52

标签: spring prepared-statement jdbctemplate spring-jdbc

根据我的理解,在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。如果这是使用PreparedStatementCreator的正确方法,而不是如何从PreparedStatement的可重用性中受益?

3 个答案:

答案 0 :(得分:3)

您使用PreparedStatementCreator是正确的方法。

  1. 在每笔新交易中,您应该创建全新的 PreparedStatement 实例,这绝对是正确的。 PreparedStatementCreator 主要用于包装代码块以轻松创建 PreparedStatement 实例,而不是说您应该重新生成每个itme的新实例。
  2. PreparedStatement主要用于发送模板化和预编译的SQL语句DBMS,这将为SQL执行节省一些预编译时间。
  3. 总结一下,你所做的是正确的。使用 PreparedStatement 的效果会优于 声明

答案 1 :(得分:2)

准备好的语句通常由底层连接池缓存,因此您无需担心每次都创建新的语句。

所以我认为你的实际用法是正确的。

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

}

答案 2 :(得分:1)

  

调试并查看PreparedStatementCreator和JdbcTemplate内部发生的事情后,我知道PreparedStatementCreator每次都会创建新的PreparedStatement ...... !!!

我不确定为什么会这么令人震惊,因为你自己的代码每次都会通过调用PreparedStatement创建一个新的connection.prepareStatement(query);。如果你想重复使用同一个,那么就不应该创建一个新的。