将 PreparedStatement 批处理与 Statement 批处理混合,可以吗?
Posted
技术标签:
【中文标题】将 PreparedStatement 批处理与 Statement 批处理混合,可以吗?【英文标题】:Mixing PreparedStatement batch with Statement batch, is it possible? 【发布时间】:2012-04-25 12:19:04 【问题描述】:是否有可能以某种方式将 PreparedStatement 批处理与 Statement 批处理混合在一起,并在单个事务中执行两者的好处?
我拥有的是我自己的代表事务的数据访问对象。我想这样使用它:
/* here transaction starts: object receive connection, etc. */
MyTableTransactionObject myTable = new MyTableTransactionObject();
myTable.clear();
myTable.insert(Row1);
myTable.insert(Row2);
myTable.insert(Row3);
myTable.doSomethingElse();
myTable.insert(Row4);
/* here transaction ends, batches are executed changes are commited,
statements are closed */
myTable.execute();
myTable.close();
MyTableTransactionObject 的“幕后”有一些方法使用 Statement 或 PreparedStatements(可能有多个 PreparedStatements)。例如:在clear()
方法中我想使用statement.addBatch("DELETE FROM table;")
,在insert(...)
方法中我想使用特殊的PreparedStatement来执行SQL INSERT
操作,在doSomethingElse(...)
我想使用不同的PreparedStatement来做别的事情等等.
我怎样才能实现这些语句的执行,以便它们在myTable.execute()
上被调用?
【问题讨论】:
如果您使用 mysql,请确保在驱动程序级别启用 rewriteBatchedStatements:dev.mysql.com/doc/refman/5.5/en/… 【参考方案1】:这不是最优雅的解决方案,您将付出 %$# 的性能损失,但它确实有效。
public class DBEngine
private final int defaultBatchSize = 1000;
private Pool pool = null;
private Connection con = null;
private PreparedStatement ps = null;
private ArrayList<PreparedStatement> globalBatch = new ArrayList<PreparedStatement>();
private int k = 0; //bean-wide batch counter
private boolean debugMode = false;
private PreparedStatement batchPs = null;
//--------------------------------
DBEngine()
this.pool = new Pool();
this.con = pool.getConnection();
this.ps = null;
this.k = 0; //bean-wide batch counter
//-------------
boolean mixedBatchTime(boolean force)
return mixedBatchTime(defaultBatchSize, force);
//-------------
boolean mixedBatchTime(int customBatchSize)
return mixedBatchTime(customBatchSize, false);
//-------------
boolean mixedBatchTime()
return mixedBatchTime(defaultBatchSize, false);
//-------------
// Executes a mixed batch of PreparedStatements
//-------------
boolean mixedBatchTime(int customBatchSize, boolean force)
if(k > customBatchSize - 1 || force)
try
StringBuilder sqlStmt = new StringBuilder();
for(int i = 0; i < globalBatch.size(); i++)
sqlStmt.append(globalBatch.get(i) + "; ");
batchPs = con.prepareStatement(sqlStmt.toString());
batchPs.execute();
ps = null;
sqlStmt = null;
batchPs = null;
catch (SQLException e)
e.printStackTrace();
k = 0;
globalBatch = null;
globalBatch = new ArrayList<PreparedStatement>();
return true;
else return false;
您必须在在 DBEngine bean 内部的实际批处理准备部分中增加 k,例如:
//------------------------------------------
boolean updateSomeQuantity(int someID, int someQuantity)
try
// detects if the statement has changed in order to recompile it only once per change
if(ps!=null && !ps.toString().contains("UPDATE sometable SET somequantity ="))
ps = null;
String updateStmt = "UPDATE sometable SET somequantity = ? WHERE someID = ?";
ps = con.prepareStatement(updateStmt);
else if(ps == null)
String updateStmt = "UPDATE sometable SET somequantity = ? WHERE someID = ?";
ps = con.prepareStatement(updateStmt);
ps.setInt(1, someQuantity)
ps.setInt(2, someID);
globalBatch.add(ps);
k++; // very important
return true;
catch (SQLException e)
e.printStackTrace();
if(e.getNextException() != null) e.getNextException().printStackTrace();
return false;
实际实现在另一个代码中,您在其中实例化 DBEngine bean 的实例并在循环中使用 updateSomeQuantity(somequantity) 方法和 mixedBatchTime() 方法。循环结束后,调用mixedBatchTime(true) 对剩余的部分进行批处理。
注意:此解决方案使用 AutoCommit(true)
【讨论】:
@jtahlborn:您可以在方法重载中为 bean 引擎指定自定义批量大小。主要问题和性能受到影响的地方是,它必须通过每次更改“主题”时重新准备它们来重用 PreparedStatements。【参考方案2】:如果您的 Statements/PreparedStatements 共享一个公共连接并且您没有在该连接上提交事务,那么它们将共享一个公共事务。
【讨论】:
所以无论我在每个 addBatch() 之后还是在几个 addBatch() 块之后调用 executeBatch() 都没有任何区别(假设 AutoCommit 设置为 false)? 正确,就当前事务而言,这无关紧要。请注意,如果您不那么频繁地调用 executeBatch(),您将获得更好的性能(因为您将减少到数据库的往返次数)。以上是关于将 PreparedStatement 批处理与 Statement 批处理混合,可以吗?的主要内容,如果未能解决你的问题,请参考以下文章
Statement 和 PreparedStatement 的区别
java中PreparedStatement与Statement相比具有啥优势
如何将 Oracle 的 JSON_VALUE 函数与 PreparedStatement 一起使用