Skip to content

Commit

Permalink
Multiple batch updates in NamedParameterJdbcOperations
Browse files Browse the repository at this point in the history
Signed-off-by: Alexej Timonin <[email protected]>
  • Loading branch information
AlexejTimonin committed Feb 2, 2025
1 parent ecc7ddd commit 92734db
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.jdbc.core.namedparam;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
Expand Down Expand Up @@ -586,4 +587,16 @@ int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHol
int[] batchUpdate(String sql, SqlParameterSource[] batchArgs, KeyHolder generatedKeyHolder,
String[] keyColumnNames);

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
Expand Down Expand Up @@ -423,6 +424,21 @@ public int getBatchSize() {
}, generatedKeyHolder);
}

@Override
public int[][] batchUpdate(String sql, Collection<SqlParameterSource> batchArgs, int batchSize) {
if (batchArgs.isEmpty() || batchSize <= 0) {
return new int[0][0];
}

ParsedSql parsedSql = getParsedSql(sql);
SqlParameterSource sqlParameterSource = batchArgs.iterator().next();
PreparedStatementCreatorFactory pscf = getPreparedStatementCreatorFactory(parsedSql, sqlParameterSource);

return getJdbcOperations().batchUpdate(pscf.getSql(), batchArgs, batchSize, (ps, argument) -> {
@Nullable Object[] values = NamedParameterUtils.buildValueArray(parsedSql, argument, null);
pscf.newPreparedStatementSetter(values).setValues(ps);
});
}

/**
* Build a {@link PreparedStatementCreator} based on the given SQL and named parameters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;

/**
* @author Rick Evans
Expand Down Expand Up @@ -580,4 +581,104 @@ void testBatchUpdateWithSqlParameterSourcePlusTypeInfo() throws Exception {
verify(connection, atLeastOnce()).close();
}


@Test
void testMultipleBatchUpdateWithSqlParameterSource() throws Exception {
List<SqlParameterSource> ids = List.of(
new MapSqlParameterSource("id", 100),
new MapSqlParameterSource("id", 200),
new MapSqlParameterSource("id", 300),
new MapSqlParameterSource("id", 400),
new MapSqlParameterSource("id", 500)
);

int[] rowsAffected1 = new int[]{1, 2};
int[] rowsAffected2 = new int[]{3, 4};
int[] rowsAffected3 = new int[]{5};

given(preparedStatement.executeBatch()).willReturn(rowsAffected1, rowsAffected2, rowsAffected3);
given(connection.getMetaData()).willReturn(databaseMetaData);

namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));

int batchSize = 2;

int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids, batchSize);

assertThat(actualRowsAffected.length).as("executed 3 batches").isEqualTo(3);
assertThat(actualRowsAffected[0]).isEqualTo(rowsAffected1);
assertThat(actualRowsAffected[1]).isEqualTo(rowsAffected2);
assertThat(actualRowsAffected[2]).isEqualTo(rowsAffected3);

verify(preparedStatement, times(5)).addBatch();
verify(connection, times(1)).prepareStatement("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?");
verify(preparedStatement).setObject(1, 100);
verify(preparedStatement).setObject(1, 200);
verify(preparedStatement).setObject(1, 300);
verify(preparedStatement).setObject(1, 400);
verify(preparedStatement).setObject(1, 500);
verify(preparedStatement, atLeastOnce()).close();
verify(connection, atLeastOnce()).close();
}

@Test
void testMultipleBatchUpdateWithEmptySqlParameterSourceArg() {
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));

int batchSize = 2;

int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", Collections.emptyList(), batchSize);

assertThat(actualRowsAffected.length).as("executed 0 batches").isZero();
verifyNoInteractions(preparedStatement);
verifyNoInteractions(connection);
}

@Test
void testMultipleBatchUpdateWithSqlParameterSourceWithZeroBatchSize() {
namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));

int batchSize = 0;

int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id",
List.of(new MapSqlParameterSource("id", 100)),
batchSize);

assertThat(actualRowsAffected.length).as("executed 0 batches").isZero();
verifyNoInteractions(preparedStatement);
verifyNoInteractions(connection);
}

@Test
void testMultipleBatchUpdateWithSqlParameterSourceSmallerThanBatchSize() throws Exception {
List<SqlParameterSource> ids = List.of(
new MapSqlParameterSource("id", 100),
new MapSqlParameterSource("id", 200)
);

int[] rowsAffected = new int[]{1, 2};

given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);

namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));

int batchSize = 3;

int[][] actualRowsAffected = namedParameterTemplate.batchUpdate(
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids, batchSize);

assertThat(actualRowsAffected.length).as("executed 1 batch").isEqualTo(1);
assertThat(actualRowsAffected[0]).isEqualTo(rowsAffected);

verify(preparedStatement, times(2)).addBatch();
verify(connection, times(1)).prepareStatement("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?");
verify(preparedStatement).setObject(1, 100);
verify(preparedStatement).setObject(1, 200);
verify(preparedStatement, atLeastOnce()).close();
verify(connection, atLeastOnce()).close();
}
}

0 comments on commit 92734db

Please sign in to comment.