如何解决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 举报,一经查实,本站将立刻删除。