Browse Source

Omit Sequence value generation when identifier value is provided or the entity is not new.

Signed-off-by: mipo256 <mikhailpolivakha@gmail.com>

Closes #2003
Original pull request: #2005
pull/2034/head
mipo256 10 months ago committed by Mark Paluch
parent
commit
0fc3187916
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 1
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java
  2. 47
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java
  3. 3
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/AbstractJdbcRepositoryLookUpStrategyTests.java
  4. 184
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java
  5. 52
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java
  6. 8
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-db2.sql
  7. 7
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-h2.sql
  8. 7
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-hsql.sql
  9. 6
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mariadb.sql
  10. 11
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mssql.sql
  11. 6
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mysql.sql
  12. 21
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-oracle.sql
  13. 9
      spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-postgres.sql

1
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java

@ -557,7 +557,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
} }
private <T> RootAggregateChange<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) { private <T> RootAggregateChange<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
RootAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity, RootAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
entityAndVersion.version); entityAndVersion.version);
new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity, aggregateChange); new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity, aggregateChange);

47
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java

@ -2,6 +2,7 @@ package org.springframework.data.jdbc.core.mapping;
import java.util.Map; import java.util.Map;
import java.util.Optional; 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.data.jdbc.repository.config.AbstractJdbcConfiguration; import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
@ -16,8 +17,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Callback for generating ID via the database sequence. By default, it is registered as a * Callback for generating ID via the database sequence. By default, it is registered as a bean in
* bean in {@link AbstractJdbcConfiguration} * {@link AbstractJdbcConfiguration}
* *
* @author Mikhail Polivakha * @author Mikhail Polivakha
*/ */
@ -29,11 +30,8 @@ public class IdGeneratingBeforeSaveCallback implements BeforeSaveCallback<Object
private final Dialect dialect; private final Dialect dialect;
private final NamedParameterJdbcOperations operations; private final NamedParameterJdbcOperations operations;
public IdGeneratingBeforeSaveCallback( public IdGeneratingBeforeSaveCallback(RelationalMappingContext relationalMappingContext, Dialect dialect,
RelationalMappingContext relationalMappingContext, NamedParameterJdbcOperations namedParameterJdbcOperations) {
Dialect dialect,
NamedParameterJdbcOperations namedParameterJdbcOperations
) {
this.relationalMappingContext = relationalMappingContext; this.relationalMappingContext = relationalMappingContext;
this.dialect = dialect; this.dialect = dialect;
this.operations = namedParameterJdbcOperations; this.operations = namedParameterJdbcOperations;
@ -45,27 +43,44 @@ public class IdGeneratingBeforeSaveCallback implements BeforeSaveCallback<Object
Assert.notNull(aggregate, "The aggregate cannot be null at this point"); Assert.notNull(aggregate, "The aggregate cannot be null at this point");
RelationalPersistentEntity<?> persistentEntity = relationalMappingContext.getPersistentEntity(aggregate.getClass()); RelationalPersistentEntity<?> persistentEntity = relationalMappingContext.getPersistentEntity(aggregate.getClass());
if (!persistentEntity.hasIdProperty()) {
return aggregate;
}
// we're doing INSERT and ID property value is not set explicitly by client
if (persistentEntity.isNew(aggregate) && !hasIdentifierValue(aggregate, persistentEntity)) {
return potentiallyFetchIdFromSequence(aggregate, persistentEntity);
} else {
return aggregate;
}
}
private boolean hasIdentifierValue(Object aggregate, RelationalPersistentEntity<?> persistentEntity) {
Object identifier = persistentEntity.getIdentifierAccessor(aggregate).getIdentifier();
if (persistentEntity.getIdProperty().getType().isPrimitive()) {
return identifier instanceof Number num && num.longValue() != 0L;
} else {
return identifier != null;
}
}
private Object potentiallyFetchIdFromSequence(Object aggregate, RelationalPersistentEntity<?> persistentEntity) {
Optional<SqlIdentifier> idSequence = persistentEntity.getIdSequence(); Optional<SqlIdentifier> idSequence = persistentEntity.getIdSequence();
if (dialect.getIdGeneration().sequencesSupported()) { if (dialect.getIdGeneration().sequencesSupported()) {
idSequence.map(s -> dialect.getIdGeneration().createSequenceQuery(s)).ifPresent(sql -> {
if (persistentEntity.getIdProperty() != null) {
idSequence
.map(s -> dialect.getIdGeneration().createSequenceQuery(s))
.ifPresent(sql -> {
Long idValue = operations.queryForObject(sql, Map.of(), (rs, rowNum) -> rs.getLong(1)); Long idValue = operations.queryForObject(sql, Map.of(), (rs, rowNum) -> rs.getLong(1));
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(aggregate); PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(aggregate);
propertyAccessor.setProperty(persistentEntity.getRequiredIdProperty(), idValue); propertyAccessor.setProperty(persistentEntity.getRequiredIdProperty(), idValue);
}); });
}
} else { } else {
if (idSequence.isPresent()) { if (idSequence.isPresent()) {
LOG.warn(""" LOG.warn("""
It seems you're trying to insert an aggregate of type '%s' annotated with @TargetSequence, but the problem is RDBMS you're It seems you're trying to insert an aggregate of type '%s' annotated with @TargetSequence, but the problem is RDBMS you're
working with does not support sequences as such. Falling back to identity columns working with does not support sequences as such. Falling back to identity columns
""" """.formatted(aggregate.getClass().getName()));
.formatted(aggregate.getClass().getName())
);
} }
} }

3
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/AbstractJdbcRepositoryLookUpStrategyTests.java

@ -28,6 +28,7 @@ import org.springframework.data.jdbc.testing.DatabaseType;
import org.springframework.data.jdbc.testing.EnabledOnDatabase; import org.springframework.data.jdbc.testing.EnabledOnDatabase;
import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
/** /**
@ -40,7 +41,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
abstract class AbstractJdbcRepositoryLookUpStrategyTests { abstract class AbstractJdbcRepositoryLookUpStrategyTests {
@Autowired protected OnesRepository onesRepository; @Autowired protected OnesRepository onesRepository;
@Autowired NamedParameterJdbcTemplate template; @Autowired NamedParameterJdbcOperations template;
@Autowired RelationalMappingContext context; @Autowired RelationalMappingContext context;
void insertTestInstances() { void insertTestInstances() {

184
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java

@ -15,13 +15,14 @@
*/ */
package org.springframework.data.jdbc.repository; package org.springframework.data.jdbc.repository;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
@ -29,31 +30,53 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceCreator;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
import org.springframework.data.jdbc.core.mapping.IdGeneratingBeforeSaveCallback;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.IntegrationTest;
import org.springframework.data.jdbc.testing.TestConfiguration; import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.Sequence;
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback; import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.ListCrudRepository; import org.springframework.data.repository.ListCrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.test.context.jdbc.Sql;
/** /**
* Testing special cases for id generation with {@link SimpleJdbcRepository}. * Testing special cases for id generation with {@link SimpleJdbcRepository}.
* *
* @author Jens Schauder * @author Jens Schauder
* @author Greg Turnquist * @author Greg Turnquist
* @author Mikhail Polivakha
*/ */
@IntegrationTest @IntegrationTest
class JdbcRepositoryIdGenerationIntegrationTests { class JdbcRepositoryIdGenerationIntegrationTests {
@Autowired NamedParameterJdbcTemplate template; @Autowired
@Autowired ReadOnlyIdEntityRepository readOnlyIdRepository; ReadOnlyIdEntityRepository readOnlyIdRepository;
@Autowired PrimitiveIdEntityRepository primitiveIdRepository; @Autowired
@Autowired ImmutableWithManualIdEntityRepository immutableWithManualIdEntityRepository; PrimitiveIdEntityRepository primitiveIdRepository;
@Autowired
ImmutableWithManualIdEntityRepository immutableWithManualIdEntityRepository;
@Test // DATAJDBC-98 @Autowired
SimpleSeqRepository simpleSeqRepository;
@Autowired
PersistableSeqRepository persistableSeqRepository;
@Autowired
PrimitiveIdSeqRepository primitiveIdSeqRepository;
@Autowired
IdGeneratingBeforeSaveCallback idGeneratingCallback;
@Test
// DATAJDBC-98
void idWithoutSetterGetsSet() { void idWithoutSetterGetsSet() {
ReadOnlyIdEntity entity = readOnlyIdRepository.save(new ReadOnlyIdEntity(null, "Entity Name")); ReadOnlyIdEntity entity = readOnlyIdRepository.save(new ReadOnlyIdEntity(null, "Entity Name"));
@ -67,7 +90,8 @@ class JdbcRepositoryIdGenerationIntegrationTests {
}); });
} }
@Test // DATAJDBC-98 @Test
// DATAJDBC-98
void primitiveIdGetsSet() { void primitiveIdGetsSet() {
PrimitiveIdEntity entity = new PrimitiveIdEntity(); PrimitiveIdEntity entity = new PrimitiveIdEntity();
@ -84,7 +108,8 @@ class JdbcRepositoryIdGenerationIntegrationTests {
}); });
} }
@Test // DATAJDBC-393 @Test
// DATAJDBC-393
void manuallyGeneratedId() { void manuallyGeneratedId() {
ImmutableWithManualIdEntity entity = new ImmutableWithManualIdEntity(null, "immutable"); ImmutableWithManualIdEntity entity = new ImmutableWithManualIdEntity(null, "immutable");
@ -95,7 +120,8 @@ class JdbcRepositoryIdGenerationIntegrationTests {
assertThat(immutableWithManualIdEntityRepository.findAll()).hasSize(1); assertThat(immutableWithManualIdEntityRepository.findAll()).hasSize(1);
} }
@Test // DATAJDBC-393 @Test
// DATAJDBC-393
void manuallyGeneratedIdForSaveAll() { void manuallyGeneratedIdForSaveAll() {
ImmutableWithManualIdEntity one = new ImmutableWithManualIdEntity(null, "one"); ImmutableWithManualIdEntity one = new ImmutableWithManualIdEntity(null, "one");
@ -107,18 +133,146 @@ class JdbcRepositoryIdGenerationIntegrationTests {
assertThat(immutableWithManualIdEntityRepository.findAll()).hasSize(2); assertThat(immutableWithManualIdEntityRepository.findAll()).hasSize(2);
} }
private interface PrimitiveIdEntityRepository extends ListCrudRepository<PrimitiveIdEntity, Long> {} @Test // DATAJDBC-2003
@Sql(statements = "INSERT INTO SimpleSeq(id, name) VALUES(1, 'initial value');")
void testUpdateAggregateWithSequence() {
SimpleSeq entity = new SimpleSeq();
entity.id = 1L;
entity.name = "New name";
AtomicReference<SimpleSeq> afterCallback = mockIdGeneratingCallback(entity);
SimpleSeq updated = simpleSeqRepository.save(entity);
assertThat(updated.id).isEqualTo(1L);
assertThat(afterCallback.get()).isSameAs(entity);
assertThat(afterCallback.get().id).isEqualTo(1L);
}
@Test
// DATAJDBC-2003
void testInsertPersistableAggregateWithSequenceClientIdIsFavored() {
long initialId = 1L;
PersistableSeq entityWithSeq = PersistableSeq.createNew(initialId, "name");
AtomicReference<PersistableSeq> afterCallback = mockIdGeneratingCallback(entityWithSeq);
PersistableSeq saved = persistableSeqRepository.save(entityWithSeq);
// We do not expect the SELECT next value from sequence in case we're doing an INSERT with ID provided by the client
assertThat(saved.getId()).isEqualTo(initialId);
assertThat(afterCallback.get()).isSameAs(entityWithSeq);
}
@Test
// DATAJDBC-2003
void testInsertAggregateWithSequenceAndUnsetPrimitiveId() {
PrimitiveIdSeq entity = new PrimitiveIdSeq();
entity.name = "some name";
AtomicReference<PrimitiveIdSeq> afterCallback = mockIdGeneratingCallback(entity);
private interface ReadOnlyIdEntityRepository extends ListCrudRepository<ReadOnlyIdEntity, Long> {} PrimitiveIdSeq saved = primitiveIdSeqRepository.save(entity);
// 1. Select from sequence
// 2. Actual INSERT
assertThat(afterCallback.get().id).isEqualTo(1L);
assertThat(saved.id).isEqualTo(1L); // sequence starts with 1
}
@SuppressWarnings("unchecked")
private <T> AtomicReference<T> mockIdGeneratingCallback(T entity) {
AtomicReference<T> afterCallback = new AtomicReference<>();
Mockito
.doAnswer(invocationOnMock -> {
afterCallback.set((T) invocationOnMock.callRealMethod());
return afterCallback.get();
})
.when(idGeneratingCallback)
.onBeforeSave(Mockito.eq(entity), Mockito.any(MutableAggregateChange.class));
return afterCallback;
}
interface PrimitiveIdEntityRepository extends ListCrudRepository<PrimitiveIdEntity, Long> {
}
interface ReadOnlyIdEntityRepository extends ListCrudRepository<ReadOnlyIdEntity, Long> {
}
interface ImmutableWithManualIdEntityRepository extends ListCrudRepository<ImmutableWithManualIdEntity, Long> {
}
interface SimpleSeqRepository extends ListCrudRepository<SimpleSeq, Long> {
}
interface PersistableSeqRepository extends ListCrudRepository<PersistableSeq, Long> {
}
private interface ImmutableWithManualIdEntityRepository extends ListCrudRepository<ImmutableWithManualIdEntity, Long> {} interface PrimitiveIdSeqRepository extends ListCrudRepository<PrimitiveIdSeq, Long> {
}
record ReadOnlyIdEntity(@Id Long id, String name) { record ReadOnlyIdEntity(@Id Long id, String name) {
} }
static class SimpleSeq {
@Id
@Sequence(value = "simple_seq_seq")
private Long id;
private String name;
}
static class PersistableSeq implements Persistable<Long> {
@Id
@Sequence(value = "persistable_seq_seq")
private Long id;
private String name;
@Transient
private boolean isNew;
@PersistenceCreator
public PersistableSeq() {
}
public PersistableSeq(Long id, String name, boolean isNew) {
this.id = id;
this.name = name;
this.isNew = isNew;
}
static PersistableSeq createNew(Long id, String name) {
return new PersistableSeq(id, name, true);
}
@Override
public Long getId() {
return id;
}
@Override
public boolean isNew() {
return isNew;
}
}
static class PrimitiveIdSeq {
@Id
@Sequence(value = "primitive_seq_seq")
private long id;
private String name;
}
static class PrimitiveIdEntity { static class PrimitiveIdEntity {
@Id private long id; @Id
private long id;
String name; String name;
public long getId() { public long getId() {

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

@ -23,6 +23,7 @@ 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.mockito.Mockito;
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;
@ -68,6 +69,7 @@ import org.springframework.transaction.PlatformTransactionManager;
* @author Christoph Strobl * @author Christoph Strobl
* @author Chirag Tailor * @author Chirag Tailor
* @author Christopher Klein * @author Christopher Klein
* @author Mikhail Polivakha
*/ */
@Configuration @Configuration
@ComponentScan // To pick up configuration classes (per activated profile) @ComponentScan // To pick up configuration classes (per activated profile)
@ -76,24 +78,25 @@ public class TestConfiguration {
public static final String PROFILE_SINGLE_QUERY_LOADING = "singleQueryLoading"; public static final String PROFILE_SINGLE_QUERY_LOADING = "singleQueryLoading";
public static final String PROFILE_NO_SINGLE_QUERY_LOADING = "!" + PROFILE_SINGLE_QUERY_LOADING; public static final String PROFILE_NO_SINGLE_QUERY_LOADING = "!" + PROFILE_SINGLE_QUERY_LOADING;
@Autowired DataSource dataSource; @Autowired
@Autowired BeanFactory beanFactory; DataSource dataSource;
@Autowired ApplicationEventPublisher publisher; @Autowired
@Autowired(required = false) SqlSessionFactory sqlSessionFactory; BeanFactory beanFactory;
@Autowired
ApplicationEventPublisher publisher;
@Autowired(required = false)
SqlSessionFactory sqlSessionFactory;
@Bean @Bean
JdbcRepositoryFactory jdbcRepositoryFactory( JdbcRepositoryFactory jdbcRepositoryFactory(
@Qualifier("defaultDataAccessStrategy") DataAccessStrategy dataAccessStrategy, RelationalMappingContext context, @Qualifier("defaultDataAccessStrategy") DataAccessStrategy dataAccessStrategy, RelationalMappingContext context,
Dialect dialect, JdbcConverter converter, Optional<List<NamedQueries>> namedQueries, Dialect dialect, JdbcConverter converter, Optional<List<NamedQueries>> namedQueries,
List<EntityCallback<?>> callbacks, List<EntityCallback<?>> callbacks, List<EvaluationContextExtension> evaulationContextExtensions) {
List<EvaluationContextExtension> evaulationContextExtensions) {
JdbcRepositoryFactory factory = new JdbcRepositoryFactory(dataAccessStrategy, context, converter, dialect, JdbcRepositoryFactory factory = new JdbcRepositoryFactory(dataAccessStrategy, context, converter, dialect,
publisher, namedParameterJdbcTemplate()); publisher, namedParameterJdbcTemplate());
factory.setEntityCallbacks( factory.setEntityCallbacks(EntityCallbacks.create(callbacks.toArray(new EntityCallback[0])));
EntityCallbacks.create(callbacks.toArray(new EntityCallback[0]))
);
namedQueries.map(it -> it.iterator().next()).ifPresent(factory::setNamedQueries); namedQueries.map(it -> it.iterator().next()).ifPresent(factory::setNamedQueries);
@ -118,22 +121,24 @@ public class TestConfiguration {
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context, @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context,
JdbcConverter converter, Dialect dialect) { JdbcConverter converter, Dialect dialect) {
return new DataAccessStrategyFactory(new SqlGeneratorSource(context, converter, dialect), converter, return new DataAccessStrategyFactory(new SqlGeneratorSource(context, converter, dialect), converter, template,
template, new SqlParametersFactory(context, converter), new SqlParametersFactory(context, converter), new InsertStrategyFactory(template, dialect)).create();
new InsertStrategyFactory(template, dialect)).create();
} }
@Bean("jdbcMappingContext") @Bean("jdbcMappingContext")
@Profile(PROFILE_NO_SINGLE_QUERY_LOADING) @Profile(PROFILE_NO_SINGLE_QUERY_LOADING)
JdbcMappingContext jdbcMappingContextWithOutSingleQueryLoading(Optional<NamingStrategy> namingStrategy, CustomConversions conversions) { JdbcMappingContext jdbcMappingContextWithOutSingleQueryLoading(Optional<NamingStrategy> namingStrategy,
CustomConversions conversions) {
JdbcMappingContext mappingContext = new JdbcMappingContext(namingStrategy.orElse(DefaultNamingStrategy.INSTANCE)); JdbcMappingContext mappingContext = new JdbcMappingContext(namingStrategy.orElse(DefaultNamingStrategy.INSTANCE));
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return mappingContext; return mappingContext;
} }
@Bean("jdbcMappingContext") @Bean("jdbcMappingContext")
@Profile(PROFILE_SINGLE_QUERY_LOADING) @Profile(PROFILE_SINGLE_QUERY_LOADING)
JdbcMappingContext jdbcMappingContextWithSingleQueryLoading(Optional<NamingStrategy> namingStrategy, CustomConversions conversions) { JdbcMappingContext jdbcMappingContextWithSingleQueryLoading(Optional<NamingStrategy> namingStrategy,
CustomConversions conversions) {
JdbcMappingContext mappingContext = new JdbcMappingContext(namingStrategy.orElse(DefaultNamingStrategy.INSTANCE)); JdbcMappingContext mappingContext = new JdbcMappingContext(namingStrategy.orElse(DefaultNamingStrategy.INSTANCE));
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
@ -144,8 +149,9 @@ public class TestConfiguration {
@Bean @Bean
CustomConversions jdbcCustomConversions(Dialect dialect) { CustomConversions jdbcCustomConversions(Dialect dialect) {
SimpleTypeHolder simpleTypeHolder = dialect.simpleTypes().isEmpty() ? JdbcSimpleTypes.HOLDER SimpleTypeHolder simpleTypeHolder = dialect.simpleTypes().isEmpty() ?
: new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER); JdbcSimpleTypes.HOLDER :
new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)), return new JdbcCustomConversions(CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)),
Collections.emptyList()); Collections.emptyList());
@ -164,8 +170,9 @@ public class TestConfiguration {
CustomConversions conversions, @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, CustomConversions conversions, @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template,
Dialect dialect) { Dialect dialect) {
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport() JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ?
: JdbcArrayColumns.DefaultSupport.INSTANCE; ((JdbcDialect) dialect).getArraySupport() :
JdbcArrayColumns.DefaultSupport.INSTANCE;
return new MappingJdbcConverter( // return new MappingJdbcConverter( //
mappingContext, // mappingContext, //
@ -181,12 +188,9 @@ public class TestConfiguration {
* @return must not be {@literal null}. * @return must not be {@literal null}.
*/ */
@Bean @Bean
public IdGeneratingBeforeSaveCallback idGeneratingBeforeSaveCallback( public IdGeneratingBeforeSaveCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext,
JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations, Dialect dialect) {
NamedParameterJdbcOperations operations, return Mockito.spy(new IdGeneratingBeforeSaveCallback(mappingContext, dialect, operations));
Dialect dialect
) {
return new IdGeneratingBeforeSaveCallback(mappingContext, dialect, operations);
} }
@Bean @Bean

8
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-db2.sql

@ -1,7 +1,15 @@
DROP TABLE ReadOnlyIdEntity; DROP TABLE ReadOnlyIdEntity;
DROP TABLE PrimitiveIdEntity; DROP TABLE PrimitiveIdEntity;
DROP TABLE ImmutableWithManualIdentity; DROP TABLE ImmutableWithManualIdentity;
DROP TABLE EntityWithSeq;
DROP TABLE PrimitiveIdEntityWithSeq;
CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE primitive_seq_seq START WITH 1;

7
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-h2.sql

@ -3,3 +3,10 @@
CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE primitive_seq_seq START WITH 1;

7
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-hsql.sql

@ -3,3 +3,10 @@
CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE "simple_seq_seq" START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE "persistable_seq_seq" START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE "primitive_seq_seq" START WITH 1;

6
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mariadb.sql

@ -1,3 +1,9 @@
CREATE TABLE ReadOnlyIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE primitive_seq_seq START WITH 1;

11
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mssql.sql

@ -1,8 +1,17 @@
DROP TABLE IF EXISTS ReadOnlyIdEntity; DROP TABLE IF EXISTS ReadOnlyIdEntity;
DROP TABLE IF EXISTS PrimitiveIdEntity; DROP TABLE IF EXISTS PrimitiveIdEntity;
DROP TABLE IF EXISTS ImmutableWithManualIdentity; DROP TABLE IF EXISTS ImmutableWithManualIdentity;
DROP TABLE IF EXISTS EntityWithSeq;
DROP TABLE IF EXISTS PersistableEntityWithSeq;
DROP TABLE IF EXISTS PrimitiveIdEntityWithSeq;
CREATE TABLE ReadOnlyIdEntity (ID BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE EntityWithSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE primitive_seq_seq START WITH 1;

6
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mysql.sql

@ -1,3 +1,9 @@
CREATE TABLE ReadOnlyIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE primitive_seq_seq START WITH 1;

21
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-oracle.sql

@ -16,3 +16,24 @@ CREATE TABLE ImmutableWithManualIdentity (
ID NUMBER PRIMARY KEY, ID NUMBER PRIMARY KEY,
NAME VARCHAR2(100) NAME VARCHAR2(100)
); );
CREATE TABLE SimpleSeq (
ID NUMBER PRIMARY KEY,
NAME VARCHAR2(100)
);
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (
ID NUMBER PRIMARY KEY,
NAME VARCHAR2(100)
);
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (
ID NUMBER PRIMARY KEY,
NAME VARCHAR2(100)
);
CREATE SEQUENCE primitive_seq_seq START WITH 1;

9
spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-postgres.sql

@ -1,7 +1,16 @@
DROP TABLE ReadOnlyIdEntity; DROP TABLE ReadOnlyIdEntity;
DROP TABLE PrimitiveIdEntity; DROP TABLE PrimitiveIdEntity;
DROP TABLE ImmutableWithManualIdentity; DROP TABLE ImmutableWithManualIdentity;
DROP TABLE EntityWithSeq;
DROP TABLE PersistableEntityWithSeq;
DROP TABLE PrimitiveIdEntityWithSeq;
CREATE TABLE ReadOnlyIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ReadOnlyIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE PrimitiveIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE simple_seq_seq START WITH 1;
CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE persistable_seq_seq START WITH 1;
CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100));
CREATE SEQUENCE primitive_seq_seq START WITH 1;
Loading…
Cancel
Save