Polishing.

Delegate BatchJdbcOperations calls to NamedParameterJdbcOperations. Refine since and deprecation tags.

See #1616
This commit is contained in:
Mark Paluch
2023-10-11 09:52:13 +02:00
parent 5d6d575ab2
commit a72bf1a65d
4 changed files with 41 additions and 134 deletions

View File

@@ -15,49 +15,35 @@
*/
package org.springframework.data.jdbc.core.convert;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
import org.springframework.jdbc.core.namedparam.ParsedSql;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Counterpart to {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations} containing methods for
* performing batch updates with generated keys.
*
* @author Chirag Tailor
* @author Mark Paluch
* @since 2.4
* @deprecated Use the standard {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations} instead.
* @deprecated since 3.2. Use {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations#batchUpdate}
* methods instead.
*/
@Deprecated(since = "3.2")
public class BatchJdbcOperations {
private final JdbcOperations jdbcOperations;
private final NamedParameterJdbcOperations jdbcOperations;
public BatchJdbcOperations(JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations;
this.jdbcOperations = new NamedParameterJdbcTemplate(jdbcOperations);
}
/**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments, returning generated keys.
*
*
* @param sql the SQL statement to execute
* @param batchArgs the array of {@link SqlParameterSource} containing the batch of arguments for the query
* @param generatedKeyHolder a {@link KeyHolder} that will hold the generated keys
@@ -69,12 +55,12 @@ public class BatchJdbcOperations {
* @since 2.4
*/
int[] batchUpdate(String sql, SqlParameterSource[] batchArgs, KeyHolder generatedKeyHolder) {
return batchUpdate(sql, batchArgs, generatedKeyHolder, null);
return jdbcOperations.batchUpdate(sql, batchArgs, generatedKeyHolder);
}
/**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments, returning generated keys.
*
*
* @param sql the SQL statement to execute
* @param batchArgs the array of {@link SqlParameterSource} containing the batch of arguments for the query
* @param generatedKeyHolder a {@link KeyHolder} that will hold the generated keys
@@ -88,87 +74,6 @@ public class BatchJdbcOperations {
*/
int[] batchUpdate(String sql, SqlParameterSource[] batchArgs, KeyHolder generatedKeyHolder,
@Nullable String[] keyColumnNames) {
if (batchArgs.length == 0) {
return new int[0];
}
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
SqlParameterSource paramSource = batchArgs[0];
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
List<SqlParameter> declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters);
if (keyColumnNames != null) {
pscf.setGeneratedKeysColumnNames(keyColumnNames);
} else {
pscf.setReturnGeneratedKeys(true);
}
Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
PreparedStatementCreator psc = pscf.newPreparedStatementCreator(params);
BatchPreparedStatementSetter bpss = new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = NamedParameterUtils.buildValueArray(parsedSql, batchArgs[i], null);
pscf.newPreparedStatementSetter(values).setValues(ps);
}
@Override
public int getBatchSize() {
return batchArgs.length;
}
};
PreparedStatementCallback<int[]> preparedStatementCallback = ps -> {
int batchSize = bpss.getBatchSize();
generatedKeyHolder.getKeyList().clear();
if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
for (int i = 0; i < batchSize; i++) {
bpss.setValues(ps, i);
ps.addBatch();
}
int[] results = ps.executeBatch();
storeGeneratedKeys(generatedKeyHolder, ps, batchSize);
return results;
} else {
List<Integer> rowsAffected = new ArrayList<>();
for (int i = 0; i < batchSize; i++) {
bpss.setValues(ps, i);
rowsAffected.add(ps.executeUpdate());
storeGeneratedKeys(generatedKeyHolder, ps, 1);
}
int[] rowsAffectedArray = new int[rowsAffected.size()];
for (int i = 0; i < rowsAffectedArray.length; i++) {
rowsAffectedArray[i] = rowsAffected.get(i);
}
return rowsAffectedArray;
}
};
int[] result = jdbcOperations.execute(psc, preparedStatementCallback);
Assert.state(result != null, "No result array");
return result;
}
private void storeGeneratedKeys(KeyHolder generatedKeyHolder, PreparedStatement ps, int rowsExpected)
throws SQLException {
List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList();
ResultSet keys = ps.getGeneratedKeys();
if (keys != null) {
try {
RowMapperResultSetExtractor<Map<String, Object>> rse = new RowMapperResultSetExtractor<>(
new ColumnMapRowMapper(), rowsExpected);
// noinspection ConstantConditions
generatedKeys.addAll(rse.extractData(keys));
} finally {
JdbcUtils.closeResultSet(keys);
}
}
return jdbcOperations.batchUpdate(sql, batchArgs, generatedKeyHolder, keyColumnNames);
}
}

View File

@@ -43,9 +43,9 @@ public class InsertStrategyFactory {
/**
* Constructor with additional {@link BatchJdbcOperations} constructor.
*
* @deprecated Use the {@link InsertStrategyFactory#InsertStrategyFactory(NamedParameterJdbcOperations, Dialect)}
* instead.
*
* @deprecated since 3.2, use
* {@link InsertStrategyFactory#InsertStrategyFactory(NamedParameterJdbcOperations, Dialect)} instead.
*/
@Deprecated(since = "3.2")
public InsertStrategyFactory(NamedParameterJdbcOperations namedParameterJdbcOperations,

View File

@@ -65,6 +65,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Unit tests for application events via {@link SimpleJdbcRepository}.
@@ -81,7 +82,7 @@ class SimpleJdbcRepositoryEventsUnitTests {
private static final long generatedId = 4711L;
private CollectingEventPublisher publisher = new CollectingEventPublisher();
private final CollectingEventPublisher publisher = new CollectingEventPublisher();
private DummyEntityRepository repository;
private DefaultDataAccessStrategy dataAccessStrategy;
@@ -306,7 +307,7 @@ class SimpleJdbcRepositoryEventsUnitTests {
extends CrudRepository<DummyEntity, Long>, PagingAndSortingRepository<DummyEntity, Long> {}
static final class DummyEntity {
@Id private final Long id;
private final @Id Long id;
public DummyEntity(Long id) {
this.id = id;
@@ -316,34 +317,31 @@ class SimpleJdbcRepositoryEventsUnitTests {
return this.id;
}
public boolean equals(final Object o) {
if (o == this)
return true;
if (!(o instanceof DummyEntity))
return false;
final DummyEntity other = (DummyEntity) o;
final Object this$id = this.getId();
final Object other$id = other.getId();
if (this$id == null ? other$id != null : !this$id.equals(other$id))
return false;
return true;
public DummyEntity withId(Long id) {
return this.id == id ? this : new DummyEntity(id);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
DummyEntity that = (DummyEntity) o;
return ObjectUtils.nullSafeEquals(id, that.id);
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $id = this.getId();
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
return result;
return ObjectUtils.nullSafeHashCode(id);
}
public String toString() {
return "SimpleJdbcRepositoryEventsUnitTests.DummyEntity(id=" + this.getId() + ")";
}
public DummyEntity withId(Long id) {
return this.id == id ? this : new DummyEntity(id);
}
}
static class CollectingEventPublisher implements ApplicationEventPublisher {

View File

@@ -30,13 +30,17 @@ This should be significant more efficient, especially for complex aggregates, co
+
Currently, Single Query Loading is restricted in different ways:
1. The aggregate must not have nested collections, this includes `Map`.The plan is to remove this constraint in the future.
1. The aggregate must not have nested collections, this includes `Map`.
The plan is to remove this constraint in the future.
2. The aggregate must not use `AggregateReference` or embedded entities.The plan is to remove this constraint in the future.
2. The aggregate must not use `AggregateReference` or embedded entities.
The plan is to remove this constraint in the future.
3. The database dialect must support it.Of the dialects provided by Spring Data JDBC all but H2 and HSQL support this.H2 and HSQL don't support analytic functions (aka windowing functions).
3. The database dialect must support it.Of the dialects provided by Spring Data JDBC all but H2 and HSQL support this.
H2 and HSQL don't support analytic functions (aka windowing functions).
4. It only works for the find methods in `CrudRepository`, not for derived queries and not for annotated queries.The plan is to remove this constraint in the future.
4. It only works for the find methods in `CrudRepository`, not for derived queries and not for annotated queries.
The plan is to remove this constraint in the future.
5. Single Query Loading needs to be enabled in the `JdbcMappingContext`, by calling `setSingleQueryLoadingEnabled(true)`