Java Spring5JDBCTemplate无法重用PreparedStatement?
SQL的一个简单优化是重用准备好的语句。您需要一次解析成本,然后可以在循环中重用Java Spring5JDBCTemplate无法重用PreparedStatement?,java,spring,jdbc,prepared-statement,spring-jdbc,Java,Spring,Jdbc,Prepared Statement,Spring Jdbc,SQL的一个简单优化是重用准备好的语句。您需要一次解析成本,然后可以在循环中重用PreparedStatement对象,只需根据需要更改参数。这在许多其他地方都有明确的记录 Spring5在使用JdbcTemplate时,似乎不可能做到这一点。所有处理PreparedStatementCreators的JdbcTemplate查询和更新方法都会向下扩展到一个execute方法。下面是该方法的全部代码 public <T> T execute(PreparedStatementCrea
PreparedStatement
对象,只需根据需要更改参数。这在许多其他地方都有明确的记录
Spring5在使用JdbcTemplate
时,似乎不可能做到这一点。所有处理PreparedStatementCreator
s的JdbcTemplate
查询和更新方法都会向下扩展到一个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());
}
}
这使得使用JdbcTemplate重用准备好的语句完全不可能
我已经有很长一段时间(5年)没有机会使用SpringJDBC了,但我不记得这是一个问题。我在一个大型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);
}
);
private static final String sqlGetPDFFile=“从PDFFile中选择id、根目录、文件路径、文件时间、文件大小,其中摘要=?”;
私人编制的PSGetPffile报表;
@自动连线
public void setDataSource(DataSource DataSource)引发SQLException
{
Connection con=dataSource.getConnection();
psGetPdfile=con.prepareStatement(sqlGetPdfile);
this.tmpl=新的JdbcTemplate(数据源);
}
...
...
列表文件=
tmpl.query(
//PreparedStatementCreator
c->{
psGetPdfile.setBytes(1,文件摘要);
返回psGetPDFFile;
},
//行映射器
(rs,n)->
{
long id=rs.getLong(1);
Path rootDir=Path.get(rs.getString(2));
Path filePath=Path.get(rs.getString(3));
FileTime FileTime=FileTime.from(rs.getTimestamp(4.toInstant());
long fileSize=rs.getLong(5);
返回新的PDFFile(id、fileDigest、rootDir、filePath、fileTime、fileSize);
}
);
当然,由于硬编码语句close调用,第二次失败
问题:假设我想继续使用SpringJDBC,那么重用准备好的语句的正确方法是什么
另外,如果有人知道Spring为什么这样做(也就是说,这有一个很好的理由),我想知道。当然可以,它被称为批处理,您可以通过调用
JdbcTemplate
上的batchUpdate()
方法之一来实现。在网上搜索,你会发现无数的例子。这并不是我想要的。这不是一个大批量的问题,这是在一个有UI的程序中。你的意思是,在非批处理场景中,准备语句的成本不足以让人担心吗?正确。一些JDBC驱动程序将缓存准备好的SQL语句以供重用,因此当您重新准备SQL时,它不必再次将其发送到数据库服务器进行解析。例如,具有连接属性prepareThreshold
(默认值:5)、preparedStatementCacheQueries
(默认值:256)和preparedStatementCacheSizeMiB
(默认值:5)。第一个“确定切换到使用服务器端准备语句之前所需的PreparedStatement
执行次数”。PreparedStatement的不重用对于JdbcTemplate来说并不是唯一的。最可能的原因是必须实现PreparedStatement以覆盖绑定变量,但重用将引入游标泄漏的可能问题(即打开的游标太多)。此外,这种JDBC框架仍然比ORM“更好”,比普通JDBC“更差”(由于光标缓存),没有太大的改变压力。@MarmiteBomber这似乎是最好的答案,请将其作为答案而不是评论发布。是的,我发现JdbcTemplate
在易用性和功能性之间取得了正确的平衡。ORM太重了,而普通的JDBC是个麻烦:-)
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);
}
);