Spring 5 JdbcTemplate不能重用PreparedStatement吗?

问题描述

sql一个简单优化是重用已准备好的语句。您只需花费一次解析费用,然后就可以在循环内重用PreparedStatement对象,只需根据需要更改参数即可。这在https://developer.paypal.com/docs/checkout/integration-features/pay-another-account/和许多其他地方都有明确的记录。

Spring 5在使用JdbcTemplate时似乎无法实现。处理JdbcTemplate的程序的所有PreparedStatementCreator查询和更新方法都会降级为一个execute方法。这是该方法的全部代码

public <T> T execute(PreparedStatementCreator psc,PreparedStatementCallback<T> action)
        throws DataAccessException {

    Assert.notNull(psc,"PreparedStatementCreator must not be null");
    Assert.notNull(action,"Callback object must not be null");
    if (logger.isDebugEnabled()) {
        String sql = getsql(psc);
        logger.debug("Executing prepared sql statement" + (sql != null ? " [" + sql + "]" : ""));
    }

    Connection con = DataSourceUtils.getConnection(obtainDataSource());
    PreparedStatement ps = null;
    try {
        ps = psc.createPreparedStatement(con);
        applyStatementSettings(ps);
        T result = action.doInPreparedStatement(ps);
        handleWarnings(ps);
        return result;
    }
    catch (sqlException ex) {
        // Release Connection early,to avoid potential connection pool deadlock
        // in the case when the exception translator hasn't been initialized yet.
        if (psc instanceof Parameterdisposer) {
            ((Parameterdisposer) psc).cleanupParameters();
        }
        String sql = getsql(psc);
        psc = null;
        JdbcUtils.closeStatement(ps);
        ps = null;
        DataSourceUtils.releaseConnection(con,getDataSource());
        con = null;
        throw translateException("PreparedStatementCallback",sql,ex);
    }
    finally {
        if (psc instanceof Parameterdisposer) {
            ((Parameterdisposer) psc).cleanupParameters();
        }
        JdbcUtils.closeStatement(ps);
        DataSourceUtils.releaseConnection(con,getDataSource());
    }
}

“有趣”位在finally块中:

        JdbcUtils.closeStatement(ps);

这使得使用JdbcTemplate重用已准备好的语句完全不可能。

距离我有机会使用Spring JDBC已有很长时间(5年),但是我不记得这曾经是一个问题。我在大型sql后端上处理了数百个准备好的语句,我清楚地记得不必为每次执行都重新准备它们。

我想做的是这个

private static final String sqlGetPDFFile = "select id,root_dir,file_path,file_time,file_size from PDFFile where digest=?";
private PreparedStatement psGetPDFFile;

@Autowired
public void setDataSource(DataSource dataSource) throws sqlException
{
    Connection con = dataSource.getConnection();
    psGetPDFFile = con.prepareStatement(sqlGetPDFFile);
    this.tmpl = new JdbcTemplate(dataSource);
}
...
...
    List<PDFFile> files = 
        tmpl.query(
            
            // PreparedStatementCreator
            c -> { 
                psGetPDFFile.setBytes(1,fileDigest); 
                return psGetPDFFile; 
            },// RowMapper
            (rs,n)-> 
            {
                long        id          = rs.getLong(1);
                Path        rootDir     = Paths.get(rs.getString(2));
                Path        filePath    = Paths.get(rs.getString(3));
                FileTime    fileTime    = FileTime.from(rs.getTimestamp(4).toInstant());
                long        fileSize    = rs.getLong(5);
                return new PDFFile(id,fileDigest,rootDir,filePath,fileTime,fileSize);
            }
            );

但是当然,由于硬编码的语句close调用,第二次失败。

问题:假设我想继续使用Spring JDBC,重用已准备好的语句的正确方法是什么?

此外,如果有人知道Spring为什么要这样做(即有充分的理由),我想知道。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)