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

使用SpringJDBC的PreparedStatementCreator的正确方法是什么?,spring,prepared-statement,jdbctemplate,spring-jdbc,Spring,Prepared Statement,Jdbctemplate,Spring Jdbc,根据我的理解,在Java中使用PreparedStatement是因为我们可以多次使用它。 但是我对使用SpringJDBC的PreparedStatementCreator有些困惑 例如考虑下面的代码, public class SpringTest { JdbcTemplate jdbcTemplate; PreparedStatementCreator preparedStatementCreator; ResultSetExtractor<String

根据我的理解,在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{
jdbc模板jdbc模板;
PreparedStatementCreator PreparedStatementCreator;
结果文本采集器结果文本采集器;
public SpringTest()引发SQLException{
jdbcTemplate=新的jdbcTemplate(OracleUtil.getDataSource());
preparedStatementCreator=新的preparedStatementCreator(){
String query=“从表1中选择名称,其中ID=?”;
公共PreparedStatement createPreparedStatement(连接)引发SQLException{
返回连接。准备声明(查询);
}
};
ResultsTextRactor=新的ResultsTextRactor(){
公共字符串extractData(ResultSet ResultSet)引发SQLException,
DataAccessException{
if(resultSet.next()){
返回resultSet.getString(1);
}
返回null;
}
};
}
公共字符串getNameFromId(int-id){
返回jdbcTemplate.query(preparedStatementCreator、new Table1Setter(id)、ResultsTextRactor);
}
私有静态类Table1Setter实现PreparedStatementSetter{
私有int-id;
公共表1服务器(int id){
this.id=id;
}
@凌驾
public void setValues(PreparedStatement PreparedStatement)引发SQLException{
编制的报表。setInt(1,id);
}
}
公共静态void main(字符串[]args){
试一试{
SpringTest SpringTest=新的SpringTest();
对于(int i=0;i
在调试并查看PreparedStatementCreator和JdbcTemplate内部发生的情况后,我知道PreparedStatementCreator每次都会创建新的PreparedStatement


我不知道为什么这会如此令人震惊,因为每次都是您自己的代码通过调用
connection.prepareStatement(query);
来创建一个新的
PreparedStatement
。如果您想重用同一个语句,那么就不应该创建一个新语句。

您使用PreparedStatementCreator的方法是正确的

  • 在每个新事务中,您都应该创建全新的PreparedStatement实例,这绝对正确。PreparedStatementCreator主要用于包装代码块,以便轻松创建PreparedStatement实例,而不是说您应该在每个itme重新创建新实例
  • PreparedStatement主要用于向DBMS发送模板化和预编译的SQL语句,这将为SQL执行节省一些预编译时间

  • 总之,您所做的是正确的。使用PreparedStatement将比Statement

    性能更好。准备好的语句通常由底层连接池缓存,因此您不必担心是否每次都创建一个新语句

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

    JdbcTemplate在执行语句后关闭该语句,因此如果您真的想重用同一条准备好的语句,可以代理该语句并在语句创建者中截取close方法

    例如(未测试,仅作为示例):


    我还尝试返回相同的PreparedStatement,然后得到错误“Closed Connection”,当我尝试使用相同的连接对象时,得到错误“Closed Statement”如果您在上面的示例中谈到,那可能是因为您正在进行正确的事务管理。在事务进行中,所有操作都将针对相同的连接执行,重用相同的语句应该不是问题。抱歉,上面的输入错误…请确认“您没有进行正确的事务管理”。
    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
            }
        }
    
    }