Browse Source

Consistently return empty array in case of empty batch arguments

Issue: SPR-17476
pull/2013/head
Juergen Hoeller 7 years ago
parent
commit
362c59c310
  1. 13
      spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
  2. 11
      spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
  3. 60
      spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
  4. 35
      spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java

13
spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,24 +27,29 @@ import org.springframework.lang.Nullable;
* Mainly for internal use within the framework. * Mainly for internal use within the framework.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
public abstract class BatchUpdateUtils { public abstract class BatchUpdateUtils {
public static int[] executeBatchUpdate( public static int[] executeBatchUpdate(
String sql, final List<Object[]> batchValues, final int[] columnTypes, JdbcOperations jdbcOperations) { String sql, final List<Object[]> batchArgs, final int[] columnTypes, JdbcOperations jdbcOperations) {
if (batchArgs.isEmpty()) {
return new int[0];
}
return jdbcOperations.batchUpdate( return jdbcOperations.batchUpdate(
sql, sql,
new BatchPreparedStatementSetter() { new BatchPreparedStatementSetter() {
@Override @Override
public void setValues(PreparedStatement ps, int i) throws SQLException { public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = batchValues.get(i); Object[] values = batchArgs.get(i);
setStatementParameters(values, ps, columnTypes); setStatementParameters(values, ps, columnTypes);
} }
@Override @Override
public int getBatchSize() { public int getBatchSize() {
return batchValues.size(); return batchArgs.size();
} }
}); });
} }

11
spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,15 +28,16 @@ import org.springframework.jdbc.core.JdbcOperations;
* Mainly for internal use within the framework. * Mainly for internal use within the framework.
* *
* @author Thomas Risberg * @author Thomas Risberg
* @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
public abstract class NamedParameterBatchUpdateUtils extends BatchUpdateUtils { public abstract class NamedParameterBatchUpdateUtils extends BatchUpdateUtils {
public static int[] executeBatchUpdateWithNamedParameters(final ParsedSql parsedSql, public static int[] executeBatchUpdateWithNamedParameters(
final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) { final ParsedSql parsedSql, final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
if (batchArgs.length <= 0) { if (batchArgs.length == 0) {
return new int[] {0}; return new int[0];
} }
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, batchArgs[0]); String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, batchArgs[0]);

60
spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java

@ -192,7 +192,7 @@ public class JdbcTemplateTests {
Object argument, JdbcTemplateCallback jdbcTemplateCallback) throws Exception { Object argument, JdbcTemplateCallback jdbcTemplateCallback) throws Exception {
String sql = "SELECT FORENAME FROM CUSTMR"; String sql = "SELECT FORENAME FROM CUSTMR";
String[] results = { "rod", "gary", " portia" }; String[] results = {"rod", "gary", " portia"};
class StringHandler implements RowCallbackHandler { class StringHandler implements RowCallbackHandler {
private List<String> list = new LinkedList<>(); private List<String> list = new LinkedList<>();
@ -491,8 +491,8 @@ public class JdbcTemplateTests {
@Test @Test
public void testBatchUpdateWithPreparedStatement() throws Exception { public void testBatchUpdateWithPreparedStatement() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 }; final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] { 1, 2 }; final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
@ -525,8 +525,8 @@ public class JdbcTemplateTests {
@Test @Test
public void testInterruptibleBatchUpdate() throws Exception { public void testInterruptibleBatchUpdate() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 }; final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] { 1, 2 }; final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
@ -566,8 +566,8 @@ public class JdbcTemplateTests {
@Test @Test
public void testInterruptibleBatchUpdateWithBaseClass() throws Exception { public void testInterruptibleBatchUpdateWithBaseClass() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 }; final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] { 1, 2 }; final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
@ -603,8 +603,8 @@ public class JdbcTemplateTests {
@Test @Test
public void testInterruptibleBatchUpdateWithBaseClassAndNoBatchSupport() throws Exception { public void testInterruptibleBatchUpdateWithBaseClassAndNoBatchSupport() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 }; final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] { 1, 2 }; final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]); given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]);
mockDatabaseMetaData(false); mockDatabaseMetaData(false);
@ -640,8 +640,8 @@ public class JdbcTemplateTests {
@Test @Test
public void testBatchUpdateWithPreparedStatementAndNoBatchSupport() throws Exception { public void testBatchUpdateWithPreparedStatementAndNoBatchSupport() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 }; final int[] ids = new int[] {100, 200};
final int[] rowsAffected = new int[] { 1, 2 }; final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]); given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]);
@ -671,7 +671,7 @@ public class JdbcTemplateTests {
@Test @Test
public void testBatchUpdateFails() throws Exception { public void testBatchUpdateFails() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final int[] ids = new int[] { 100, 200 }; final int[] ids = new int[] {100, 200};
SQLException sqlException = new SQLException(); SQLException sqlException = new SQLException();
given(this.preparedStatement.executeBatch()).willThrow(sqlException); given(this.preparedStatement.executeBatch()).willThrow(sqlException);
@ -702,6 +702,15 @@ public class JdbcTemplateTests {
} }
} }
@Test
public void testBatchUpdateWithEmptyList() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = template.batchUpdate(sql, Collections.emptyList());
assertTrue("executed 0 updates", actualRowsAffected.length == 0);
}
@Test @Test
public void testBatchUpdateWithListOfObjectArrays() throws Exception { public void testBatchUpdateWithListOfObjectArrays() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
@ -712,11 +721,9 @@ public class JdbcTemplateTests {
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
JdbcTemplate template = new JdbcTemplate(this.dataSource, false); JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = template.batchUpdate(sql, ids); int[] actualRowsAffected = template.batchUpdate(sql, ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2); assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]); assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]); assertEquals(rowsAffected[1], actualRowsAffected[1]);
@ -739,10 +746,9 @@ public class JdbcTemplateTests {
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
this.template = new JdbcTemplate(this.dataSource, false); this.template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = this.template.batchUpdate(sql, ids, sqlTypes);
int[] actualRowsAffected = this.template.batchUpdate(sql, ids, sqlTypes);
assertTrue("executed 2 updates", actualRowsAffected.length == 2); assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]); assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]); assertEquals(rowsAffected[1], actualRowsAffected[1]);
@ -757,8 +763,8 @@ public class JdbcTemplateTests {
public void testBatchUpdateWithCollectionOfObjects() throws Exception { public void testBatchUpdateWithCollectionOfObjects() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?"; final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final List<Integer> ids = Arrays.asList(100, 200, 300); final List<Integer> ids = Arrays.asList(100, 200, 300);
final int[] rowsAffected1 = new int[] { 1, 2 }; final int[] rowsAffected1 = new int[] {1, 2};
final int[] rowsAffected2 = new int[] { 3 }; final int[] rowsAffected2 = new int[] {3};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected1, rowsAffected2); given(this.preparedStatement.executeBatch()).willReturn(rowsAffected1, rowsAffected2);
mockDatabaseMetaData(true); mockDatabaseMetaData(true);
@ -781,19 +787,20 @@ public class JdbcTemplateTests {
} }
@Test @Test
public void testCouldntGetConnectionForOperationOrExceptionTranslator() throws SQLException { public void testCouldNotGetConnectionForOperationOrExceptionTranslator() throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx"); SQLException sqlException = new SQLException("foo", "07xxx");
this.dataSource = mock(DataSource.class); this.dataSource = mock(DataSource.class);
given(this.dataSource.getConnection()).willThrow(sqlException); given(this.dataSource.getConnection()).willThrow(sqlException);
JdbcTemplate template = new JdbcTemplate(this.dataSource, false); JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
RowCountCallbackHandler rcch = new RowCountCallbackHandler(); RowCountCallbackHandler rcch = new RowCountCallbackHandler();
this.thrown.expect(CannotGetJdbcConnectionException.class); this.thrown.expect(CannotGetJdbcConnectionException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException))); this.thrown.expect(exceptionCause(sameInstance(sqlException)));
template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch); template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch);
} }
@Test @Test
public void testCouldntGetConnectionForOperationWithLazyExceptionTranslator() throws SQLException { public void testCouldNotGetConnectionForOperationWithLazyExceptionTranslator() throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx"); SQLException sqlException = new SQLException("foo", "07xxx");
this.dataSource = mock(DataSource.class); this.dataSource = mock(DataSource.class);
given(this.dataSource.getConnection()).willThrow(sqlException); given(this.dataSource.getConnection()).willThrow(sqlException);
@ -801,30 +808,31 @@ public class JdbcTemplateTests {
this.template.setDataSource(this.dataSource); this.template.setDataSource(this.dataSource);
this.template.afterPropertiesSet(); this.template.afterPropertiesSet();
RowCountCallbackHandler rcch = new RowCountCallbackHandler(); RowCountCallbackHandler rcch = new RowCountCallbackHandler();
this.thrown.expect(CannotGetJdbcConnectionException.class); this.thrown.expect(CannotGetJdbcConnectionException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException))); this.thrown.expect(exceptionCause(sameInstance(sqlException)));
this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch); this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch);
} }
@Test @Test
public void testCouldntGetConnectionInOperationWithExceptionTranslatorInitializedViaBeanProperty() public void testCouldNotGetConnectionInOperationWithExceptionTranslatorInitializedViaBeanProperty()
throws SQLException { throws SQLException {
doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(true); doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(true);
} }
@Test @Test
public void testCouldntGetConnectionInOperationWithExceptionTranslatorInitializedInAfterPropertiesSet() public void testCouldNotGetConnectionInOperationWithExceptionTranslatorInitializedInAfterPropertiesSet()
throws SQLException { throws SQLException {
doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(false); doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(false);
} }
/** /**
* If beanProperty is true, initialize via exception translator bean property; * If beanProperty is true, initialize via exception translator bean property;
* if false, use afterPropertiesSet(). * if false, use afterPropertiesSet().
*/ */
private void doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(boolean beanProperty) private void doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(boolean beanProperty)
throws SQLException { throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx"); SQLException sqlException = new SQLException("foo", "07xxx");
@ -884,7 +892,7 @@ public class JdbcTemplateTests {
} }
@Test @Test
public void testCouldntClose() throws Exception { public void testCouldNotClose() throws Exception {
SQLException sqlException = new SQLException("bar"); SQLException sqlException = new SQLException("bar");
given(this.connection.createStatement()).willReturn(this.statement); given(this.connection.createStatement()).willReturn(this.statement);
given(this.resultSet.next()).willReturn(false); given(this.resultSet.next()).willReturn(false);

35
spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java

@ -418,12 +418,10 @@ public class NamedParameterJdbcTemplateTests {
given(preparedStatement.executeBatch()).willReturn(rowsAffected); given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData); given(connection.getMetaData()).willReturn(databaseMetaData);
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
JdbcTemplate template = new JdbcTemplate(dataSource, false); int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
namedParameterTemplate = new NamedParameterJdbcTemplate(template); "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertSame(template, namedParameterTemplate.getJdbcTemplate());
int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2); assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]); assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]); assertEquals(rowsAffected[1], actualRowsAffected[1]);
@ -435,6 +433,17 @@ public class NamedParameterJdbcTemplateTests {
verify(connection, atLeastOnce()).close(); verify(connection, atLeastOnce()).close();
} }
@Test
public void testBatchUpdateWithEmptyMap() throws Exception {
@SuppressWarnings("unchecked")
final Map<String, Integer>[] ids = new Map[0];
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 0 updates", actualRowsAffected.length == 0);
}
@Test @Test
public void testBatchUpdateWithSqlParameterSource() throws Exception { public void testBatchUpdateWithSqlParameterSource() throws Exception {
SqlParameterSource[] ids = new SqlParameterSource[2]; SqlParameterSource[] ids = new SqlParameterSource[2];
@ -444,12 +453,10 @@ public class NamedParameterJdbcTemplateTests {
given(preparedStatement.executeBatch()).willReturn(rowsAffected); given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData); given(connection.getMetaData()).willReturn(databaseMetaData);
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
JdbcTemplate template = new JdbcTemplate(dataSource, false); int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
namedParameterTemplate = new NamedParameterJdbcTemplate(template); "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertSame(template, namedParameterTemplate.getJdbcTemplate());
int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2); assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]); assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]); assertEquals(rowsAffected[1], actualRowsAffected[1]);
@ -470,12 +477,10 @@ public class NamedParameterJdbcTemplateTests {
given(preparedStatement.executeBatch()).willReturn(rowsAffected); given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData); given(connection.getMetaData()).willReturn(databaseMetaData);
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
JdbcTemplate template = new JdbcTemplate(dataSource, false); int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
namedParameterTemplate = new NamedParameterJdbcTemplate(template); "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertSame(template, namedParameterTemplate.getJdbcTemplate());
int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2); assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]); assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]); assertEquals(rowsAffected[1], actualRowsAffected[1]);

Loading…
Cancel
Save