Skip to content

Commit

Permalink
Polishing.
Browse files Browse the repository at this point in the history
Delegate BatchJdbcOperations calls to NamedParameterJdbcOperations. Refine since and deprecation tags.

See #1616
  • Loading branch information
mp911de committed Oct 11, 2023
1 parent 5d6d575 commit a72bf1a
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -69,12 +55,12 @@ public BatchJdbcOperations(JdbcOperations jdbcOperations) {
* @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
Expand All @@ -88,87 +74,6 @@ int[] batchUpdate(String sql, SqlParameterSource[] batchArgs, KeyHolder generate
*/
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public InsertStrategyFactory(NamedParameterJdbcOperations jdbcOperations, Dialec

/**
* 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
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}.
Expand All @@ -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;
Expand Down Expand Up @@ -306,7 +307,7 @@ interface DummyEntityRepository
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;
Expand All @@ -316,34 +317,31 @@ public Long getId() {
return this.id;
}

public boolean equals(final Object o) {
if (o == this)
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 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))
if (o == null || getClass() != o.getClass())
return false;
return true;

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 {
Expand Down
12 changes: 8 additions & 4 deletions src/main/antora/modules/ROOT/pages/jdbc/entity-persistence.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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)`

Expand Down

0 comments on commit a72bf1a

Please sign in to comment.