Fedor Bobin opened SPR-17402 and commented
It will be nice to allow do batch queries with list in IN clause. Now it is not supported because query is expanded but NamedParameterBatchUpdateUtils does not aware of it.
Issue Links: - #22008 NamedParameterJdbcTemplate batchUpdate returns an array of size 1 when the batchArgs passed is an empty array
Referenced from: pull request https://github.com/spring-projects/spring-framework/pull/1997, and commits https://github.com/spring-projects/spring-framework/commit/a3d763d137f954e86378dc2fdce31f06a8cfc0e3
2 votes, 4 watchers
Comment From: spring-projects-issues
Fedor Bobin commented
I have a fix. I will not have access to GitHub till Monday. Then I will make a PR with this patch:
Index: spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java (revision aa656c47b87b75f5bf28f864c28d8a220ac25fbc)
+++ spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java (revision 475e623883a6f07419242b54d61a8f6758b9fe7a)
@@ -176,6 +176,9 @@
sqlToUse, params != null ? Arrays.asList(params) : Collections.emptyList());
}
+ public String getSql() {
+ return sql;
+ }
/**
* PreparedStatementCreator implementation returned by this class.
Index: spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java (revision aa656c47b87b75f5bf28f864c28d8a220ac25fbc)
+++ spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java (revision 475e623883a6f07419242b54d61a8f6758b9fe7a)
@@ -22,6 +22,8 @@
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.BatchUpdateUtils;
import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
+import org.springframework.jdbc.core.PreparedStatementSetter;
/**
* Generic utility methods for working with JDBC batch statements using named parameters.
@@ -32,22 +34,17 @@
*/
public class NamedParameterBatchUpdateUtils extends BatchUpdateUtils {
- public static int[] executeBatchUpdateWithNamedParameters(final ParsedSql parsedSql,
- final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
+ public static int[] executeBatchUpdateWithNamedParameters(ParsedSql parsedSql, final PreparedStatementCreatorFactory pscf,
+ final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
- if (batchArgs.length <= 0) {
- return new int[] {0};
- }
-
- String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, batchArgs[0]);
return jdbcOperations.batchUpdate(
- sqlToUse,
+ pscf.getSql(),
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = NamedParameterUtils.buildValueArray(parsedSql, batchArgs[i], null);
- int[] columnTypes = NamedParameterUtils.buildSqlTypeArray(parsedSql, batchArgs[i]);
- setStatementParameters(values, ps, columnTypes);
+ PreparedStatementSetter preparedStatementSetter = pscf.newPreparedStatementSetter(values);
+ preparedStatementSetter.setValues(ps);
}
@Override
public int getBatchSize() {
Index: spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java (revision aa656c47b87b75f5bf28f864c28d8a220ac25fbc)
+++ spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java (revision 475e623883a6f07419242b54d61a8f6758b9fe7a)
@@ -352,8 +352,13 @@
@Override
public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs) {
+ if (batchArgs.length <= 0) {
+ return new int[] {0};
+ }
+ ParsedSql parsedSql = getParsedSql(sql);
+ PreparedStatementCreatorFactory pscf = getPreparedStatementCreatorFactory(parsedSql, batchArgs[0], null);
return NamedParameterBatchUpdateUtils.executeBatchUpdateWithNamedParameters(
- getParsedSql(sql), batchArgs, getJdbcOperations());
+ parsedSql, pscf, batchArgs, getJdbcOperations());
}
@@ -389,14 +394,20 @@
@Nullable Consumer<PreparedStatementCreatorFactory> customizer) {
ParsedSql parsedSql = getParsedSql(sql);
+ PreparedStatementCreatorFactory pscf = getPreparedStatementCreatorFactory(parsedSql, paramSource, customizer);
+ Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
+ return pscf.newPreparedStatementCreator(params);
+ }
+
+ protected PreparedStatementCreatorFactory getPreparedStatementCreatorFactory(ParsedSql parsedSql, SqlParameterSource paramSource,
+ @Nullable Consumer<PreparedStatementCreatorFactory> customizer){
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
List<SqlParameter> declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
if (customizer != null) {
customizer.accept(pscf);
}
- Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
- return pscf.newPreparedStatementCreator(params);
+ return pscf;
}
/**
Index: spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java (revision aa656c47b87b75f5bf28f864c28d8a220ac25fbc)
+++ spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java (revision 475e623883a6f07419242b54d61a8f6758b9fe7a)
@@ -36,6 +36,7 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.InOrder;
import org.springframework.jdbc.Customer;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -460,6 +461,41 @@
verify(preparedStatement, atLeastOnce()).close();
verify(connection, atLeastOnce()).close();
}
+
+ @Test
+ public void testBatchUpdateWithInClause() throws Exception {
+ @SuppressWarnings("unchecked")
+ Map<String, Object>[] 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 {
Comment From: spring-projects-issues
Fedor Bobin commented
https://github.com/spring-projects/spring-framework/pull/1997
Comment From: spring-projects-issues
Fedor Bobin commented
Juergen Hoeller any updates?
Comment From: spring-projects-issues
Juergen Hoeller commented
I'll consider this for 5.1.3 still. That said, if there is a risk for subtle side effects, we might have to turn this into a 5.2 topic instead.
Comment From: spring-projects-issues
Juergen Hoeller commented
I went with a more extensive refactoring, inlining the batch update code into (NamedParameter)JdbcTemplate
itself and deprecating (NamedParameter)BatchUpdateUtils
accordingly.
Comment From: spring-projects-issues
Yanming Zhou commented
Does NamedParameterJdbcTemplate supports Array/Collection with IN clause like this now?
String sql = "select * from users where id in (:ids)";
Map<String,Object> sqlParameterSource = Collections.singletonMap("ids", new Integer[]{1, 2, 3});
namedParameterJdbcTemplate.query(sql, sqlParameterSource, rowCallbackHandler);
currently my workaround is expand sql to "select * from users where id in (:ids[0],:ids[1],:ids[2])".
Comment From: spring-projects-issues
Fedor Bobin commented
Yes. It will work (if your batches has same size)
Comment From: spring-projects-issues
Yanming Zhou commented
Fedor Bobin It's seems not working, It's querying not updating, It's one single sql not batch update.
Comment From: niloay
If the Array/Collection size is different, It's seems not working.
Comment From: sbrannen
@niloay, this issue was closed 2.5 years ago.
If you feel you have discovered a bug, please open a new issue.