Added batchUpdate method taking a Collection, a batch size and a ParameterizedPreparedStatementSetter as arguments (SPR-6334)
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.jdbc.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1004,7 +1005,19 @@ public interface JdbcOperations {
|
||||
* @return an array containing the numbers of rows affected by each update in the batch
|
||||
*/
|
||||
public int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);
|
||||
|
||||
|
||||
/**
|
||||
* Execute multiple batches using the supplied SQL statement with the collect of supplied arguments.
|
||||
* The arguments' values will be set using the ParameterizedPreparedStatementSetter.
|
||||
* Each batch should be of size indicated in 'batchSize'.
|
||||
* @param sql the SQL statement to execute.
|
||||
* @param batchArgs the List of Object arrays containing the batch of arguments for the query
|
||||
* @param argTypes SQL types of the arguments
|
||||
* (constants from <code>java.sql.Types</code>)
|
||||
* @return an array containing for each batch another array containing the numbers of rows affected
|
||||
* by each update in the batch
|
||||
*/
|
||||
public <T> int[][] batchUpdate(String sql, Collection<T> batchArgs, int batchSize, ParameterizedPreparedStatementSetter<T> pss);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Methods dealing with callable statements
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -929,7 +930,59 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
|
||||
public int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) {
|
||||
return BatchUpdateUtils.executeBatchUpdate(sql, batchArgs, argTypes, this);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.jdbc.core.JdbcOperations#batchUpdate(java.lang.String, java.util.Collection, int, org.springframework.jdbc.core.ParameterizedPreparedStatementSetter)
|
||||
*
|
||||
* Contribution by Nicolas Fabre
|
||||
*/
|
||||
public <T> int[][] batchUpdate(String sql, final Collection<T> batchArgs, final int batchSize, final ParameterizedPreparedStatementSetter<T> pss) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing SQL batch update [" + sql + "] with a batch size of " + batchSize);
|
||||
}
|
||||
return execute(sql, new PreparedStatementCallback<int[][]>() {
|
||||
public int[][] doInPreparedStatement(PreparedStatement ps) throws SQLException {
|
||||
List<int[]> rowsAffected = new ArrayList<int[]>();
|
||||
try {
|
||||
boolean batchSupported = true;
|
||||
if (!JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
|
||||
batchSupported = false;
|
||||
logger.warn("JDBC Driver does not support Batch updates; resorting to single statement execution");
|
||||
}
|
||||
int n = 0;
|
||||
for (T obj : batchArgs) {
|
||||
pss.setValues(ps, obj);
|
||||
n++;
|
||||
if (batchSupported) {
|
||||
ps.addBatch();
|
||||
if (n % batchSize == 0 || n == batchArgs.size()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
int batchIdx = (n % batchSize == 0) ? n / batchSize : (n / batchSize) + 1;
|
||||
int items = n - ((n % batchSize == 0) ? n / batchSize - 1 : (n / batchSize)) * batchSize;
|
||||
logger.debug("Sending SQL batch update #" + batchIdx + " with " + items + " items");
|
||||
}
|
||||
rowsAffected.add(ps.executeBatch());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i = ps.executeUpdate();
|
||||
rowsAffected.add(new int[] {i});
|
||||
}
|
||||
}
|
||||
int[][] result = new int[rowsAffected.size()][];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = rowsAffected.get(i);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
if (pss instanceof ParameterDisposer) {
|
||||
((ParameterDisposer) pss).cleanupParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Methods dealing with callable statements
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jdbc.core;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Parameterized callback interface used by the {@link JdbcTemplate} class for batch updates.
|
||||
*
|
||||
* <p>This interface sets values on a {@link java.sql.PreparedStatement} provided
|
||||
* by the JdbcTemplate class, for each of a number of updates in a batch using the
|
||||
* same SQL. Implementations are responsible for setting any necessary parameters.
|
||||
* SQL with placeholders will already have been supplied.
|
||||
*
|
||||
* <p>Implementations <i>do not</i> need to concern themselves with SQLExceptions
|
||||
* that may be thrown from operations they attempt. The JdbcTemplate class will
|
||||
* catch and handle SQLExceptions appropriately.
|
||||
*
|
||||
* @author Nicolas Fabre
|
||||
* @author Thomas Risberg
|
||||
* @since 3.1
|
||||
* @see JdbcTemplate#batchUpdate(String sql, Collection<T> objs, int batchSize, ParameterizedPreparedStatementSetter<T> pss)
|
||||
*/
|
||||
public interface ParameterizedPreparedStatementSetter<T> {
|
||||
|
||||
/**
|
||||
* Set parameter values on the given PreparedStatement.
|
||||
*
|
||||
* @param ps the PreparedStatement to invoke setter methods on
|
||||
* @param argument the object containing the values to be set
|
||||
* @throws SQLException if a SQLException is encountered (i.e. there is no need to catch SQLException)
|
||||
*/
|
||||
void setValues(PreparedStatement ps, T argument) throws SQLException;
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import java.sql.SQLWarning;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -1189,6 +1190,76 @@ public class JdbcTemplateTests extends AbstractJdbcTests {
|
||||
BatchUpdateTestHelper.verifyBatchUpdateMocks(ctrlPreparedStatement, ctrlDatabaseMetaData);
|
||||
}
|
||||
|
||||
public void testBatchUpdateWithCollectionOfObjects() throws Exception {
|
||||
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
|
||||
final List<Integer> ids = new ArrayList<Integer>();
|
||||
ids.add(Integer.valueOf(100));
|
||||
ids.add(Integer.valueOf(200));
|
||||
ids.add(Integer.valueOf(300));
|
||||
final int[] rowsAffected1 = new int[] { 1, 2 };
|
||||
final int[] rowsAffected2 = new int[] { 3 };
|
||||
|
||||
MockControl ctrlPreparedStatement = MockControl.createControl(PreparedStatement.class);
|
||||
PreparedStatement mockPreparedStatement = (PreparedStatement) ctrlPreparedStatement.getMock();
|
||||
mockPreparedStatement.getConnection();
|
||||
ctrlPreparedStatement.setReturnValue(mockConnection);
|
||||
mockPreparedStatement.setInt(1, ids.get(0));
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
mockPreparedStatement.addBatch();
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
mockPreparedStatement.setInt(1, ids.get(1));
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
mockPreparedStatement.addBatch();
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
mockPreparedStatement.setInt(1, ids.get(2));
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
mockPreparedStatement.executeBatch();
|
||||
ctrlPreparedStatement.setReturnValue(rowsAffected1);
|
||||
mockPreparedStatement.addBatch();
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
mockPreparedStatement.executeBatch();
|
||||
ctrlPreparedStatement.setReturnValue(rowsAffected2);
|
||||
if (debugEnabled) {
|
||||
mockPreparedStatement.getWarnings();
|
||||
ctrlPreparedStatement.setReturnValue(null);
|
||||
}
|
||||
mockPreparedStatement.close();
|
||||
ctrlPreparedStatement.setVoidCallable();
|
||||
|
||||
MockControl ctrlDatabaseMetaData = MockControl.createControl(DatabaseMetaData.class);
|
||||
DatabaseMetaData mockDatabaseMetaData = (DatabaseMetaData) ctrlDatabaseMetaData.getMock();
|
||||
mockDatabaseMetaData.getDatabaseProductName();
|
||||
ctrlDatabaseMetaData.setReturnValue("MySQL");
|
||||
mockDatabaseMetaData.supportsBatchUpdates();
|
||||
ctrlDatabaseMetaData.setReturnValue(true);
|
||||
|
||||
mockConnection.prepareStatement(sql);
|
||||
ctrlConnection.setReturnValue(mockPreparedStatement);
|
||||
mockConnection.getMetaData();
|
||||
ctrlConnection.setReturnValue(mockDatabaseMetaData, 2);
|
||||
|
||||
ctrlPreparedStatement.replay();
|
||||
ctrlDatabaseMetaData.replay();
|
||||
replay();
|
||||
|
||||
ParameterizedPreparedStatementSetter setter = new ParameterizedPreparedStatementSetter<Integer>() {
|
||||
public void setValues(PreparedStatement ps, Integer argument) throws SQLException {
|
||||
ps.setInt(1, argument.intValue());
|
||||
}
|
||||
};
|
||||
|
||||
JdbcTemplate template = new JdbcTemplate(mockDataSource, false);
|
||||
|
||||
int[][] actualRowsAffected = template.batchUpdate(sql, ids, 2, setter);
|
||||
assertTrue("executed 2 updates", actualRowsAffected[0].length == 2);
|
||||
assertEquals(rowsAffected1[0], actualRowsAffected[0][0]);
|
||||
assertEquals(rowsAffected1[1], actualRowsAffected[0][1]);
|
||||
assertEquals(rowsAffected2[0], actualRowsAffected[1][0]);
|
||||
|
||||
ctrlPreparedStatement.verify();
|
||||
ctrlDatabaseMetaData.verify();
|
||||
}
|
||||
|
||||
public void testCouldntGetConnectionOrExceptionTranslator() throws SQLException {
|
||||
SQLException sex = new SQLException("foo", "07xxx");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user