Browse Source

Polishing.

Formatting.

See #1159
Original pull request # 1191
pull/1200/head
Jens Schauder 4 years ago
parent
commit
2c66ce5d3d
No known key found for this signature in database
GPG Key ID: 45CC872F17423DBF
  1. 8
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java
  2. 67
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BatchJdbcOperations.java
  3. 2
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java
  4. 21
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
  5. 4
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
  6. 13
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategy.java
  7. 14
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategy.java
  8. 14
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java
  9. 3
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertSubject.java
  10. 50
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java
  11. 32
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java
  12. 11
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
  13. 4
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
  14. 2
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java
  15. 19
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java
  16. 39
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java
  17. 16
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
  18. 65
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategyTest.java
  19. 9
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategyTest.java
  20. 3
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactoryTest.java
  21. 28
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlParametersFactoryTest.java
  22. 6
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategyUnitTests.java
  23. 2
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java
  24. 6
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
  25. 8
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java
  26. 24
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-db2.sql
  27. 22
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-h2.sql
  28. 22
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-hsql.sql
  29. 22
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-mariadb.sql
  30. 24
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-mssql.sql
  31. 22
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-mysql.sql
  32. 30
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-oracle.sql
  33. 24
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-postgres.sql
  34. 1
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
  35. 23
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java
  36. 1
      spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java
  37. 3
      spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DbActionTestSupport.java
  38. 168
      spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java

8
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java

@ -31,9 +31,9 @@ import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.Identifier; import org.springframework.data.jdbc.core.convert.Identifier;
import org.springframework.data.jdbc.core.convert.InsertSubject;
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder; import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
import org.springframework.data.jdbc.core.convert.InsertSubject;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPath;
@ -94,7 +94,8 @@ class JdbcAggregateChangeExecutionContext {
setNewVersion(initialVersion); setNewVersion(initialVersion);
} else { } else {
id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty(), insert.getIdValueSource()); id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty(),
insert.getIdValueSource());
} }
add(new DbActionExecutionResult(insert, id)); add(new DbActionExecutionResult(insert, id));
@ -103,7 +104,8 @@ class JdbcAggregateChangeExecutionContext {
<T> void executeInsert(DbAction.Insert<T> insert) { <T> void executeInsert(DbAction.Insert<T> insert) {
Identifier parentKeys = getParentKeys(insert, converter); Identifier parentKeys = getParentKeys(insert, converter);
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), parentKeys, insert.getIdValueSource()); Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), parentKeys,
insert.getIdValueSource());
add(new DbActionExecutionResult(insert, id)); add(new DbActionExecutionResult(insert, id));
} }

67
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BatchJdbcOperations.java

@ -15,6 +15,13 @@
*/ */
package org.springframework.data.jdbc.core.convert; 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.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
@ -31,21 +38,15 @@ import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/** /**
* Counterpart to {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations} containing * Counterpart to {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations} containing methods for
* methods for performing batch updates with generated keys. * performing batch updates with generated keys.
* *
* @author Chirag Tailor * @author Chirag Tailor
* @since 2.4 * @since 2.4
*/ */
public class BatchJdbcOperations { public class BatchJdbcOperations {
private final JdbcOperations jdbcOperations; private final JdbcOperations jdbcOperations;
public BatchJdbcOperations(JdbcOperations jdbcOperations) { public BatchJdbcOperations(JdbcOperations jdbcOperations) {
@ -53,15 +54,14 @@ public class BatchJdbcOperations {
} }
/** /**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments, * Execute a batch using the supplied SQL statement with the batch of supplied arguments, returning generated keys.
* returning generated keys. *
* @param sql the SQL statement to execute * @param sql the SQL statement to execute
* @param batchArgs the array of {@link SqlParameterSource} containing the batch of * @param batchArgs the array of {@link SqlParameterSource} containing the batch of arguments for the query
* arguments for the query
* @param generatedKeyHolder a {@link KeyHolder} that will hold the generated keys * @param generatedKeyHolder a {@link KeyHolder} that will hold the generated keys
* @return an array containing the numbers of rows affected by each update in the batch * @return an array containing the numbers of rows affected by each update in the batch (may also contain special
* (may also contain special JDBC-defined negative values for affected rows such as * JDBC-defined negative values for affected rows such as
* {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED}) * {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
* @throws org.springframework.dao.DataAccessException if there is any problem issuing the update * @throws org.springframework.dao.DataAccessException if there is any problem issuing the update
* @see org.springframework.jdbc.support.GeneratedKeyHolder * @see org.springframework.jdbc.support.GeneratedKeyHolder
* @since 2.4 * @since 2.4
@ -71,16 +71,15 @@ public class BatchJdbcOperations {
} }
/** /**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments, * Execute a batch using the supplied SQL statement with the batch of supplied arguments, returning generated keys.
* returning generated keys. *
* @param sql the SQL statement to execute * @param sql the SQL statement to execute
* @param batchArgs the array of {@link SqlParameterSource} containing the batch of * @param batchArgs the array of {@link SqlParameterSource} containing the batch of arguments for the query
* arguments for the query
* @param generatedKeyHolder a {@link KeyHolder} that will hold the generated keys * @param generatedKeyHolder a {@link KeyHolder} that will hold the generated keys
* @param keyColumnNames names of the columns that will have keys generated for them * @param keyColumnNames names of the columns that will have keys generated for them
* @return an array containing the numbers of rows affected by each update in the batch * @return an array containing the numbers of rows affected by each update in the batch (may also contain special
* (may also contain special JDBC-defined negative values for affected rows such as * JDBC-defined negative values for affected rows such as
* {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED}) * {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
* @throws org.springframework.dao.DataAccessException if there is any problem issuing the update * @throws org.springframework.dao.DataAccessException if there is any problem issuing the update
* @see org.springframework.jdbc.support.GeneratedKeyHolder * @see org.springframework.jdbc.support.GeneratedKeyHolder
* @since 2.4 * @since 2.4
@ -91,7 +90,7 @@ public class BatchJdbcOperations {
if (batchArgs.length == 0) { if (batchArgs.length == 0) {
return new int[0]; return new int[0];
} }
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql); ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
SqlParameterSource paramSource = batchArgs[0]; SqlParameterSource paramSource = batchArgs[0];
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource); String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
@ -105,6 +104,7 @@ public class BatchJdbcOperations {
Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null); Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
PreparedStatementCreator psc = pscf.newPreparedStatementCreator(params); PreparedStatementCreator psc = pscf.newPreparedStatementCreator(params);
BatchPreparedStatementSetter bpss = new BatchPreparedStatementSetter() { BatchPreparedStatementSetter bpss = new BatchPreparedStatementSetter() {
@Override @Override
public void setValues(PreparedStatement ps, int i) throws SQLException { public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] values = NamedParameterUtils.buildValueArray(parsedSql, batchArgs[i], null); Object[] values = NamedParameterUtils.buildValueArray(parsedSql, batchArgs[i], null);
@ -117,10 +117,13 @@ public class BatchJdbcOperations {
} }
}; };
PreparedStatementCallback<int[]> preparedStatementCallback = ps -> { PreparedStatementCallback<int[]> preparedStatementCallback = ps -> {
int batchSize = bpss.getBatchSize(); int batchSize = bpss.getBatchSize();
generatedKeyHolder.getKeyList().clear(); generatedKeyHolder.getKeyList().clear();
if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) { if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
for (int i = 0; i < batchSize; i++) { for (int i = 0; i < batchSize; i++) {
bpss.setValues(ps, i); bpss.setValues(ps, i);
ps.addBatch(); ps.addBatch();
} }
@ -128,8 +131,10 @@ public class BatchJdbcOperations {
storeGeneratedKeys(generatedKeyHolder, ps, batchSize); storeGeneratedKeys(generatedKeyHolder, ps, batchSize);
return results; return results;
} else { } else {
List<Integer> rowsAffected = new ArrayList<>(); List<Integer> rowsAffected = new ArrayList<>();
for (int i = 0; i < batchSize; i++) { for (int i = 0; i < batchSize; i++) {
bpss.setValues(ps, i); bpss.setValues(ps, i);
rowsAffected.add(ps.executeUpdate()); rowsAffected.add(ps.executeUpdate());
storeGeneratedKeys(generatedKeyHolder, ps, 1); storeGeneratedKeys(generatedKeyHolder, ps, 1);
@ -146,18 +151,20 @@ public class BatchJdbcOperations {
return result; return result;
} }
private void storeGeneratedKeys(KeyHolder generatedKeyHolder, PreparedStatement ps, int rowsExpected) throws SQLException { private void storeGeneratedKeys(KeyHolder generatedKeyHolder, PreparedStatement ps, int rowsExpected)
throws SQLException {
List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList(); List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList();
ResultSet keys = ps.getGeneratedKeys(); ResultSet keys = ps.getGeneratedKeys();
if (keys != null) { if (keys != null) {
try { try {
RowMapperResultSetExtractor<Map<String, Object>> rse =
new RowMapperResultSetExtractor<>(new ColumnMapRowMapper(), rowsExpected); RowMapperResultSetExtractor<Map<String, Object>> rse = new RowMapperResultSetExtractor<>(
//noinspection ConstantConditions new ColumnMapRowMapper(), rowsExpected);
// noinspection ConstantConditions
generatedKeys.addAll(rse.extractData(keys)); generatedKeys.addAll(rse.extractData(keys));
} } finally {
finally {
JdbcUtils.closeResultSet(keys); JdbcUtils.closeResultSet(keys);
} }
} }

2
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java

@ -76,7 +76,7 @@ public interface DataAccessStrategy extends RelationResolver {
<T> Object insert(T instance, Class<T> domainType, Identifier identifier, IdValueSource idValueSource); <T> Object insert(T instance, Class<T> domainType, Identifier identifier, IdValueSource idValueSource);
/** /**
* Inserts the data of a multiple entities. * Inserts the data of multiple entities.
* *
* @param <T> the type of the instance. * @param <T> the type of the instance.
* @param insertSubjects the subjects to be inserted, where each subject contains the instance and its identifier. * @param insertSubjects the subjects to be inserted, where each subject contains the instance and its identifier.

21
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java

@ -79,6 +79,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, RelationalMappingContext context, public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, RelationalMappingContext context,
JdbcConverter converter, NamedParameterJdbcOperations operations, SqlParametersFactory sqlParametersFactory, JdbcConverter converter, NamedParameterJdbcOperations operations, SqlParametersFactory sqlParametersFactory,
InsertStrategyFactory insertStrategyFactory) { InsertStrategyFactory insertStrategyFactory) {
Assert.notNull(sqlGeneratorSource, "SqlGeneratorSource must not be null"); Assert.notNull(sqlGeneratorSource, "SqlGeneratorSource must not be null");
Assert.notNull(context, "RelationalMappingContext must not be null"); Assert.notNull(context, "RelationalMappingContext must not be null");
Assert.notNull(converter, "JdbcConverter must not be null"); Assert.notNull(converter, "JdbcConverter must not be null");
@ -108,11 +109,13 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
@Override @Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier, IdValueSource idValueSource) { public <T> Object insert(T instance, Class<T> domainType, Identifier identifier, IdValueSource idValueSource) {
SqlIdentifierParameterSource parameterSource = sqlParametersFactory.forInsert(instance, domainType, identifier, idValueSource); SqlIdentifierParameterSource parameterSource = sqlParametersFactory.forInsert(instance, domainType, identifier,
idValueSource);
String insertSql = sql(domainType).getInsert(parameterSource.getIdentifiers()); String insertSql = sql(domainType).getInsert(parameterSource.getIdentifiers());
return insertStrategyFactory.insertStrategy(idValueSource, getIdColumn(domainType)).execute(insertSql, parameterSource); return insertStrategyFactory.insertStrategy(idValueSource, getIdColumn(domainType)).execute(insertSql,
parameterSource);
} }
@Override @Override
@ -120,12 +123,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
Assert.notEmpty(insertSubjects, "Batch insert must contain at least one InsertSubject"); Assert.notEmpty(insertSubjects, "Batch insert must contain at least one InsertSubject");
SqlIdentifierParameterSource[] sqlParameterSources = insertSubjects.stream() SqlIdentifierParameterSource[] sqlParameterSources = insertSubjects.stream()
.map(insertSubject -> sqlParametersFactory.forInsert(insertSubject.getInstance(), domainType, insertSubject.getIdentifier(), idValueSource)) .map(insertSubject -> sqlParametersFactory.forInsert(insertSubject.getInstance(), domainType,
insertSubject.getIdentifier(), idValueSource))
.toArray(SqlIdentifierParameterSource[]::new); .toArray(SqlIdentifierParameterSource[]::new);
String insertSql = sql(domainType).getInsert(sqlParameterSources[0].getIdentifiers()); String insertSql = sql(domainType).getInsert(sqlParameterSources[0].getIdentifiers());
return insertStrategyFactory.batchInsertStrategy(idValueSource, getIdColumn(domainType)).execute(insertSql, sqlParameterSources); return insertStrategyFactory.batchInsertStrategy(idValueSource, getIdColumn(domainType)).execute(insertSql,
sqlParameterSources);
} }
/* /*
@ -210,7 +215,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
String delete = sql(rootEntity.getType()).createDeleteByPath(propertyPath); String delete = sql(rootEntity.getType()).createDeleteByPath(propertyPath);
SqlIdentifierParameterSource parameters = sqlParametersFactory.forQueryById(rootId, rootEntity.getType(), ROOT_ID_PARAMETER); SqlIdentifierParameterSource parameters = sqlParametersFactory.forQueryById(rootId, rootEntity.getType(),
ROOT_ID_PARAMETER);
operations.update(delete, parameters); operations.update(delete, parameters);
} }
@ -229,6 +235,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
*/ */
@Override @Override
public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) { public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
operations.getJdbcOperations() operations.getJdbcOperations()
.update(sql(propertyPath.getBaseProperty().getOwner().getType()).createDeleteAllSql(propertyPath)); .update(sql(propertyPath.getBaseProperty().getOwner().getType()).createDeleteAllSql(propertyPath));
} }
@ -410,8 +417,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
@Nullable @Nullable
private <T> SqlIdentifier getIdColumn(Class<T> domainType) { private <T> SqlIdentifier getIdColumn(Class<T> domainType) {
return Optional.ofNullable(context.getRequiredPersistentEntity(domainType).getIdProperty()) return Optional.ofNullable(context.getRequiredPersistentEntity(domainType).getIdProperty())
.map(RelationalPersistentProperty::getColumnName) .map(RelationalPersistentProperty::getColumnName).orElse(null);
.orElse(null);
} }
} }

4
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java

@ -15,6 +15,8 @@
*/ */
package org.springframework.data.jdbc.core.convert; package org.springframework.data.jdbc.core.convert;
import java.util.List;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPath;
@ -23,8 +25,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp
import org.springframework.data.relational.core.sql.LockMode; import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.List;
/** /**
* Delegates all method calls to an instance set after construction. This is useful for {@link DataAccessStrategy}s with * Delegates all method calls to an instance set after construction. This is useful for {@link DataAccessStrategy}s with
* cyclic dependencies. * cyclic dependencies.

13
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategy.java

@ -41,9 +41,9 @@ class IdGeneratingBatchInsertStrategy implements BatchInsertStrategy {
private final BatchJdbcOperations batchJdbcOperations; private final BatchJdbcOperations batchJdbcOperations;
private final SqlIdentifier idColumn; private final SqlIdentifier idColumn;
public IdGeneratingBatchInsertStrategy(InsertStrategy insertStrategy, IdGeneratingBatchInsertStrategy(InsertStrategy insertStrategy, Dialect dialect,
Dialect dialect, BatchJdbcOperations batchJdbcOperations, BatchJdbcOperations batchJdbcOperations, @Nullable SqlIdentifier idColumn) {
@Nullable SqlIdentifier idColumn) {
this.insertStrategy = insertStrategy; this.insertStrategy = insertStrategy;
this.dialect = dialect; this.dialect = dialect;
this.batchJdbcOperations = batchJdbcOperations; this.batchJdbcOperations = batchJdbcOperations;
@ -54,9 +54,9 @@ class IdGeneratingBatchInsertStrategy implements BatchInsertStrategy {
public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) { public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) {
if (!dialect.getIdGeneration().supportedForBatchOperations()) { if (!dialect.getIdGeneration().supportedForBatchOperations()) {
return Arrays.stream(sqlParameterSources) return Arrays.stream(sqlParameterSources)
.map(sqlParameterSource -> insertStrategy.execute(sql, sqlParameterSource)) .map(sqlParameterSource -> insertStrategy.execute(sql, sqlParameterSource)).toArray();
.toArray();
} }
GeneratedKeyHolder holder = new GeneratedKeyHolder(); GeneratedKeyHolder holder = new GeneratedKeyHolder();
@ -75,6 +75,7 @@ class IdGeneratingBatchInsertStrategy implements BatchInsertStrategy {
Object[] ids = new Object[sqlParameterSources.length]; Object[] ids = new Object[sqlParameterSources.length];
List<Map<String, Object>> keyList = holder.getKeyList(); List<Map<String, Object>> keyList = holder.getKeyList();
for (int i = 0; i < keyList.size(); i++) { for (int i = 0; i < keyList.size(); i++) {
Map<String, Object> keys = keyList.get(i); Map<String, Object> keys = keyList.get(i);
if (keys.size() > 1) { if (keys.size() > 1) {
if (idColumn != null) { if (idColumn != null) {
@ -92,7 +93,7 @@ class IdGeneratingBatchInsertStrategy implements BatchInsertStrategy {
private String[] getKeyColumnNames() { private String[] getKeyColumnNames() {
return Optional.ofNullable(idColumn) return Optional.ofNullable(idColumn)
.map(idColumn -> new String[]{idColumn.getReference(dialect.getIdentifierProcessing())}) .map(idColumn -> new String[] { idColumn.getReference(dialect.getIdentifierProcessing()) })
.orElse(new String[0]); .orElse(new String[0]);
} }
} }

14
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategy.java

@ -15,6 +15,9 @@
*/ */
package org.springframework.data.jdbc.core.convert; package org.springframework.data.jdbc.core.convert;
import java.util.Map;
import java.util.Optional;
import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.Dialect;
@ -26,9 +29,6 @@ import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder; import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import java.util.Map;
import java.util.Optional;
/** /**
* An {@link InsertStrategy} that expects an id to be generated from the insert. * An {@link InsertStrategy} that expects an id to be generated from the insert.
* *
@ -41,8 +41,8 @@ class IdGeneratingInsertStrategy implements InsertStrategy {
private final NamedParameterJdbcOperations jdbcOperations; private final NamedParameterJdbcOperations jdbcOperations;
private final SqlIdentifier idColumn; private final SqlIdentifier idColumn;
public IdGeneratingInsertStrategy(Dialect dialect, NamedParameterJdbcOperations jdbcOperations, IdGeneratingInsertStrategy(Dialect dialect, NamedParameterJdbcOperations jdbcOperations,
@Nullable SqlIdentifier idColumn) { @Nullable SqlIdentifier idColumn) {
this.dialect = dialect; this.dialect = dialect;
this.jdbcOperations = jdbcOperations; this.jdbcOperations = jdbcOperations;
this.idColumn = idColumn; this.idColumn = idColumn;
@ -68,7 +68,7 @@ class IdGeneratingInsertStrategy implements InsertStrategy {
} }
try { try {
// MySQL just returns one value with a special name // MySQL just returns one value with a special name
return holder.getKey(); return holder.getKey();
} catch (DataRetrievalFailureException | InvalidDataAccessApiUsageException e) { } catch (DataRetrievalFailureException | InvalidDataAccessApiUsageException e) {
// Postgres returns a value for each column // Postgres returns a value for each column
@ -85,7 +85,7 @@ class IdGeneratingInsertStrategy implements InsertStrategy {
private String[] getKeyColumnNames() { private String[] getKeyColumnNames() {
return Optional.ofNullable(idColumn) return Optional.ofNullable(idColumn)
.map(idColumn -> new String[]{idColumn.getReference(dialect.getIdentifierProcessing())}) .map(idColumn -> new String[] { idColumn.getReference(dialect.getIdentifierProcessing()) })
.orElse(new String[0]); .orElse(new String[0]);
} }
} }

14
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java

@ -30,12 +30,14 @@ import org.springframework.lang.Nullable;
* @since 2.4 * @since 2.4
*/ */
public class InsertStrategyFactory { public class InsertStrategyFactory {
private final NamedParameterJdbcOperations namedParameterJdbcOperations; private final NamedParameterJdbcOperations namedParameterJdbcOperations;
private final BatchJdbcOperations batchJdbcOperations; private final BatchJdbcOperations batchJdbcOperations;
private final Dialect dialect; private final Dialect dialect;
public InsertStrategyFactory(NamedParameterJdbcOperations namedParameterJdbcOperations, BatchJdbcOperations batchJdbcOperations, public InsertStrategyFactory(NamedParameterJdbcOperations namedParameterJdbcOperations,
Dialect dialect) { BatchJdbcOperations batchJdbcOperations, Dialect dialect) {
this.namedParameterJdbcOperations = namedParameterJdbcOperations; this.namedParameterJdbcOperations = namedParameterJdbcOperations;
this.batchJdbcOperations = batchJdbcOperations; this.batchJdbcOperations = batchJdbcOperations;
this.dialect = dialect; this.dialect = dialect;
@ -65,8 +67,8 @@ public class InsertStrategyFactory {
if (IdValueSource.GENERATED.equals(idValueSource)) { if (IdValueSource.GENERATED.equals(idValueSource)) {
return new IdGeneratingBatchInsertStrategy( return new IdGeneratingBatchInsertStrategy(
new IdGeneratingInsertStrategy(dialect, namedParameterJdbcOperations, idColumn), new IdGeneratingInsertStrategy(dialect, namedParameterJdbcOperations, idColumn), dialect, batchJdbcOperations,
dialect, batchJdbcOperations, idColumn); idColumn);
} }
return new DefaultBatchInsertStrategy(namedParameterJdbcOperations); return new DefaultBatchInsertStrategy(namedParameterJdbcOperations);
} }
@ -75,7 +77,7 @@ public class InsertStrategyFactory {
private final NamedParameterJdbcOperations jdbcOperations; private final NamedParameterJdbcOperations jdbcOperations;
public DefaultInsertStrategy(NamedParameterJdbcOperations jdbcOperations) { DefaultInsertStrategy(NamedParameterJdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations; this.jdbcOperations = jdbcOperations;
} }
@ -91,7 +93,7 @@ public class InsertStrategyFactory {
private final NamedParameterJdbcOperations jdbcOperations; private final NamedParameterJdbcOperations jdbcOperations;
public DefaultBatchInsertStrategy(NamedParameterJdbcOperations jdbcOperations) { DefaultBatchInsertStrategy(NamedParameterJdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations; this.jdbcOperations = jdbcOperations;
} }

3
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertSubject.java

@ -27,6 +27,7 @@ import java.util.Objects;
* @since 2.4 * @since 2.4
*/ */
public final class InsertSubject<T> { public final class InsertSubject<T> {
private final T instance; private final T instance;
private final Identifier identifier; private final Identifier identifier;
@ -35,6 +36,7 @@ public final class InsertSubject<T> {
} }
private InsertSubject(T instance, Identifier identifier) { private InsertSubject(T instance, Identifier identifier) {
this.instance = instance; this.instance = instance;
this.identifier = identifier; this.identifier = identifier;
} }
@ -49,6 +51,7 @@ public final class InsertSubject<T> {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o)
return true; return true;
if (o == null || getClass() != o.getClass()) if (o == null || getClass() != o.getClass())

50
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java

@ -15,6 +15,11 @@
*/ */
package org.springframework.data.jdbc.core.convert; package org.springframework.data.jdbc.core.convert;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
@ -33,11 +38,6 @@ import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/** /**
* Generates SQL statements to be used by {@link SimpleJdbcRepository} * Generates SQL statements to be used by {@link SimpleJdbcRepository}
* *
@ -383,11 +383,11 @@ class SqlGenerator {
Table table = this.getTable(); Table table = this.getTable();
Select select = StatementBuilder // Select select = StatementBuilder //
.select(getIdColumn()) // .select(getIdColumn()) //
.from(table) // .from(table) //
.where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) // .where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
.lock(lockMode) // .lock(lockMode) //
.build(); .build();
return render(select); return render(select);
} }
@ -397,10 +397,10 @@ class SqlGenerator {
Table table = this.getTable(); Table table = this.getTable();
Select select = StatementBuilder // Select select = StatementBuilder //
.select(getIdColumn()) // .select(getIdColumn()) //
.from(table) // .from(table) //
.lock(lockMode) // .lock(lockMode) //
.build(); .build();
return render(select); return render(select);
} }
@ -733,9 +733,9 @@ class SqlGenerator {
Join(Table joinTable, Column joinColumn, Column parentId) { Join(Table joinTable, Column joinColumn, Column parentId) {
Assert.notNull( joinTable,"JoinTable must not be null."); Assert.notNull(joinTable, "JoinTable must not be null.");
Assert.notNull( joinColumn,"JoinColumn must not be null."); Assert.notNull(joinColumn, "JoinColumn must not be null.");
Assert.notNull( parentId,"ParentId must not be null."); Assert.notNull(parentId, "ParentId must not be null.");
this.joinTable = joinTable; this.joinTable = joinTable;
this.joinColumn = joinColumn; this.joinColumn = joinColumn;
@ -757,12 +757,12 @@ class SqlGenerator {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o)
if (o == null || getClass() != o.getClass()) return false; return true;
if (o == null || getClass() != o.getClass())
return false;
Join join = (Join) o; Join join = (Join) o;
return joinTable.equals(join.joinTable) && return joinTable.equals(join.joinTable) && joinColumn.equals(join.joinColumn) && parentId.equals(join.parentId);
joinColumn.equals(join.joinColumn) &&
parentId.equals(join.parentId);
} }
@Override @Override
@ -773,11 +773,7 @@ class SqlGenerator {
@Override @Override
public String toString() { public String toString() {
return "Join{" + return "Join{" + "joinTable=" + joinTable + ", joinColumn=" + joinColumn + ", parentId=" + parentId + '}';
"joinTable=" + joinTable +
", joinColumn=" + joinColumn +
", parentId=" + parentId +
'}';
} }
} }

32
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java

@ -15,8 +15,6 @@
*/ */
package org.springframework.data.jdbc.core.convert; package org.springframework.data.jdbc.core.convert;
import static org.springframework.data.jdbc.core.convert.SqlGenerator.*;
import java.sql.SQLType; import java.sql.SQLType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -69,7 +67,8 @@ public class SqlParametersFactory {
* @return the {@link SqlIdentifierParameterSource} for the insert. Guaranteed to not be {@code null}. * @return the {@link SqlIdentifierParameterSource} for the insert. Guaranteed to not be {@code null}.
* @since 2.4 * @since 2.4
*/ */
<T> SqlIdentifierParameterSource forInsert(T instance, Class<T> domainType, Identifier identifier, IdValueSource idValueSource) { <T> SqlIdentifierParameterSource forInsert(T instance, Class<T> domainType, Identifier identifier,
IdValueSource idValueSource) {
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType); RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "", SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
@ -110,9 +109,9 @@ public class SqlParametersFactory {
* @since 2.4 * @since 2.4
*/ */
<T> SqlIdentifierParameterSource forQueryById(Object id, Class<T> domainType, SqlIdentifier name) { <T> SqlIdentifierParameterSource forQueryById(Object id, Class<T> domainType, SqlIdentifier name) {
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(dialect.getIdentifierProcessing()); SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(dialect.getIdentifierProcessing());
addConvertedPropertyValue( // addConvertedPropertyValue( //
parameterSource, // parameterSource, //
getRequiredPersistentEntity(domainType).getRequiredIdProperty(), // getRequiredPersistentEntity(domainType).getRequiredIdProperty(), //
@ -135,8 +134,8 @@ public class SqlParametersFactory {
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(dialect.getIdentifierProcessing()); SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(dialect.getIdentifierProcessing());
addConvertedPropertyValuesAsList(parameterSource, getRequiredPersistentEntity(domainType).getRequiredIdProperty(), addConvertedPropertyValuesAsList(parameterSource, getRequiredPersistentEntity(domainType).getRequiredIdProperty(),
ids, IDS_SQL_PARAMETER); ids);
return parameterSource; return parameterSource;
} }
@ -150,7 +149,7 @@ public class SqlParametersFactory {
SqlIdentifierParameterSource forQueryByIdentifier(Identifier identifier) { SqlIdentifierParameterSource forQueryByIdentifier(Identifier identifier) {
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(dialect.getIdentifierProcessing()); SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(dialect.getIdentifierProcessing());
identifier.toMap() identifier.toMap()
.forEach((name, value) -> addConvertedPropertyValue(parameterSource, name, value, value.getClass())); .forEach((name, value) -> addConvertedPropertyValue(parameterSource, name, value, value.getClass()));
@ -173,19 +172,20 @@ public class SqlParametersFactory {
} }
private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource,
RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) { RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) {
addConvertedValue(parameterSource, value, name, converter.getColumnType(property), converter.getTargetSqlType(property)); addConvertedValue(parameterSource, value, name, converter.getColumnType(property),
converter.getTargetSqlType(property));
} }
private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value, private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value,
Class<?> javaType) { Class<?> javaType) {
addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.targetSqlTypeFor(javaType)); addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.targetSqlTypeFor(javaType));
} }
private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value, private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value,
SqlIdentifier paramName, Class<?> javaType, SQLType sqlType) { SqlIdentifier paramName, Class<?> javaType, SQLType sqlType) {
JdbcValue jdbcValue = converter.writeJdbcValue( // JdbcValue jdbcValue = converter.writeJdbcValue( //
value, // value, //
@ -200,7 +200,7 @@ public class SqlParametersFactory {
} }
private void addConvertedPropertyValuesAsList(SqlIdentifierParameterSource parameterSource, private void addConvertedPropertyValuesAsList(SqlIdentifierParameterSource parameterSource,
RelationalPersistentProperty property, Iterable<?> values, SqlIdentifier paramName) { RelationalPersistentProperty property, Iterable<?> values) {
List<Object> convertedIds = new ArrayList<>(); List<Object> convertedIds = new ArrayList<>();
JdbcValue jdbcValue = null; JdbcValue jdbcValue = null;
@ -218,7 +218,7 @@ public class SqlParametersFactory {
SQLType jdbcType = jdbcValue.getJdbcType(); SQLType jdbcType = jdbcValue.getJdbcType();
int typeNumber = jdbcType == null ? JdbcUtils.TYPE_UNKNOWN : jdbcType.getVendorTypeNumber(); int typeNumber = jdbcType == null ? JdbcUtils.TYPE_UNKNOWN : jdbcType.getVendorTypeNumber();
parameterSource.addValue(paramName, convertedIds, typeNumber); parameterSource.addValue(SqlGenerator.IDS_SQL_PARAMETER, convertedIds, typeNumber);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -227,8 +227,8 @@ public class SqlParametersFactory {
} }
private <S, T> SqlIdentifierParameterSource getParameterSource(@Nullable S instance, private <S, T> SqlIdentifierParameterSource getParameterSource(@Nullable S instance,
RelationalPersistentEntity<S> persistentEntity, String prefix, RelationalPersistentEntity<S> persistentEntity, String prefix,
Predicate<RelationalPersistentProperty> skipProperty, IdentifierProcessing identifierProcessing) { Predicate<RelationalPersistentProperty> skipProperty, IdentifierProcessing identifierProcessing) {
SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing); SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing);

11
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

@ -23,10 +23,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@ -101,7 +101,8 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, converter, dialect); SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, converter, dialect);
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect); SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect);
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations, new BatchJdbcOperations(operations.getJdbcOperations()), dialect); InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations,
new BatchJdbcOperations(operations.getJdbcOperations()), dialect);
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( // DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( //
sqlGeneratorSource, // sqlGeneratorSource, //
context, // context, //
@ -172,8 +173,8 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override @Override
public <T> Object[] insert(List<InsertSubject<T>> insertSubjects, Class<T> domainType, IdValueSource idValueSource) { public <T> Object[] insert(List<InsertSubject<T>> insertSubjects, Class<T> domainType, IdValueSource idValueSource) {
return insertSubjects.stream() return insertSubjects.stream().map(
.map(insertSubject -> insert(insertSubject.getInstance(), domainType, insertSubject.getIdentifier(), idValueSource)) insertSubject -> insert(insertSubject.getInstance(), domainType, insertSubject.getIdentifier(), idValueSource))
.toArray(); .toArray();
} }

4
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java

@ -22,7 +22,6 @@ import java.util.Optional;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -96,8 +95,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport() JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport()
: JdbcArrayColumns.DefaultSupport.INSTANCE; : JdbcArrayColumns.DefaultSupport.INSTANCE;
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(), DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(), arrayColumns);
arrayColumns);
return new BasicJdbcConverter(mappingContext, relationResolver, conversions, jdbcTypeFactory, return new BasicJdbcConverter(mappingContext, relationResolver, conversions, jdbcTypeFactory,
dialect.getIdentifierProcessing()); dialect.getIdentifierProcessing());

2
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java

@ -72,7 +72,7 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
JdbcConverter converter = mock(JdbcConverter.class); JdbcConverter converter = mock(JdbcConverter.class);
DbAction.WithEntity<?> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED); DbAction.WithEntity<?> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
private DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class); DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
AggregateChangeExecutor executor = new AggregateChangeExecutor(converter, accessStrategy); AggregateChangeExecutor executor = new AggregateChangeExecutor(converter, accessStrategy);

19
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java

@ -67,7 +67,8 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
public void afterInsertRootIdAndVersionMaybeUpdated() { public void afterInsertRootIdAndVersionMaybeUpdated() {
// note that the root entity isn't the original one, but a new instance with the version set. // note that the root entity isn't the original one, but a new instance with the version set.
when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()), eq(IdValueSource.GENERATED))).thenReturn(23L); when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()),
eq(IdValueSource.GENERATED))).thenReturn(23L);
executionContext.executeInsertRoot(new DbAction.InsertRoot<>(root, IdValueSource.GENERATED)); executionContext.executeInsertRoot(new DbAction.InsertRoot<>(root, IdValueSource.GENERATED));
@ -86,8 +87,10 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
Content content = new Content(); Content content = new Content();
when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()), eq(IdValueSource.GENERATED))).thenReturn(23L); when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()),
when(accessStrategy.insert(any(Content.class), eq(Content.class), eq(createBackRef()), eq(IdValueSource.GENERATED))).thenReturn(24L); eq(IdValueSource.GENERATED))).thenReturn(23L);
when(accessStrategy.insert(any(Content.class), eq(Content.class), eq(createBackRef()), eq(IdValueSource.GENERATED)))
.thenReturn(24L);
DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED); DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
executionContext.executeInsertRoot(rootInsert); executionContext.executeInsertRoot(rootInsert);
@ -106,8 +109,10 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
Content content = new Content(); Content content = new Content();
when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()), eq(IdValueSource.GENERATED))).thenReturn(23L); when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()),
when(accessStrategy.insert(eq(content), eq(Content.class), any(Identifier.class), eq(IdValueSource.GENERATED))).thenReturn(24L); eq(IdValueSource.GENERATED))).thenReturn(23L);
when(accessStrategy.insert(eq(content), eq(Content.class), any(Identifier.class), eq(IdValueSource.GENERATED)))
.thenReturn(24L);
DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED); DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
executionContext.executeInsertRoot(rootInsert); executionContext.executeInsertRoot(rootInsert);
@ -124,10 +129,8 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value, DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value,
@Nullable Object key) { @Nullable Object key) {
DbAction.Insert<Object> insert = new DbAction.Insert<>(value, getPersistentPropertyPath(propertyName), parent, return new DbAction.Insert<>(value, getPersistentPropertyPath(propertyName), parent,
key == null ? emptyMap() : singletonMap(toPath(propertyName), key), IdValueSource.GENERATED); key == null ? emptyMap() : singletonMap(toPath(propertyName), key), IdValueSource.GENERATED);
return insert;
} }
PersistentPropertyPathExtension toPathExt(String path) { PersistentPropertyPathExtension toPathExt(String path) {

39
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java

@ -28,9 +28,9 @@ import org.springframework.data.annotation.Version;
import org.springframework.data.jdbc.core.convert.BasicJdbcConverter; import org.springframework.data.jdbc.core.convert.BasicJdbcConverter;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.Identifier; import org.springframework.data.jdbc.core.convert.Identifier;
import org.springframework.data.jdbc.core.convert.InsertSubject;
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder; import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
import org.springframework.data.jdbc.core.convert.InsertSubject;
import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PersistentPropertyPaths; import org.springframework.data.mapping.PersistentPropertyPaths;
import org.springframework.data.relational.core.conversion.DbAction; import org.springframework.data.relational.core.conversion.DbAction;
@ -56,14 +56,13 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
}); });
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class); DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
AggregateChangeExecutor executor = new AggregateChangeExecutor(converter, accessStrategy);
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter, JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
accessStrategy); accessStrategy);
DummyEntity root = new DummyEntity(); DummyEntity root = new DummyEntity();
@Test // DATAJDBC-453 @Test // DATAJDBC-453
public void rootOfEmptySetOfActionsisNull() { public void rootOfEmptySetOfActionIsNull() {
Object root = executionContext.populateIdsIfNecessary(); Object root = executionContext.populateIdsIfNecessary();
@ -92,7 +91,8 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
DummyEntityNonPrimitiveVersion dummyEntityNonPrimitiveVersion = new DummyEntityNonPrimitiveVersion(); DummyEntityNonPrimitiveVersion dummyEntityNonPrimitiveVersion = new DummyEntityNonPrimitiveVersion();
executionContext.executeInsertRoot(new DbAction.InsertRoot<>(dummyEntityNonPrimitiveVersion, IdValueSource.GENERATED)); executionContext
.executeInsertRoot(new DbAction.InsertRoot<>(dummyEntityNonPrimitiveVersion, IdValueSource.GENERATED));
executionContext.populateRootVersionIfNecessary(dummyEntityNonPrimitiveVersion); executionContext.populateRootVersionIfNecessary(dummyEntityNonPrimitiveVersion);
assertThat(dummyEntityNonPrimitiveVersion.version).isEqualTo(0); assertThat(dummyEntityNonPrimitiveVersion.version).isEqualTo(0);
@ -124,7 +124,8 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
Content content = new Content(); Content content = new Content();
when(accessStrategy.insert(root, DummyEntity.class, Identifier.empty(), IdValueSource.GENERATED)).thenReturn(23L); when(accessStrategy.insert(root, DummyEntity.class, Identifier.empty(), IdValueSource.GENERATED)).thenReturn(23L);
when(accessStrategy.insert(eq(content), eq(Content.class), any(Identifier.class), eq(IdValueSource.GENERATED))).thenReturn(24L); when(accessStrategy.insert(eq(content), eq(Content.class), any(Identifier.class), eq(IdValueSource.GENERATED)))
.thenReturn(24L);
DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED); DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
executionContext.executeInsertRoot(rootInsert); executionContext.executeInsertRoot(rootInsert);
@ -147,15 +148,12 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
executionContext.executeInsertRoot(rootInsert); executionContext.executeInsertRoot(rootInsert);
Content content = new Content(); Content content = new Content();
Identifier identifier = Identifier.empty() Identifier identifier = Identifier.empty().withPart(SqlIdentifier.quoted("DUMMY_ENTITY"), 123L, Long.class)
.withPart(SqlIdentifier.quoted("DUMMY_ENTITY"), 123L, Long.class)
.withPart(SqlIdentifier.quoted("DUMMY_ENTITY_KEY"), 0, Integer.class); .withPart(SqlIdentifier.quoted("DUMMY_ENTITY_KEY"), 0, Integer.class);
when(accessStrategy.insert(singletonList(InsertSubject.describedBy(content, identifier)), Content.class, IdValueSource.GENERATED)) when(accessStrategy.insert(singletonList(InsertSubject.describedBy(content, identifier)), Content.class,
.thenReturn(new Object[] { 456L }); IdValueSource.GENERATED)).thenReturn(new Object[] { 456L });
DbAction.InsertBatch<?> insertBatch = new DbAction.InsertBatch<>( DbAction.InsertBatch<?> insertBatch = new DbAction.InsertBatch<>(
singletonList(createInsert(rootInsert, "list", content, 0)), singletonList(createInsert(rootInsert, "list", content, 0)), IdValueSource.GENERATED);
IdValueSource.GENERATED
);
executionContext.executeInsertBatch(insertBatch); executionContext.executeInsertBatch(insertBatch);
DummyEntity newRoot = executionContext.populateIdsIfNecessary(); DummyEntity newRoot = executionContext.populateIdsIfNecessary();
@ -174,15 +172,12 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
executionContext.executeInsertRoot(rootInsert); executionContext.executeInsertRoot(rootInsert);
Content content = new Content(); Content content = new Content();
Identifier identifier = Identifier.empty() Identifier identifier = Identifier.empty().withPart(SqlIdentifier.quoted("DUMMY_ENTITY"), 123L, Long.class)
.withPart(SqlIdentifier.quoted("DUMMY_ENTITY"), 123L, Long.class)
.withPart(SqlIdentifier.quoted("DUMMY_ENTITY_KEY"), 0, Integer.class); .withPart(SqlIdentifier.quoted("DUMMY_ENTITY_KEY"), 0, Integer.class);
when(accessStrategy.insert(singletonList(InsertSubject.describedBy(content, identifier)), Content.class, IdValueSource.PROVIDED)) when(accessStrategy.insert(singletonList(InsertSubject.describedBy(content, identifier)), Content.class,
.thenReturn(new Object[] { null }); IdValueSource.PROVIDED)).thenReturn(new Object[] { null });
DbAction.InsertBatch<?> insertBatch = new DbAction.InsertBatch<>( DbAction.InsertBatch<?> insertBatch = new DbAction.InsertBatch<>(
singletonList(createInsert(rootInsert, "list", content, 0)), singletonList(createInsert(rootInsert, "list", content, 0)), IdValueSource.PROVIDED);
IdValueSource.PROVIDED
);
executionContext.executeInsertBatch(insertBatch); executionContext.executeInsertBatch(insertBatch);
DummyEntity newRoot = executionContext.populateIdsIfNecessary(); DummyEntity newRoot = executionContext.populateIdsIfNecessary();
@ -193,12 +188,10 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
} }
DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value, DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value,
@Nullable Object key) { @Nullable Object key) {
DbAction.Insert<Object> insert = new DbAction.Insert<>(value, getPersistentPropertyPath(propertyName), parent, return new DbAction.Insert<>(value, getPersistentPropertyPath(propertyName), parent,
key == null ? emptyMap() : singletonMap(toPath(propertyName), key), IdValueSource.GENERATED); key == null ? emptyMap() : singletonMap(toPath(propertyName), key), IdValueSource.GENERATED);
return insert;
} }
PersistentPropertyPathExtension toPathExt(String path) { PersistentPropertyPathExtension toPathExt(String path) {

16
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java

@ -19,6 +19,8 @@ import static java.util.Collections.*;
import static org.mockito.ArgumentMatchers.*; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
@ -31,8 +33,6 @@ import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import lombok.RequiredArgsConstructor;
/** /**
* Unit tests for {@link DefaultDataAccessStrategy}. * Unit tests for {@link DefaultDataAccessStrategy}.
* *
@ -72,7 +72,7 @@ public class DefaultDataAccessStrategyUnitTests {
insertStrategyFactory); insertStrategyFactory);
relationResolver.setDelegate(accessStrategy); relationResolver.setDelegate(accessStrategy);
when(sqlParametersFactory.forInsert(any(), any(), any(), any())) when(sqlParametersFactory.forInsert(any(), any(), any(), any()))
.thenReturn(new SqlIdentifierParameterSource(dialect.getIdentifierProcessing())); .thenReturn(new SqlIdentifierParameterSource(dialect.getIdentifierProcessing()));
when(insertStrategyFactory.insertStrategy(any(), any())).thenReturn(mock(InsertStrategy.class)); when(insertStrategyFactory.insertStrategy(any(), any())).thenReturn(mock(InsertStrategy.class));
@ -90,7 +90,8 @@ public class DefaultDataAccessStrategyUnitTests {
@Test // GH-1159 @Test // GH-1159
public void batchInsert() { public void batchInsert() {
accessStrategy.insert(singletonList(InsertSubject.describedBy(new DummyEntity(ORIGINAL_ID), Identifier.empty())), DummyEntity.class, IdValueSource.PROVIDED); accessStrategy.insert(singletonList(InsertSubject.describedBy(new DummyEntity(ORIGINAL_ID), Identifier.empty())),
DummyEntity.class, IdValueSource.PROVIDED);
verify(insertStrategyFactory).batchInsertStrategy(IdValueSource.PROVIDED, SqlIdentifier.quoted("ID")); verify(insertStrategyFactory).batchInsertStrategy(IdValueSource.PROVIDED, SqlIdentifier.quoted("ID"));
} }
@ -98,7 +99,8 @@ public class DefaultDataAccessStrategyUnitTests {
@Test // GH-1159 @Test // GH-1159
public void insertForEntityWithNoId() { public void insertForEntityWithNoId() {
accessStrategy.insert(new DummyEntityWithoutIdAnnotation(ORIGINAL_ID), DummyEntityWithoutIdAnnotation.class, Identifier.empty(), IdValueSource.GENERATED); accessStrategy.insert(new DummyEntityWithoutIdAnnotation(ORIGINAL_ID), DummyEntityWithoutIdAnnotation.class,
Identifier.empty(), IdValueSource.GENERATED);
verify(insertStrategyFactory).insertStrategy(IdValueSource.GENERATED, null); verify(insertStrategyFactory).insertStrategy(IdValueSource.GENERATED, null);
} }
@ -106,7 +108,9 @@ public class DefaultDataAccessStrategyUnitTests {
@Test // GH-1159 @Test // GH-1159
public void batchInsertForEntityWithNoId() { public void batchInsertForEntityWithNoId() {
accessStrategy.insert(singletonList(InsertSubject.describedBy(new DummyEntityWithoutIdAnnotation(ORIGINAL_ID), Identifier.empty())), DummyEntityWithoutIdAnnotation.class, IdValueSource.GENERATED); accessStrategy.insert(
singletonList(InsertSubject.describedBy(new DummyEntityWithoutIdAnnotation(ORIGINAL_ID), Identifier.empty())),
DummyEntityWithoutIdAnnotation.class, IdValueSource.GENERATED);
verify(insertStrategyFactory).batchInsertStrategy(IdValueSource.GENERATED, null); verify(insertStrategyFactory).batchInsertStrategy(IdValueSource.GENERATED, null);
} }

65
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategyTest.java

@ -22,7 +22,6 @@ import static org.mockito.Mockito.*;
import java.util.HashMap; import java.util.HashMap;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.relational.core.dialect.AbstractDialect; import org.springframework.data.relational.core.dialect.AbstractDialect;
import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.IdGeneration; import org.springframework.data.relational.core.dialect.IdGeneration;
@ -45,15 +44,14 @@ class IdGeneratingBatchInsertStrategyTest {
BatchJdbcOperations batchJdbcOperations = mock(BatchJdbcOperations.class); BatchJdbcOperations batchJdbcOperations = mock(BatchJdbcOperations.class);
InsertStrategy insertStrategy = mock(InsertStrategy.class); InsertStrategy insertStrategy = mock(InsertStrategy.class);
String sql = "some sql"; String sql = "some sql";
SqlParameterSource[] sqlParameterSources = new SqlParameterSource[]{new SqlIdentifierParameterSource(identifierProcessing) }; SqlParameterSource[] sqlParameterSources = new SqlParameterSource[] {
new SqlIdentifierParameterSource(identifierProcessing) };
@Test @Test
void insertsSequentially_whenIdGenerationForBatchOperationsNotSupported() { void insertsSequentially_whenIdGenerationForBatchOperationsNotSupported() {
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, true, false), batchJdbcOperations, idColumn);
createDialect(identifierProcessing, true, false),
batchJdbcOperations, idColumn);
SqlIdentifierParameterSource sqlParameterSource1 = new SqlIdentifierParameterSource(identifierProcessing); SqlIdentifierParameterSource sqlParameterSource1 = new SqlIdentifierParameterSource(identifierProcessing);
sqlParameterSource1.addValue(SqlIdentifier.quoted("property1"), "value1"); sqlParameterSource1.addValue(SqlIdentifier.quoted("property1"), "value1");
@ -65,7 +63,8 @@ class IdGeneratingBatchInsertStrategyTest {
long id2 = 2L; long id2 = 2L;
when(insertStrategy.execute(sql, sqlParameterSource2)).thenReturn(id2); when(insertStrategy.execute(sql, sqlParameterSource2)).thenReturn(id2);
Object[] ids = batchInsertStrategy.execute(sql, new SqlParameterSource[]{sqlParameterSource1, sqlParameterSource2}); Object[] ids = batchInsertStrategy.execute(sql,
new SqlParameterSource[] { sqlParameterSource1, sqlParameterSource2 });
assertThat(ids).containsExactly(id1, id2); assertThat(ids).containsExactly(id1, id2);
} }
@ -73,10 +72,8 @@ class IdGeneratingBatchInsertStrategyTest {
@Test @Test
void insertsWithKeyHolderAndKeyColumnNames_whenDriverRequiresKeyColumnNames() { void insertsWithKeyHolderAndKeyColumnNames_whenDriverRequiresKeyColumnNames() {
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, true, true), batchJdbcOperations, idColumn);
createDialect(identifierProcessing, true, true),
batchJdbcOperations, idColumn);
batchInsertStrategy.execute(sql, sqlParameterSources); batchInsertStrategy.execute(sql, sqlParameterSources);
@ -87,10 +84,8 @@ class IdGeneratingBatchInsertStrategyTest {
@Test @Test
void insertsWithKeyHolder_whenDriverRequiresKeyColumnNames_butIdColumnIsNull() { void insertsWithKeyHolder_whenDriverRequiresKeyColumnNames_butIdColumnIsNull() {
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, true, true), batchJdbcOperations, null);
createDialect(identifierProcessing, true, true),
batchJdbcOperations, null);
batchInsertStrategy.execute(sql, sqlParameterSources); batchInsertStrategy.execute(sql, sqlParameterSources);
@ -100,10 +95,8 @@ class IdGeneratingBatchInsertStrategyTest {
@Test @Test
void insertsWithKeyHolder_whenDriverDoesNotRequireKeyColumnNames() { void insertsWithKeyHolder_whenDriverDoesNotRequireKeyColumnNames() {
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, false, true), batchJdbcOperations, idColumn);
createDialect(identifierProcessing, false, true),
batchJdbcOperations, idColumn);
batchInsertStrategy.execute(sql, sqlParameterSources); batchInsertStrategy.execute(sql, sqlParameterSources);
@ -115,16 +108,15 @@ class IdGeneratingBatchInsertStrategyTest {
Long idValue = 123L; Long idValue = 123L;
when(batchJdbcOperations.batchUpdate(any(), any(), any())).thenAnswer(invocationOnMock -> { when(batchJdbcOperations.batchUpdate(any(), any(), any())).thenAnswer(invocationOnMock -> {
KeyHolder keyHolder = invocationOnMock.getArgument(2); KeyHolder keyHolder = invocationOnMock.getArgument(2);
HashMap<String, Object> keys = new HashMap<>(); HashMap<String, Object> keys = new HashMap<>();
keys.put("anything", idValue); keys.put("anything", idValue);
keyHolder.getKeyList().add(keys); keyHolder.getKeyList().add(keys);
return null; return null;
}); });
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, false, true), batchJdbcOperations, idColumn);
createDialect(identifierProcessing, false, true),
batchJdbcOperations, idColumn);
Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources); Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources);
@ -136,6 +128,7 @@ class IdGeneratingBatchInsertStrategyTest {
Long idValue = 123L; Long idValue = 123L;
when(batchJdbcOperations.batchUpdate(any(), any(), any())).thenAnswer(invocationOnMock -> { when(batchJdbcOperations.batchUpdate(any(), any(), any())).thenAnswer(invocationOnMock -> {
KeyHolder keyHolder = invocationOnMock.getArgument(2); KeyHolder keyHolder = invocationOnMock.getArgument(2);
HashMap<String, Object> keys = new HashMap<>(); HashMap<String, Object> keys = new HashMap<>();
keys.put(idColumn.getReference(), idValue); keys.put(idColumn.getReference(), idValue);
@ -143,10 +136,8 @@ class IdGeneratingBatchInsertStrategyTest {
keyHolder.getKeyList().add(keys); keyHolder.getKeyList().add(keys);
return null; return null;
}); });
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, false, true), batchJdbcOperations, idColumn);
createDialect(identifierProcessing, false, true),
batchJdbcOperations, idColumn);
Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources); Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources);
@ -158,6 +149,7 @@ class IdGeneratingBatchInsertStrategyTest {
Long idValue = 123L; Long idValue = 123L;
when(batchJdbcOperations.batchUpdate(any(), any(), any())).thenAnswer(invocationOnMock -> { when(batchJdbcOperations.batchUpdate(any(), any(), any())).thenAnswer(invocationOnMock -> {
KeyHolder keyHolder = invocationOnMock.getArgument(2); KeyHolder keyHolder = invocationOnMock.getArgument(2);
HashMap<String, Object> keys = new HashMap<>(); HashMap<String, Object> keys = new HashMap<>();
keys.put(idColumn.getReference(), idValue); keys.put(idColumn.getReference(), idValue);
@ -165,10 +157,8 @@ class IdGeneratingBatchInsertStrategyTest {
keyHolder.getKeyList().add(keys); keyHolder.getKeyList().add(keys);
return null; return null;
}); });
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, false, true), batchJdbcOperations, null);
createDialect(identifierProcessing, false, true),
batchJdbcOperations, null);
Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources); Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources);
@ -179,10 +169,8 @@ class IdGeneratingBatchInsertStrategyTest {
@Test @Test
void insertsWithKeyHolder_returningNull_whenKeyHolderHasNoKeys() { void insertsWithKeyHolder_returningNull_whenKeyHolderHasNoKeys() {
BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy( BatchInsertStrategy batchInsertStrategy = new IdGeneratingBatchInsertStrategy(insertStrategy,
insertStrategy, createDialect(identifierProcessing, false, true), batchJdbcOperations, idColumn);
createDialect(identifierProcessing, false, true),
batchJdbcOperations, idColumn);
Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources); Object[] ids = batchInsertStrategy.execute(sql, sqlParameterSources);
@ -191,8 +179,7 @@ class IdGeneratingBatchInsertStrategyTest {
} }
private static Dialect createDialect(final IdentifierProcessing identifierProcessing, private static Dialect createDialect(final IdentifierProcessing identifierProcessing,
final boolean requiresKeyColumnNames, final boolean requiresKeyColumnNames, final boolean supportsIdGenerationForBatchOperations) {
final boolean supportsIdGenerationForBatchOperations) {
return new AbstractDialect() { return new AbstractDialect() {
@ -213,7 +200,9 @@ class IdGeneratingBatchInsertStrategyTest {
@Override @Override
public IdGeneration getIdGeneration() { public IdGeneration getIdGeneration() {
return new IdGeneration() { return new IdGeneration() {
@Override @Override
public boolean driverRequiresKeyColumnNames() { public boolean driverRequiresKeyColumnNames() {
return requiresKeyColumnNames; return requiresKeyColumnNames;
@ -227,4 +216,4 @@ class IdGeneratingBatchInsertStrategyTest {
} }
}; };
} }
} }

9
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategyTest.java

@ -45,7 +45,7 @@ class IdGeneratingInsertStrategyTest {
NamedParameterJdbcOperations namedParameterJdbcOperations = mock(NamedParameterJdbcOperations.class); NamedParameterJdbcOperations namedParameterJdbcOperations = mock(NamedParameterJdbcOperations.class);
String sql = "some sql"; String sql = "some sql";
SqlParameterSource sqlParameterSource = new SqlIdentifierParameterSource(identifierProcessing); SqlParameterSource sqlParameterSource = new SqlIdentifierParameterSource(identifierProcessing);
@Test @Test
void insertsWithKeyHolderAndKeyColumnNames_whenDriverRequiresKeyColumnNames() { void insertsWithKeyHolderAndKeyColumnNames_whenDriverRequiresKeyColumnNames() {
@ -85,6 +85,7 @@ class IdGeneratingInsertStrategyTest {
Long idValue = 123L; Long idValue = 123L;
when(namedParameterJdbcOperations.update(any(), any(), any())).thenAnswer(invocationOnMock -> { when(namedParameterJdbcOperations.update(any(), any(), any())).thenAnswer(invocationOnMock -> {
KeyHolder keyHolder = invocationOnMock.getArgument(2); KeyHolder keyHolder = invocationOnMock.getArgument(2);
HashMap<String, Object> keys = new HashMap<>(); HashMap<String, Object> keys = new HashMap<>();
keys.put("anything", idValue); keys.put("anything", idValue);
@ -104,6 +105,7 @@ class IdGeneratingInsertStrategyTest {
Long idValue = 123L; Long idValue = 123L;
when(namedParameterJdbcOperations.update(any(), any(), any())).thenAnswer(invocationOnMock -> { when(namedParameterJdbcOperations.update(any(), any(), any())).thenAnswer(invocationOnMock -> {
KeyHolder keyHolder = invocationOnMock.getArgument(2); KeyHolder keyHolder = invocationOnMock.getArgument(2);
HashMap<String, Object> keys = new HashMap<>(); HashMap<String, Object> keys = new HashMap<>();
keys.put(idColumn.getReference(), idValue); keys.put(idColumn.getReference(), idValue);
@ -124,6 +126,7 @@ class IdGeneratingInsertStrategyTest {
Long idValue = 123L; Long idValue = 123L;
when(namedParameterJdbcOperations.update(any(), any(), any())).thenAnswer(invocationOnMock -> { when(namedParameterJdbcOperations.update(any(), any(), any())).thenAnswer(invocationOnMock -> {
KeyHolder keyHolder = invocationOnMock.getArgument(2); KeyHolder keyHolder = invocationOnMock.getArgument(2);
HashMap<String, Object> keys = new HashMap<>(); HashMap<String, Object> keys = new HashMap<>();
keys.put(idColumn.getReference(), idValue); keys.put(idColumn.getReference(), idValue);
@ -151,7 +154,7 @@ class IdGeneratingInsertStrategyTest {
} }
private static Dialect createDialect(final IdentifierProcessing identifierProcessing, private static Dialect createDialect(final IdentifierProcessing identifierProcessing,
final boolean requiresKeyColumnNames) { final boolean requiresKeyColumnNames) {
return new AbstractDialect() { return new AbstractDialect() {
@ -181,4 +184,4 @@ class IdGeneratingInsertStrategyTest {
} }
}; };
} }
} }

3
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactoryTest.java

@ -55,7 +55,8 @@ class InsertStrategyFactoryTest {
@Test @Test
void batchInsertWithoutGeneratedIds() { void batchInsertWithoutGeneratedIds() {
Object[] ids = insertStrategyFactory.batchInsertStrategy(IdValueSource.GENERATED, null).execute(sql, sqlParameterSources); Object[] ids = insertStrategyFactory.batchInsertStrategy(IdValueSource.GENERATED, null).execute(sql,
sqlParameterSources);
verify(namedParameterJdbcOperations).batchUpdate(sql, sqlParameterSources); verify(namedParameterJdbcOperations).batchUpdate(sql, sqlParameterSources);
assertThat(ids).hasSize(sqlParameterSources.length); assertThat(ids).hasSize(sqlParameterSources.length);

28
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlParametersFactoryTest.java

@ -21,6 +21,11 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategyUnitTests.*; import static org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategyUnitTests.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -37,11 +42,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.Value;
/** /**
* Unit tests for {@link SqlParametersFactory}. * Unit tests for {@link SqlParametersFactory}.
* *
@ -62,7 +62,8 @@ class SqlParametersFactoryTest {
singletonList(IdValueToStringConverter.INSTANCE)); singletonList(IdValueToStringConverter.INSTANCE));
String rawId = "batman"; String rawId = "batman";
SqlIdentifierParameterSource sqlParameterSource = sqlParametersFactory.forQueryById(new IdValue(rawId), WithValueObjectId.class, SqlGenerator.ID_SQL_PARAMETER); SqlIdentifierParameterSource sqlParameterSource = sqlParametersFactory.forQueryById(new IdValue(rawId),
WithValueObjectId.class, SqlGenerator.ID_SQL_PARAMETER);
assertThat(sqlParameterSource.getValue("id")).isEqualTo(rawId); assertThat(sqlParameterSource.getValue("id")).isEqualTo(rawId);
} }
@ -83,7 +84,8 @@ class SqlParametersFactoryTest {
HashMap<SqlIdentifier, Object> additionalParameters = new HashMap<>(); HashMap<SqlIdentifier, Object> additionalParameters = new HashMap<>();
additionalParameters.put(SqlIdentifier.quoted("DUMMYENTITYROOT"), rootIdValue); additionalParameters.put(SqlIdentifier.quoted("DUMMYENTITYROOT"), rootIdValue);
SqlIdentifierParameterSource sqlParameterSource = sqlParametersFactory.forQueryByIdentifier(Identifier.from(additionalParameters)); SqlIdentifierParameterSource sqlParameterSource = sqlParametersFactory
.forQueryByIdentifier(Identifier.from(additionalParameters));
assertThat(sqlParameterSource.getValue("DUMMYENTITYROOT")).isEqualTo(rawId); assertThat(sqlParameterSource.getValue("DUMMYENTITYROOT")).isEqualTo(rawId);
} }
@ -207,14 +209,14 @@ class SqlParametersFactoryTest {
@RequiredArgsConstructor @RequiredArgsConstructor
private static class DummyEntity { private static class DummyEntity {
@Id @Id private final Long id;
private final Long id;
} }
private SqlParametersFactory createSqlParametersFactoryWithConverters(List<?> converters) { private SqlParametersFactory createSqlParametersFactoryWithConverters(List<?> converters) {
BasicJdbcConverter converter = new BasicJdbcConverter(context, relationResolver, new JdbcCustomConversions(converters), BasicJdbcConverter converter = new BasicJdbcConverter(context, relationResolver,
new DefaultJdbcTypeFactory(mock(JdbcOperations.class)), dialect.getIdentifierProcessing()); new JdbcCustomConversions(converters), new DefaultJdbcTypeFactory(mock(JdbcOperations.class)),
dialect.getIdentifierProcessing());
return new SqlParametersFactory(context, converter, dialect); return new SqlParametersFactory(context, converter, dialect);
} }
} }

6
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategyUnitTests.java

@ -29,9 +29,7 @@ import org.mockito.ArgumentCaptor;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.core.PropertyPathTestingUtils; import org.springframework.data.jdbc.core.PropertyPathTestingUtils;
import org.springframework.data.jdbc.core.convert.BasicJdbcConverter;
import org.springframework.data.jdbc.core.convert.Identifier; import org.springframework.data.jdbc.core.convert.Identifier;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.conversion.IdValueSource; import org.springframework.data.relational.core.conversion.IdValueSource;
@ -50,7 +48,6 @@ import org.springframework.data.relational.core.sql.IdentifierProcessing;
public class MyBatisDataAccessStrategyUnitTests { public class MyBatisDataAccessStrategyUnitTests {
RelationalMappingContext context = new JdbcMappingContext(); RelationalMappingContext context = new JdbcMappingContext();
JdbcConverter converter = new BasicJdbcConverter(context, (Identifier, path) -> null);
SqlSession session = mock(SqlSession.class); SqlSession session = mock(SqlSession.class);
ArgumentCaptor<MyBatisContext> captor = ArgumentCaptor.forClass(MyBatisContext.class); ArgumentCaptor<MyBatisContext> captor = ArgumentCaptor.forClass(MyBatisContext.class);
@ -69,7 +66,8 @@ public class MyBatisDataAccessStrategyUnitTests {
@Test // DATAJDBC-123 @Test // DATAJDBC-123
public void insert() { public void insert() {
accessStrategy.insert("x", String.class, Identifier.from(singletonMap(unquoted("key"), "value")), IdValueSource.GENERATED); accessStrategy.insert("x", String.class, Identifier.from(singletonMap(unquoted("key"), "value")),
IdValueSource.GENERATED);
verify(session).insert(eq("java.lang.StringMapper.insert"), captor.capture()); verify(session).insert(eq("java.lang.StringMapper.insert"), captor.capture());

2
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java

@ -23,12 +23,12 @@ import static org.springframework.test.context.TestExecutionListeners.MergeMode.
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import lombok.Data; import lombok.Data;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import lombok.Value;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

6
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java

@ -83,9 +83,11 @@ public class SimpleJdbcRepositoryEventsUnitTests {
new DefaultJdbcTypeFactory(operations.getJdbcOperations()), dialect.getIdentifierProcessing()); new DefaultJdbcTypeFactory(operations.getJdbcOperations()), dialect.getIdentifierProcessing());
SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, converter, dialect); SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, converter, dialect);
SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect); SqlParametersFactory sqlParametersFactory = new SqlParametersFactory(context, converter, dialect);
InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations, new BatchJdbcOperations(operations.getJdbcOperations()), dialect); InsertStrategyFactory insertStrategyFactory = new InsertStrategyFactory(operations,
new BatchJdbcOperations(operations.getJdbcOperations()), dialect);
this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations, sqlParametersFactory, insertStrategyFactory)); this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations,
sqlParametersFactory, insertStrategyFactory));
delegatingDataAccessStrategy.setDelegate(dataAccessStrategy); delegatingDataAccessStrategy.setDelegate(dataAccessStrategy);
doReturn(true).when(dataAccessStrategy).update(any(), any()); doReturn(true).when(dataAccessStrategy).update(any(), any());

8
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

@ -23,7 +23,6 @@ import java.util.Optional;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -96,12 +95,9 @@ public class TestConfiguration {
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context, @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context,
JdbcConverter converter, Dialect dialect) { JdbcConverter converter, Dialect dialect) {
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, converter, dialect), context, converter,
new SqlGeneratorSource(context, converter, dialect), context, converter, template, template, new SqlParametersFactory(context, converter, dialect),
new SqlParametersFactory(context, converter, dialect),
new InsertStrategyFactory(template, new BatchJdbcOperations(template.getJdbcOperations()), dialect)); new InsertStrategyFactory(template, new BatchJdbcOperations(template.getJdbcOperations()), dialect));
return defaultDataAccessStrategy;
} }
@Bean @Bean

24
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-db2.sql

@ -1,26 +1,36 @@
DROP TABLE element; DROP TABLE element;
DROP TABLE dummy_entity; DROP TABLE dummy_entity;
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE element (id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key BIGINT, dummy_entity BIGINT);
DROP TABLE root; DROP TABLE root;
DROP TABLE intermediate; DROP TABLE intermediate;
DROP TABLE leaf; DROP TABLE leaf;
CREATE TABLE dummy_entity
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
content VARCHAR(100),
Dummy_Entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

22
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-h2.sql

@ -1,5 +1,15 @@
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE dummy_entity
CREATE TABLE element (id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key BIGINT, dummy_entity BIGINT); (
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
content VARCHAR(100),
Dummy_Entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
@ -7,13 +17,13 @@ CREATE TABLE root
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

22
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-hsql.sql

@ -1,5 +1,15 @@
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE dummy_entity
CREATE TABLE element (id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key BIGINT, dummy_entity BIGINT); (
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
content VARCHAR(100),
Dummy_Entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
@ -7,13 +17,13 @@ CREATE TABLE root
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

22
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-mariadb.sql

@ -1,5 +1,15 @@
CREATE TABLE dummy_entity ( id BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE dummy_entity
CREATE TABLE element (id BIGINT AUTO_INCREMENT PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key BIGINT,dummy_entity BIGINT); (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
content VARCHAR(100),
Dummy_Entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
@ -7,13 +17,13 @@ CREATE TABLE root
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id BIGINT AUTO_INCREMENT PRIMARY KEY, id BIGINT AUTO_INCREMENT PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

24
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-mssql.sql

@ -1,24 +1,36 @@
DROP TABLE IF EXISTS dummy_entity; DROP TABLE IF EXISTS dummy_entity;
DROP TABLE IF EXISTS element; DROP TABLE IF EXISTS element;
CREATE TABLE dummy_entity ( id BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE element (id BIGINT IDENTITY PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key BIGINT,dummy_entity BIGINT);
DROP TABLE IF EXISTS root; DROP TABLE IF EXISTS root;
DROP TABLE IF EXISTS intermediate; DROP TABLE IF EXISTS intermediate;
DROP TABLE IF EXISTS leaf; DROP TABLE IF EXISTS leaf;
CREATE TABLE dummy_entity
(
id BIGINT IDENTITY PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id BIGINT IDENTITY PRIMARY KEY,
content VARCHAR(100),
Dummy_Entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
id BIGINT IDENTITY PRIMARY KEY id BIGINT IDENTITY PRIMARY KEY
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id BIGINT IDENTITY PRIMARY KEY, id BIGINT IDENTITY PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

22
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-mysql.sql

@ -1,5 +1,15 @@
CREATE TABLE dummy_entity ( id BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE dummy_entity
CREATE TABLE element (id BIGINT AUTO_INCREMENT PRIMARY KEY, content VARCHAR(100), Dummy_Entity_key BIGINT,dummy_entity BIGINT); (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
content VARCHAR(100),
Dummy_Entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
@ -7,13 +17,13 @@ CREATE TABLE root
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id BIGINT AUTO_INCREMENT PRIMARY KEY, id BIGINT AUTO_INCREMENT PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

30
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-oracle.sql

@ -1,35 +1,37 @@
DROP TABLE ELEMENT; DROP TABLE ELEMENT;
DROP TABLE DUMMY_ENTITY; DROP TABLE DUMMY_ENTITY;
CREATE TABLE DUMMY_ENTITY ( DROP TABLE root;
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, DROP TABLE intermediate;
DROP TABLE leaf;
CREATE TABLE DUMMY_ENTITY
(
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
NAME VARCHAR2(100) NAME VARCHAR2(100)
); );
CREATE TABLE ELEMENT ( CREATE TABLE ELEMENT
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, (
CONTENT VARCHAR(100), ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
CONTENT VARCHAR(100),
DUMMY_ENTITY_KEY NUMBER, DUMMY_ENTITY_KEY NUMBER,
DUMMY_ENTITY NUMBER DUMMY_ENTITY NUMBER
); );
DROP TABLE root;
DROP TABLE intermediate;
DROP TABLE leaf;
CREATE TABLE root CREATE TABLE root
( (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
root NUMBER NOT NULL, root NUMBER NOT NULL,
root_key NUMBER NOT NULL root_key NUMBER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate NUMBER NOT NULL, intermediate NUMBER NOT NULL,
intermediate_key NUMBER NOT NULL intermediate_key NUMBER NOT NULL
); );

24
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-postgres.sql

@ -1,24 +1,36 @@
DROP TABLE element; DROP TABLE element;
DROP TABLE dummy_entity; DROP TABLE dummy_entity;
CREATE TABLE dummy_entity ( id SERIAL PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE element (id SERIAL PRIMARY KEY, content VARCHAR(100),dummy_entity_key BIGINT, dummy_entity BIGINT);
DROP TABLE root; DROP TABLE root;
DROP TABLE intermediate; DROP TABLE intermediate;
DROP TABLE leaf; DROP TABLE leaf;
CREATE TABLE dummy_entity
(
id SERIAL PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element
(
id SERIAL PRIMARY KEY,
content VARCHAR(100),
dummy_entity_key BIGINT,
dummy_entity BIGINT
);
CREATE TABLE root CREATE TABLE root
( (
id SERIAL PRIMARY KEY id SERIAL PRIMARY KEY
); );
CREATE TABLE intermediate CREATE TABLE intermediate
( (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
root BIGINT NOT NULL, root BIGINT NOT NULL,
root_key INTEGER NOT NULL root_key INTEGER NOT NULL
); );
CREATE TABLE leaf CREATE TABLE leaf
( (
name VARCHAR(100), name VARCHAR(100),
intermediate BIGINT NOT NULL, intermediate BIGINT NOT NULL,
intermediate_key INTEGER NOT NULL intermediate_key INTEGER NOT NULL
); );

1
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java

@ -110,6 +110,7 @@ public interface DbAction<T> {
private final IdValueSource idValueSource; private final IdValueSource idValueSource;
public InsertRoot(T entity, IdValueSource idValueSource) { public InsertRoot(T entity, IdValueSource idValueSource) {
this.entity = entity; this.entity = entity;
this.idValueSource = idValueSource; this.idValueSource = idValueSource;
} }

23
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java

@ -17,7 +17,11 @@ package org.springframework.data.relational.core.conversion;
import static java.util.Arrays.*; import static java.util.Arrays.*;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPath;
@ -55,7 +59,8 @@ class WritingContext {
this.root = root; this.root = root;
this.entity = aggregateChange.getEntity(); this.entity = aggregateChange.getEntity();
this.entityType = aggregateChange.getEntityType(); this.entityType = aggregateChange.getEntityType();
this.rootIdValueSource = IdValueSource.forInstance(root, context.getRequiredPersistentEntity(aggregateChange.getEntityType())); this.rootIdValueSource = IdValueSource.forInstance(root,
context.getRequiredPersistentEntity(aggregateChange.getEntityType()));
this.paths = context.findPersistentPropertyPaths(entityType, (p) -> p.isEntity() && !p.isEmbedded()); this.paths = context.findPersistentPropertyPaths(entityType, (p) -> p.isEntity() && !p.isEmbedded());
} }
@ -124,7 +129,8 @@ class WritingContext {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private List<? extends DbAction<?>> insertAll(PersistentPropertyPath<RelationalPersistentProperty> path) { private List<? extends DbAction<?>> insertAll(PersistentPropertyPath<RelationalPersistentProperty> path) {
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(path.getRequiredLeafProperty()); RelationalPersistentEntity<?> persistentEntity = context
.getRequiredPersistentEntity(path.getRequiredLeafProperty());
List<DbAction.Insert<Object>> inserts = new ArrayList<>(); List<DbAction.Insert<Object>> inserts = new ArrayList<>();
from(path).forEach(node -> { from(path).forEach(node -> {
@ -150,18 +156,15 @@ class WritingContext {
inserts.add(insert); inserts.add(insert);
previousActions.put(node, insert); previousActions.put(node, insert);
}); });
return inserts.stream() return inserts.stream().collect(Collectors.groupingBy(DbAction.Insert::getIdValueSource)).entrySet().stream()
.collect(Collectors.groupingBy(DbAction.Insert::getIdValueSource)) .filter(entry -> (!entry.getValue().isEmpty())).map(entry -> {
.entrySet().stream()
.filter(entry -> (!entry.getValue().isEmpty()))
.map(entry -> {
List<DbAction.Insert<Object>> batch = entry.getValue(); List<DbAction.Insert<Object>> batch = entry.getValue();
if (batch.size() > 1) { if (batch.size() > 1) {
return new DbAction.InsertBatch<>(batch, entry.getKey()); return new DbAction.InsertBatch<>(batch, entry.getKey());
} }
return batch.get(0); return batch.get(0);
}) }).collect(Collectors.toList());
.collect(Collectors.toList());
} }
private List<DbAction<?>> deleteReferenced() { private List<DbAction<?>> deleteReferenced() {

1
spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java

@ -37,6 +37,7 @@ public class SqlServerDialect extends AbstractDialect {
public static final SqlServerDialect INSTANCE = new SqlServerDialect(); public static final SqlServerDialect INSTANCE = new SqlServerDialect();
private static final IdGeneration ID_GENERATION = new IdGeneration() { private static final IdGeneration ID_GENERATION = new IdGeneration() {
@Override @Override
public boolean supportedForBatchOperations() { public boolean supportedForBatchOperations() {
return false; return false;

3
spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DbActionTestSupport.java

@ -16,10 +16,12 @@
package org.springframework.data.relational.core.conversion; package org.springframework.data.relational.core.conversion;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
* Utility class for analyzing DbActions in tests. * Utility class for analyzing DbActions in tests.
*
* @author Jens Schauder * @author Jens Schauder
* @author Chirag Tailor * @author Chirag Tailor
*/ */
@ -49,6 +51,7 @@ class DbActionTestSupport {
@Nullable @Nullable
static IdValueSource insertIdValueSource(DbAction<?> action) { static IdValueSource insertIdValueSource(DbAction<?> action) {
if (action instanceof DbAction.InsertRoot) { if (action instanceof DbAction.InsertRoot) {
return ((DbAction.InsertRoot<?>) action).getIdValueSource(); return ((DbAction.InsertRoot<?>) action).getIdValueSource();
} else if (action instanceof DbAction.Insert) { } else if (action instanceof DbAction.Insert) {

168
spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java

@ -17,6 +17,9 @@ package org.springframework.data.relational.core.conversion;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -43,9 +46,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import lombok.Data;
import lombok.RequiredArgsConstructor;
/** /**
* Unit tests for the {@link RelationalEntityWriter} * Unit tests for the {@link RelationalEntityWriter}
* *
@ -97,7 +97,8 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false, IdValueSource.GENERATED) // tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false,
IdValueSource.GENERATED) //
); );
} }
@ -118,7 +119,8 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, PrimitiveLongIdEntity.class, "", PrimitiveLongIdEntity.class, false, IdValueSource.GENERATED) // tuple(InsertRoot.class, PrimitiveLongIdEntity.class, "", PrimitiveLongIdEntity.class, false,
IdValueSource.GENERATED) //
); );
} }
@ -139,7 +141,8 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, PrimitiveIntIdEntity.class, "", PrimitiveIntIdEntity.class, false, IdValueSource.GENERATED) // tuple(InsertRoot.class, PrimitiveIntIdEntity.class, "", PrimitiveIntIdEntity.class, false,
IdValueSource.GENERATED) //
); );
} }
@ -162,7 +165,8 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, EmbeddedReferenceEntity.class, "", EmbeddedReferenceEntity.class, false, IdValueSource.GENERATED) // tuple(InsertRoot.class, EmbeddedReferenceEntity.class, "", EmbeddedReferenceEntity.class, false,
IdValueSource.GENERATED) //
); );
} }
@ -185,7 +189,8 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false, IdValueSource.GENERATED), // tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false,
IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, "other", Element.class, true, IdValueSource.GENERATED) // tuple(Insert.class, Element.class, "other", Element.class, true, IdValueSource.GENERATED) //
); );
} }
@ -210,9 +215,12 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactlyInAnyOrder( // .containsExactlyInAnyOrder( //
tuple(InsertRoot.class, EntityWithReferencesToPrimitiveIdEntity.class, "", EntityWithReferencesToPrimitiveIdEntity.class, false, IdValueSource.GENERATED), // tuple(InsertRoot.class, EntityWithReferencesToPrimitiveIdEntity.class, "",
tuple(Insert.class, PrimitiveLongIdEntity.class, "primitiveLongIdEntity", PrimitiveLongIdEntity.class, true, IdValueSource.GENERATED), // EntityWithReferencesToPrimitiveIdEntity.class, false, IdValueSource.GENERATED), //
tuple(Insert.class, PrimitiveIntIdEntity.class, "primitiveIntIdEntity", PrimitiveIntIdEntity.class, true, IdValueSource.GENERATED) // tuple(Insert.class, PrimitiveLongIdEntity.class, "primitiveLongIdEntity", PrimitiveLongIdEntity.class, true,
IdValueSource.GENERATED), //
tuple(Insert.class, PrimitiveIntIdEntity.class, "primitiveIntIdEntity", PrimitiveIntIdEntity.class, true,
IdValueSource.GENERATED) //
); );
} }
@ -307,11 +315,11 @@ public class RelationalEntityWriterUnitTests {
); );
List<Insert<Element>> batchedInsertActions = getInsertBatchAction(actions, Element.class).getInserts(); List<Insert<Element>> batchedInsertActions = getInsertBatchAction(actions, Element.class).getInserts();
assertThat(batchedInsertActions).extracting(DbAction::getClass, // assertThat(batchedInsertActions).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, Element.class, "elements", Element.class, true, IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "elements", Element.class, true, IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, "elements", Element.class, true, IdValueSource.GENERATED) // tuple(Insert.class, Element.class, "elements", Element.class, true, IdValueSource.GENERATED) //
@ -346,17 +354,19 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false, IdValueSource.GENERATED), // tuple(InsertRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false,
IdValueSource.GENERATED), //
tuple(InsertBatch.class, CascadingReferenceMiddleElement.class, "", null, false, IdValueSource.GENERATED), // tuple(InsertBatch.class, CascadingReferenceMiddleElement.class, "", null, false, IdValueSource.GENERATED), //
tuple(InsertBatch.class, Element.class, "", null, false, IdValueSource.GENERATED) // tuple(InsertBatch.class, Element.class, "", null, false, IdValueSource.GENERATED) //
); );
List<Insert<CascadingReferenceMiddleElement>> middleElementInserts = getInsertBatchAction(actions, CascadingReferenceMiddleElement.class).getInserts(); List<Insert<CascadingReferenceMiddleElement>> middleElementInserts = getInsertBatchAction(actions,
CascadingReferenceMiddleElement.class).getInserts();
assertThat(middleElementInserts).extracting(DbAction::getClass, // assertThat(middleElementInserts).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class, tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class,
true, IdValueSource.GENERATED), // true, IdValueSource.GENERATED), //
@ -365,11 +375,11 @@ public class RelationalEntityWriterUnitTests {
); );
List<Insert<Element>> leafElementInserts = getInsertBatchAction(actions, Element.class).getInserts(); List<Insert<Element>> leafElementInserts = getInsertBatchAction(actions, Element.class).getInserts();
assertThat(leafElementInserts).extracting(DbAction::getClass, // assertThat(leafElementInserts).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), //
@ -415,11 +425,11 @@ public class RelationalEntityWriterUnitTests {
List<Insert<CascadingReferenceMiddleElement>> middleElementInserts = getInsertBatchAction(actions, List<Insert<CascadingReferenceMiddleElement>> middleElementInserts = getInsertBatchAction(actions,
CascadingReferenceMiddleElement.class).getInserts(); CascadingReferenceMiddleElement.class).getInserts();
assertThat(middleElementInserts).extracting(DbAction::getClass, // assertThat(middleElementInserts).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class, tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class,
true, IdValueSource.GENERATED), // true, IdValueSource.GENERATED), //
@ -428,11 +438,11 @@ public class RelationalEntityWriterUnitTests {
); );
List<Insert<Element>> elementInserts = getInsertBatchAction(actions, Element.class).getInserts(); List<Insert<Element>> elementInserts = getInsertBatchAction(actions, Element.class).getInserts();
assertThat(elementInserts).extracting(DbAction::getClass, // assertThat(elementInserts).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "other.element", Element.class, true, IdValueSource.GENERATED), //
@ -481,10 +491,10 @@ public class RelationalEntityWriterUnitTests {
); );
List<Insert<Element>> inserts = getInsertBatchAction(actions, Element.class).getInserts(); List<Insert<Element>> inserts = getInsertBatchAction(actions, Element.class).getInserts();
assertThat(inserts).extracting(DbAction::getClass, // assertThat(inserts).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
this::getMapKey, // this::getMapKey, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactlyInAnyOrder( // .containsExactlyInAnyOrder( //
tuple(Insert.class, Element.class, "one", "elements", IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "one", "elements", IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, "two", "elements", IdValueSource.GENERATED) // tuple(Insert.class, Element.class, "two", "elements", IdValueSource.GENERATED) //
@ -525,10 +535,10 @@ public class RelationalEntityWriterUnitTests {
); );
List<Insert<Element>> inserts = getInsertBatchAction(actions, Element.class).getInserts(); List<Insert<Element>> inserts = getInsertBatchAction(actions, Element.class).getInserts();
assertThat(inserts).extracting(DbAction::getClass, // assertThat(inserts).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
this::getMapKey, // this::getMapKey, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactlyInAnyOrder( // .containsExactlyInAnyOrder( //
tuple(Insert.class, Element.class, "1", "elements", IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "1", "elements", IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, "2", "elements", IdValueSource.GENERATED), // tuple(Insert.class, Element.class, "2", "elements", IdValueSource.GENERATED), //
@ -718,7 +728,8 @@ public class RelationalEntityWriterUnitTests {
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(InsertRoot.class, EmbeddedReferenceChainEntity.class, "", EmbeddedReferenceChainEntity.class, false, IdValueSource.GENERATED) // tuple(InsertRoot.class, EmbeddedReferenceChainEntity.class, "", EmbeddedReferenceChainEntity.class, false,
IdValueSource.GENERATED) //
); );
} }
@ -764,35 +775,34 @@ public class RelationalEntityWriterUnitTests {
List<DbAction<?>> actions = extractActions(aggregateChange); List<DbAction<?>> actions = extractActions(aggregateChange);
assertThat(actions).extracting(DbAction::getClass, // assertThat(actions).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsSubsequence( .containsSubsequence(
tuple(InsertRoot.class, ListContainer.class, "", ListContainer.class, false, IdValueSource.GENERATED), // tuple(InsertRoot.class, ListContainer.class, "", ListContainer.class, false, IdValueSource.GENERATED), //
tuple(InsertBatch.class, Element.class, "", null, false, IdValueSource.PROVIDED) // tuple(InsertBatch.class, Element.class, "", null, false, IdValueSource.PROVIDED) //
) ).containsSubsequence( //
.containsSubsequence( //
tuple(InsertRoot.class, ListContainer.class, "", ListContainer.class, false, IdValueSource.GENERATED), // tuple(InsertRoot.class, ListContainer.class, "", ListContainer.class, false, IdValueSource.GENERATED), //
tuple(InsertBatch.class, Element.class, "", null, false, IdValueSource.GENERATED) // tuple(InsertBatch.class, Element.class, "", null, false, IdValueSource.GENERATED) //
); );
InsertBatch<Element> insertBatchWithoutId = getInsertBatchAction(actions, Element.class, IdValueSource.GENERATED); InsertBatch<Element> insertBatchWithoutId = getInsertBatchAction(actions, Element.class, IdValueSource.GENERATED);
assertThat(insertBatchWithoutId.getInserts()).extracting(DbAction::getClass, // assertThat(insertBatchWithoutId.getInserts()).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
this::getListKey, // this::getListKey, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, Element.class, 0, "elements", IdValueSource.GENERATED), // tuple(Insert.class, Element.class, 0, "elements", IdValueSource.GENERATED), //
tuple(Insert.class, Element.class, 2, "elements", IdValueSource.GENERATED) // tuple(Insert.class, Element.class, 2, "elements", IdValueSource.GENERATED) //
); );
InsertBatch<Element> insertBatchWithId = getInsertBatchAction(actions, Element.class, IdValueSource.PROVIDED); InsertBatch<Element> insertBatchWithId = getInsertBatchAction(actions, Element.class, IdValueSource.PROVIDED);
assertThat(insertBatchWithId.getInserts()).extracting(DbAction::getClass, // assertThat(insertBatchWithId.getInserts()).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
this::getListKey, // this::getListKey, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactly( // .containsExactly( //
tuple(Insert.class, Element.class, 1, "elements", IdValueSource.PROVIDED), // tuple(Insert.class, Element.class, 1, "elements", IdValueSource.PROVIDED), //
tuple(Insert.class, Element.class, 3, "elements", IdValueSource.PROVIDED) // tuple(Insert.class, Element.class, 3, "elements", IdValueSource.PROVIDED) //
@ -813,15 +823,18 @@ public class RelationalEntityWriterUnitTests {
List<DbAction<?>> actions = extractActions(aggregateChange); List<DbAction<?>> actions = extractActions(aggregateChange);
assertThat(actions).extracting(DbAction::getClass, // assertThat(actions).extracting(DbAction::getClass, //
DbAction::getEntityType, // DbAction::getEntityType, //
DbActionTestSupport::extractPath, // DbActionTestSupport::extractPath, //
DbActionTestSupport::actualEntityType, // DbActionTestSupport::actualEntityType, //
DbActionTestSupport::isWithDependsOn, // DbActionTestSupport::isWithDependsOn, //
DbActionTestSupport::insertIdValueSource) // DbActionTestSupport::insertIdValueSource) //
.containsExactlyInAnyOrder( // .containsExactlyInAnyOrder( //
tuple(InsertRoot.class, EntityWithReferencesToPrimitiveIdEntity.class, "", EntityWithReferencesToPrimitiveIdEntity.class, false, IdValueSource.GENERATED), // tuple(InsertRoot.class, EntityWithReferencesToPrimitiveIdEntity.class, "",
tuple(Insert.class, PrimitiveLongIdEntity.class, "primitiveLongIdEntities", PrimitiveLongIdEntity.class, true, IdValueSource.GENERATED), // EntityWithReferencesToPrimitiveIdEntity.class, false, IdValueSource.GENERATED), //
tuple(Insert.class, PrimitiveIntIdEntity.class, "primitiveIntIdEntities", PrimitiveIntIdEntity.class, true, IdValueSource.GENERATED) // tuple(Insert.class, PrimitiveLongIdEntity.class, "primitiveLongIdEntities", PrimitiveLongIdEntity.class,
true, IdValueSource.GENERATED), //
tuple(Insert.class, PrimitiveIntIdEntity.class, "primitiveIntIdEntities", PrimitiveIntIdEntity.class, true,
IdValueSource.GENERATED) //
); );
} }
@ -834,28 +847,25 @@ public class RelationalEntityWriterUnitTests {
@NotNull @NotNull
private <T> InsertBatch<T> getInsertBatchAction(List<DbAction<?>> actions, Class<T> entityType) { private <T> InsertBatch<T> getInsertBatchAction(List<DbAction<?>> actions, Class<T> entityType) {
return getInsertBatchActions(actions, entityType).stream() return getInsertBatchActions(actions, entityType).stream().findFirst()
.findFirst()
.orElseThrow(() -> new RuntimeException("No InsertBatch action found!")); .orElseThrow(() -> new RuntimeException("No InsertBatch action found!"));
} }
@NotNull @NotNull
private <T> InsertBatch<T> getInsertBatchAction(List<DbAction<?>> actions, Class<T> entityType, private <T> InsertBatch<T> getInsertBatchAction(List<DbAction<?>> actions, Class<T> entityType,
IdValueSource idValueSource) { IdValueSource idValueSource) {
return getInsertBatchActions(actions, entityType).stream() return getInsertBatchActions(actions, entityType).stream()
.filter(insertBatch -> insertBatch.getIdValueSource() == idValueSource) .filter(insertBatch -> insertBatch.getIdValueSource() == idValueSource).findFirst().orElseThrow(
.findFirst() () -> new RuntimeException(String.format("No InsertBatch with includeId '%s' found!", idValueSource)));
.orElseThrow(() -> new RuntimeException(String.format("No InsertBatch with includeId '%s' found!", idValueSource)));
} }
@NotNull @NotNull
private <T> List<InsertBatch<T>> getInsertBatchActions(List<DbAction<?>> actions, Class<T> entityType) { private <T> List<InsertBatch<T>> getInsertBatchActions(List<DbAction<?>> actions, Class<T> entityType) {
//noinspection unchecked // noinspection unchecked
return actions.stream() // return actions.stream() //
.filter(dbAction -> dbAction instanceof InsertBatch) // .filter(dbAction -> dbAction instanceof InsertBatch) //
.filter(dbAction -> dbAction.getEntityType().equals(entityType)) // .filter(dbAction -> dbAction.getEntityType().equals(entityType)) //
.map(dbAction -> (InsertBatch<T>) dbAction) .map(dbAction -> (InsertBatch<T>) dbAction).collect(Collectors.toList());
.collect(Collectors.toList());
} }
private CascadingReferenceMiddleElement createMiddleElement(Element first, Element second) { private CascadingReferenceMiddleElement createMiddleElement(Element first, Element second) {

Loading…
Cancel
Save