Reputation: 5645
I have a query like the following and was wondering what kind of SQL is produced by batching a PreparedStatement.
INSERT INTO table1 (id, version, data)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
table1.data = IF(table1.version > table2.version, table1.data, table2.data),
table1.version = IF(table1.version > table2.version, table1.version, table2.version)
The question is, will it resolve this to a copy of this whole sql string for each row in the batch or will it do something like:
INSERT INTO table1 (id, version, data)
VALUES (a1, b1, c1), (a2, b2, c2), (a3, b3, c3), ...
ON DUPLICATE KEY UPDATE
table1.data = IF(table1.version > table2.version, table1.data, table2.data),
table1.version = IF(table1.version > table2.version, table1.version, table2.version)
If not, what is the performance implication and how do I write it in such a way that I can batch many of these INSERT..UPDATE statements using PreparedStatement without incurring a performance penalty?
Upvotes: 1
Views: 1694
Reputation: 57274
A prepared statement just inserts the positional values you put in into a repeating statement which then doesn't need to be parsed each time. So your second form would just require N * 3 parameters and wouldn't give you any of the speed improvement of a prepared statement. For repeating statements you want to use addTobatch. Basically you prepare the statement, (e.g. "UPDATE ... ? ? ? " and then add 3 parameters at a time, and execute the batch all at once.
I used to use something like this as a utility to wrap the messiness of this. So you'd just do something like
SQLBatchHandler h = new SQLBatchHandler(conn, "UPDATE ... WHERE ? ? ? ... ");
h.addToBatch(x, y,z);
h.addToBatch(x2,y2,z2);
...
h.flush();
public class SQLBatchHandler {
public static int MAX_BATCH_SIZE = 500;
public String query;
private Connection conn;
private PreparedStatement ps;
private int batch_ct;
public SQLBatchHandler(Connection c, String query) throws SQLException
{
conn = c;
this.query = query;
ps = conn.prepareStatement(query);
}
/**
* add this row to the batch and handle the commit if the batch size
* exceeds {@link #MAX_BATCH_SIZE}
*
* @param values row values
* @throws SQLException
*/
public void addToBatch(Object ... values) throws SQLException
{
int i = 0;
for (Object value: values)
{
ps.setObject((++i), value);
}
add();
}
private void add() throws SQLException
{
ps.addBatch();
if ((++batch_ct) > MAX_BATCH_SIZE)
{
ps.executeBatch();
batch_ct = 0;
}
}
/**
* Commit any remaining objects and close.
*
* @throws SQLException On statement close error.
*/
public void flush() throws SQLException
{
if (batch_ct == 0) { return; }
try
{
ps.executeBatch();
}
catch (SQLException e)
{
throw e;
}
finally
{
if (ps != null)
{
ps.close();
}
}
}
}
Upvotes: 2