Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -680,16 +680,37 @@ provides `firstName` and `lastName` properties, such as the `Actor` class from a
.update();
----

For batch updates, provide several batches of parameters – either positional parameter
arrays or named ``SqlParameterSource``s – which are executed as a single JDBC batch:

[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.batchUpdate(List.of(
new Object[] {"Leonor", "Watling"},
new Object[] {"Christian", "Bale"}));
----

Or with named parameters:

[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.batchUpdate(
new MapSqlParameterSource("firstName", "Leonor").addValue("lastName", "Watling"),
new MapSqlParameterSource("firstName", "Christian").addValue("lastName", "Bale"));
----

The automatic `Actor` class mapping for parameters as well as the query results above is
provided through implicit `SimplePropertySqlParameterSource` and `SimplePropertyRowMapper`
strategies which are also available for direct use. They can serve as a common replacement
for `BeanPropertySqlParameterSource` and `BeanPropertyRowMapper`/`DataClassRowMapper`,
also with `JdbcTemplate` and `NamedParameterJdbcTemplate` themselves.

NOTE: `JdbcClient` is a flexible but simplified facade for JDBC query/update statements.
Advanced capabilities such as batch inserts and stored procedure calls typically require
extra customization: consider Spring's `SimpleJdbcInsert` and `SimpleJdbcCall` classes or
plain direct `JdbcTemplate` usage for any such capabilities not available in `JdbcClient`.
Advanced capabilities such as stored procedure calls typically require extra customization:
consider Spring's `SimpleJdbcInsert` and `SimpleJdbcCall` classes or plain direct
`JdbcTemplate` usage for any such capabilities not available in `JdbcClient`.


[[jdbc-SQLExceptionTranslator]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,16 @@ public int update(KeyHolder generatedKeyHolder, String... keyColumnNames) {
this.classicOps.update(statementCreatorForIndexedParamsWithKeys(keyColumnNames), generatedKeyHolder));
}

@Override
public int[] batchUpdate(List<Object[]> batchArgs) {
return this.classicOps.batchUpdate(this.sql, batchArgs);
}

@Override
public int[] batchUpdate(SqlParameterSource... batchArgs) {
return this.namedParamOps.batchUpdate(this.sql, batchArgs);
}

private boolean useNamedParams() {
boolean hasNamedParams = (this.namedParams.hasValues() || this.namedParamSource != this.namedParams);
if (hasNamedParams && !this.indexedParams.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@
*
* <p>Delegates to {@link org.springframework.jdbc.core.JdbcTemplate} and
* {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate}.
* For complex JDBC operations &mdash; for example, batch inserts and stored
* procedure calls &mdash; you may use those lower-level template classes directly,
* or alternatively {@link SimpleJdbcInsert} and {@link SimpleJdbcCall}.
* Batch updates are supported through {@link StatementSpec#batchUpdate(List)} and
* {@link StatementSpec#batchUpdate(SqlParameterSource...)}; for other complex JDBC
* operations &mdash; for example, stored procedure calls &mdash; you may use those
* lower-level template classes directly, or alternatively {@link SimpleJdbcInsert}
* and {@link SimpleJdbcCall}.
*
* @author Juergen Hoeller
* @author Sam Brannen
Expand Down Expand Up @@ -344,6 +346,38 @@ interface StatementSpec {
* @see java.sql.DatabaseMetaData#supportsGetGeneratedKeys()
*/
int update(KeyHolder generatedKeyHolder, String... keyColumnNames);

/**
* Execute the provided SQL statement as a batch update, with several
* batches of positional parameters for "?" placeholder resolution.
* <p>Each element in the given list provides the positional parameter
* values for one statement execution, with each value bound by its
* JDBC index (that is, element index + 1).
* @param batchArgs the batches of positional parameter values, with each
* {@code Object} array representing one set of "?" placeholder values
* @return an array containing the numbers of rows affected by each
* execution in the batch
* @since 7.1
* @see java.sql.PreparedStatement#executeBatch()
* @see org.springframework.jdbc.core.JdbcOperations#batchUpdate(String, List)
*/
int[] batchUpdate(List<Object[]> batchArgs);

/**
* Execute the provided SQL statement as a batch update, with several
* batches of named parameters for ":x" placeholder resolution.
* <p>Each {@link SqlParameterSource} provides the named parameter values
* for one statement execution. Convenient batch parameter sources can be
* built from maps or parameter objects through
* {@link org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils}.
* @param batchArgs the batches of named parameter values
* @return an array containing the numbers of rows affected by each
* execution in the batch
* @since 7.1
* @see java.sql.PreparedStatement#executeBatch()
* @see org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations#batchUpdate(String, SqlParameterSource[])
*/
int[] batchUpdate(SqlParameterSource... batchArgs);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import org.junit.jupiter.api.Test;

import org.springframework.core.io.ClassRelativeResourceLoader;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
Expand Down Expand Up @@ -146,6 +148,34 @@ void updateWithGeneratedKeysAndKeyColumnNamesUsingNamedParameters() {
assertUser(expectedId, firstName, lastName);
}

@Test
void batchUpdateWithIndexedParameters() {
int[] rowsAffected = this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS)
.batchUpdate(List.of(
new Object[] {"Jane", "Smith"},
new Object[] {"John", "Doe"}));

assertThat(rowsAffected).containsExactly(1, 1);
assertNumUsers(3);
assertUser(1, "Jane", "Smith");
assertUser(2, "John", "Doe");
}

@Test
void batchUpdateWithNamedParameters() {
SqlParameterSource[] batchArgs = {
new MapSqlParameterSource().addValue("firstName", "Jane").addValue("lastName", "Smith"),
new MapSqlParameterSource().addValue("firstName", "John").addValue("lastName", "Doe")
};

int[] rowsAffected = this.jdbcClient.sql(INSERT_WITH_NAMED_PARAMS).batchUpdate(batchArgs);

assertThat(rowsAffected).containsExactly(1, 1);
assertNumUsers(3);
assertUser(1, "Jane", "Smith");
assertUser(2, "John", "Doe");
}


@Nested // gh-34768
class ReusedNamedParameterTests {
Expand Down