根据我的理解,在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的可重用性中受益?
答案 0 :(得分:3)
您使用PreparedStatementCreator是正确的方法。
总结一下,你所做的是正确的。使用 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);
。如果你想重复使用同一个,那么就不应该创建一个新的。