Skip to content

Commit 92734db

Browse files
committed
Multiple batch updates in NamedParameterJdbcOperations
Signed-off-by: Alexej Timonin <[email protected]>
1 parent ecc7ddd commit 92734db

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.jdbc.core.namedparam;
1818

19+
import java.util.Collection;
1920
import java.util.List;
2021
import java.util.Map;
2122
import java.util.stream.Stream;
@@ -586,4 +587,16 @@ int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHol
586587
int[] batchUpdate(String sql, SqlParameterSource[] batchArgs, KeyHolder generatedKeyHolder,
587588
String[] keyColumnNames);
588589

590+
/**
591+
* Executes the specified SQL update statement in multiple batches using the provided batch arguments.
592+
* @param sql the SQL statement to execute
593+
* @param batchArgs the collection of {@link SqlParameterSource} containing arguments for the query
594+
* @param batchSize batch size
595+
* @return a two-dimensional array containing results for each batch execution.
596+
* (may also contain special JDBC-defined negative values for affected rows such as
597+
* {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
598+
* @throws DataAccessException if there is any problem issuing the update
599+
*/
600+
int[][] batchUpdate(String sql, Collection<SqlParameterSource> batchArgs, int batchSize);
601+
589602
}

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

+16
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.sql.PreparedStatement;
2020
import java.sql.SQLException;
21+
import java.util.Collection;
2122
import java.util.List;
2223
import java.util.Map;
2324
import java.util.function.Consumer;
@@ -423,6 +424,21 @@ public int getBatchSize() {
423424
}, generatedKeyHolder);
424425
}
425426

427+
@Override
428+
public int[][] batchUpdate(String sql, Collection<SqlParameterSource> batchArgs, int batchSize) {
429+
if (batchArgs.isEmpty() || batchSize <= 0) {
430+
return new int[0][0];
431+
}
432+
433+
ParsedSql parsedSql = getParsedSql(sql);
434+
SqlParameterSource sqlParameterSource = batchArgs.iterator().next();
435+
PreparedStatementCreatorFactory pscf = getPreparedStatementCreatorFactory(parsedSql, sqlParameterSource);
436+
437+
return getJdbcOperations().batchUpdate(pscf.getSql(), batchArgs, batchSize, (ps, argument) -> {
438+
@Nullable Object[] values = NamedParameterUtils.buildValueArray(parsedSql, argument, null);
439+
pscf.newPreparedStatementSetter(values).setValues(ps);
440+
});
441+
}
426442

427443
/**
428444
* Build a {@link PreparedStatementCreator} based on the given SQL and named parameters.

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

+101
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import static org.mockito.Mockito.mock;
5454
import static org.mockito.Mockito.times;
5555
import static org.mockito.Mockito.verify;
56+
import static org.mockito.Mockito.verifyNoInteractions;
5657

5758
/**
5859
* @author Rick Evans
@@ -580,4 +581,104 @@ void testBatchUpdateWithSqlParameterSourcePlusTypeInfo() throws Exception {
580581
verify(connection, atLeastOnce()).close();
581582
}
582583

584+
585+
@Test
586+
void testMultipleBatchUpdateWithSqlParameterSource() throws Exception {
587+
List<SqlParameterSource> ids = List.of(
588+
new MapSqlParameterSource("id", 100),
589+
new MapSqlParameterSource("id", 200),
590+
new MapSqlParameterSource("id", 300),
591+
new MapSqlParameterSource("id", 400),
592+
new MapSqlParameterSource("id", 500)
593+
);
594+
595+
int[] rowsAffected1 = new int[]{1, 2};
596+
int[] rowsAffected2 = new int[]{3, 4};
597+
int[] rowsAffected3 = new int[]{5};
598+
599+
given(preparedStatement.executeBatch()).willReturn(rowsAffected1, rowsAffected2, rowsAffected3);
600+
given(connection.getMetaData()).willReturn(databaseMetaData);
601+
602+
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
603+
604+
int batchSize = 2;
605+
606+
int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
607+
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids, batchSize);
608+
609+
assertThat(actualRowsAffected.length).as("executed 3 batches").isEqualTo(3);
610+
assertThat(actualRowsAffected[0]).isEqualTo(rowsAffected1);
611+
assertThat(actualRowsAffected[1]).isEqualTo(rowsAffected2);
612+
assertThat(actualRowsAffected[2]).isEqualTo(rowsAffected3);
613+
614+
verify(preparedStatement, times(5)).addBatch();
615+
verify(connection, times(1)).prepareStatement("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?");
616+
verify(preparedStatement).setObject(1, 100);
617+
verify(preparedStatement).setObject(1, 200);
618+
verify(preparedStatement).setObject(1, 300);
619+
verify(preparedStatement).setObject(1, 400);
620+
verify(preparedStatement).setObject(1, 500);
621+
verify(preparedStatement, atLeastOnce()).close();
622+
verify(connection, atLeastOnce()).close();
623+
}
624+
625+
@Test
626+
void testMultipleBatchUpdateWithEmptySqlParameterSourceArg() {
627+
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
628+
629+
int batchSize = 2;
630+
631+
int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
632+
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", Collections.emptyList(), batchSize);
633+
634+
assertThat(actualRowsAffected.length).as("executed 0 batches").isZero();
635+
verifyNoInteractions(preparedStatement);
636+
verifyNoInteractions(connection);
637+
}
638+
639+
@Test
640+
void testMultipleBatchUpdateWithSqlParameterSourceWithZeroBatchSize() {
641+
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
642+
643+
int batchSize = 0;
644+
645+
int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
646+
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id",
647+
List.of(new MapSqlParameterSource("id", 100)),
648+
batchSize);
649+
650+
assertThat(actualRowsAffected.length).as("executed 0 batches").isZero();
651+
verifyNoInteractions(preparedStatement);
652+
verifyNoInteractions(connection);
653+
}
654+
655+
@Test
656+
void testMultipleBatchUpdateWithSqlParameterSourceSmallerThanBatchSize() throws Exception {
657+
List<SqlParameterSource> ids = List.of(
658+
new MapSqlParameterSource("id", 100),
659+
new MapSqlParameterSource("id", 200)
660+
);
661+
662+
int[] rowsAffected = new int[]{1, 2};
663+
664+
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
665+
given(connection.getMetaData()).willReturn(databaseMetaData);
666+
667+
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
668+
669+
int batchSize = 3;
670+
671+
int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
672+
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids, batchSize);
673+
674+
assertThat(actualRowsAffected.length).as("executed 1 batch").isEqualTo(1);
675+
assertThat(actualRowsAffected[0]).isEqualTo(rowsAffected);
676+
677+
verify(preparedStatement, times(2)).addBatch();
678+
verify(connection, times(1)).prepareStatement("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?");
679+
verify(preparedStatement).setObject(1, 100);
680+
verify(preparedStatement).setObject(1, 200);
681+
verify(preparedStatement, atLeastOnce()).close();
682+
verify(connection, atLeastOnce()).close();
683+
}
583684
}

0 commit comments

Comments
 (0)