diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
index ac54080f42..560392e969 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
@@ -29,7 +29,9 @@ import org.springframework.lang.Nullable;
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0
+ * @deprecated as of 5.1.3, not used by {@link JdbcTemplate} anymore
*/
+@Deprecated
public abstract class BatchUpdateUtils {
public static int[] executeBatchUpdate(
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/CallableStatementCreatorFactory.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/CallableStatementCreatorFactory.java
index d0db3af3ae..4ad44f3e60 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/CallableStatementCreatorFactory.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/CallableStatementCreatorFactory.java
@@ -71,6 +71,14 @@ public class CallableStatementCreatorFactory {
}
+ /**
+ * Return the SQL call string.
+ * @since 5.1.3
+ */
+ public final String getCallString() {
+ return this.callString;
+ }
+
/**
* Add a new declared parameter.
*
Order of parameter addition is significant.
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
index 2a21d16b37..5e35f621b7 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
@@ -933,6 +933,7 @@ public interface JdbcOperations {
* @param pss the ParameterizedPreparedStatementSetter to use
* @return an array containing for each batch another array containing the numbers of rows affected
* by each update in the batch
+ * @since 3.1
*/
int[][] batchUpdate(String sql, Collection batchArgs, int batchSize,
ParameterizedPreparedStatementSetter pss) throws DataAccessException;
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
index 6cdd002c9d..1c454db555 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
@@ -982,8 +982,41 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
}
@Override
- public int[] batchUpdate(String sql, List batchArgs, int[] argTypes) throws DataAccessException {
- return BatchUpdateUtils.executeBatchUpdate(sql, batchArgs, argTypes, this);
+ public int[] batchUpdate(String sql, List batchArgs, final int[] argTypes) throws DataAccessException {
+ if (batchArgs.isEmpty()) {
+ return new int[0];
+ }
+
+ return batchUpdate(
+ sql,
+ new BatchPreparedStatementSetter() {
+ @Override
+ public void setValues(PreparedStatement ps, int i) throws SQLException {
+ Object[] values = batchArgs.get(i);
+ int colIndex = 0;
+ for (Object value : values) {
+ colIndex++;
+ if (value instanceof SqlParameterValue) {
+ SqlParameterValue paramValue = (SqlParameterValue) value;
+ StatementCreatorUtils.setParameterValue(ps, colIndex, paramValue, paramValue.getValue());
+ }
+ else {
+ int colType;
+ if (argTypes.length < colIndex) {
+ colType = SqlTypeValue.TYPE_UNKNOWN;
+ }
+ else {
+ colType = argTypes[colIndex - 1];
+ }
+ StatementCreatorUtils.setParameterValue(ps, colIndex, colType, value);
+ }
+ }
+ }
+ @Override
+ public int getBatchSize() {
+ return batchArgs.size();
+ }
+ });
}
@Override
@@ -996,11 +1029,7 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
int[][] result = execute(sql, (PreparedStatementCallback) ps -> {
List rowsAffected = new ArrayList<>();
try {
- boolean batchSupported = true;
- if (!JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
- batchSupported = false;
- logger.debug("JDBC Driver does not support Batch updates; resorting to single statement execution");
- }
+ boolean batchSupported = JdbcUtils.supportsBatchUpdates(ps.getConnection());
int n = 0;
for (T obj : batchArgs) {
pss.setValues(ps, obj);
@@ -1038,6 +1067,7 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
return result;
}
+
//-------------------------------------------------------------------------
// Methods dealing with callable statements
//-------------------------------------------------------------------------
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java
index 5ce48f5a61..2fb4b84051 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java
@@ -91,6 +91,14 @@ public class PreparedStatementCreatorFactory {
}
+ /**
+ * Return the SQL statement to execute.
+ * @since 5.1.3
+ */
+ public final String getSql() {
+ return this.sql;
+ }
+
/**
* Add a new declared parameter.
* Order of parameter addition is significant.
@@ -196,7 +204,7 @@ public class PreparedStatementCreatorFactory {
Assert.notNull(parameters, "Parameters List must not be null");
this.parameters = parameters;
if (this.parameters.size() != declaredParameters.size()) {
- // account for named parameters being used multiple times
+ // Account for named parameters being used multiple times
Set names = new HashSet<>();
for (int i = 0; i < parameters.size(); i++) {
Object param = parameters.get(i);
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
index 1720f26579..8dbceccd26 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
@@ -20,7 +20,6 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
-import org.springframework.jdbc.core.BatchUpdateUtils;
import org.springframework.jdbc.core.JdbcOperations;
/**
@@ -30,8 +29,10 @@ import org.springframework.jdbc.core.JdbcOperations;
* @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0
+ * @deprecated as of 5.1.3, not used by {@link NamedParameterJdbcTemplate} anymore
*/
-public abstract class NamedParameterBatchUpdateUtils extends BatchUpdateUtils {
+@Deprecated
+public abstract class NamedParameterBatchUpdateUtils extends org.springframework.jdbc.core.BatchUpdateUtils {
public static int[] executeBatchUpdateWithNamedParameters(
final ParsedSql parsedSql, final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java
index 288e97f92e..eba270006c 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java
@@ -16,6 +16,8 @@
package org.springframework.jdbc.core.namedparam;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -24,6 +26,7 @@ import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
+import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -352,8 +355,26 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
@Override
public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs) {
- return NamedParameterBatchUpdateUtils.executeBatchUpdateWithNamedParameters(
- getParsedSql(sql), batchArgs, getJdbcOperations());
+ if (batchArgs.length == 0) {
+ return new int[0];
+ }
+
+ ParsedSql parsedSql = getParsedSql(sql);
+ PreparedStatementCreatorFactory pscf = getPreparedStatementCreatorFactory(parsedSql, batchArgs[0]);
+
+ return getJdbcOperations().batchUpdate(
+ pscf.getSql(),
+ new BatchPreparedStatementSetter() {
+ @Override
+ public void setValues(PreparedStatement ps, int i) throws SQLException {
+ Object[] values = NamedParameterUtils.buildValueArray(parsedSql, batchArgs[i], null);
+ pscf.newPreparedStatementSetter(values).setValues(ps);
+ }
+ @Override
+ public int getBatchSize() {
+ return batchArgs.length;
+ }
+ });
}
@@ -389,9 +410,7 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
@Nullable Consumer customizer) {
ParsedSql parsedSql = getParsedSql(sql);
- String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
- List declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
- PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
+ PreparedStatementCreatorFactory pscf = getPreparedStatementCreatorFactory(parsedSql, paramSource);
if (customizer != null) {
customizer.accept(pscf);
}
@@ -419,4 +438,21 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
}
}
+ /**
+ * Build a {@link PreparedStatementCreatorFactory} based on the given SQL and named parameters.
+ * @param parsedSql parsed representation of the given SQL statement
+ * @param paramSource container of arguments to bind
+ * @return the corresponding {@link PreparedStatementCreatorFactory}
+ * @since 5.1.3
+ * @see #getPreparedStatementCreator(String, SqlParameterSource, Consumer)
+ * @see #getParsedSql(String)
+ */
+ protected PreparedStatementCreatorFactory getPreparedStatementCreatorFactory(
+ ParsedSql parsedSql, SqlParameterSource paramSource) {
+
+ String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
+ List declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
+ return new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
+ }
+
}
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
index dc3a0dab60..6d30d9980e 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
@@ -35,6 +35,7 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.InOrder;
import org.springframework.jdbc.Customer;
import org.springframework.jdbc.core.JdbcOperations;
@@ -50,6 +51,7 @@ import static org.mockito.BDDMockito.*;
* @author Juergen Hoeller
* @author Chris Beams
* @author Nikita Khateev
+ * @author Fedor Bobin
*/
public class NamedParameterJdbcTemplateTests {
@@ -468,6 +470,41 @@ public class NamedParameterJdbcTemplateTests {
verify(connection, atLeastOnce()).close();
}
+ @Test
+ public void testBatchUpdateWithInClause() throws Exception {
+ @SuppressWarnings("unchecked")
+ Map[] parameters = new Map[2];
+ parameters[0] = Collections.singletonMap("ids", Arrays.asList(1, 2));
+ parameters[1] = Collections.singletonMap("ids", Arrays.asList(3, 4));
+
+ final int[] rowsAffected = new int[] {1, 2};
+ given(preparedStatement.executeBatch()).willReturn(rowsAffected);
+ given(connection.getMetaData()).willReturn(databaseMetaData);
+
+ JdbcTemplate template = new JdbcTemplate(dataSource, false);
+ namedParameterTemplate = new NamedParameterJdbcTemplate(template);
+
+ int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
+ "delete sometable where id in (:ids)",
+ parameters
+ );
+
+ assertEquals("executed 2 updates", 2, actualRowsAffected.length);
+
+ InOrder inOrder = inOrder(preparedStatement);
+
+ inOrder.verify(preparedStatement).setObject(1, 1);
+ inOrder.verify(preparedStatement).setObject(2, 2);
+ inOrder.verify(preparedStatement).addBatch();
+
+ inOrder.verify(preparedStatement).setObject(1, 3);
+ inOrder.verify(preparedStatement).setObject(2, 4);
+ inOrder.verify(preparedStatement).addBatch();
+
+ inOrder.verify(preparedStatement, atLeastOnce()).close();
+ verify(connection, atLeastOnce()).close();
+ }
+
@Test
public void testBatchUpdateWithSqlParameterSourcePlusTypeInfo() throws Exception {
SqlParameterSource[] ids = new SqlParameterSource[2];