Polishing.
Reference issues in tests comments. Removed `DisabledOnDatabase` IdGeneration default methods related to sequence generation are now internally consistent. Formatting and naming. IdGeneration offers simple support by default. Fix exception in oracle integration test setup Use SqlIdentifier for sequence names Remove SEQUENCE id source Added documentation See #1923 Original pull request #1955
This commit is contained in:
@@ -11,6 +11,7 @@ import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -40,15 +41,17 @@ public class IdGeneratingBeforeSaveCallback implements BeforeSaveCallback<Object
|
||||
|
||||
@Override
|
||||
public Object onBeforeSave(Object aggregate, MutableAggregateChange<Object> aggregateChange) {
|
||||
|
||||
Assert.notNull(aggregate, "The aggregate cannot be null at this point");
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = relationalMappingContext.getPersistentEntity(aggregate.getClass());
|
||||
Optional<String> idTargetSequence = persistentEntity.getIdTargetSequence();
|
||||
Optional<SqlIdentifier> idSequence = persistentEntity.getIdSequence();
|
||||
|
||||
if (dialect.getIdGeneration().sequencesSupported()) {
|
||||
|
||||
if (persistentEntity.getIdProperty() != null) {
|
||||
idTargetSequence
|
||||
.map(s -> dialect.getIdGeneration().nextValueFromSequenceSelect(s))
|
||||
idSequence
|
||||
.map(s -> dialect.getIdGeneration().createSequenceQuery(s))
|
||||
.ifPresent(sql -> {
|
||||
Long idValue = operations.queryForObject(sql, Map.of(), (rs, rowNum) -> rs.getLong(1));
|
||||
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(aggregate);
|
||||
@@ -56,7 +59,7 @@ public class IdGeneratingBeforeSaveCallback implements BeforeSaveCallback<Object
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (idTargetSequence.isPresent()) {
|
||||
if (idSequence.isPresent()) {
|
||||
LOG.warn("""
|
||||
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
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.springframework.data.relational.core.dialect.MySqlDialect;
|
||||
import org.springframework.data.relational.core.dialect.PostgresDialect;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.relational.core.mapping.TargetSequence;
|
||||
import org.springframework.data.relational.core.mapping.Sequence;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
@@ -26,68 +26,58 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
*/
|
||||
class IdGeneratingBeforeSaveCallbackTest {
|
||||
|
||||
@Test
|
||||
void test_mySqlDialect_sequenceGenerationIsNotSupported() {
|
||||
// given
|
||||
RelationalMappingContext relationalMappingContext = new RelationalMappingContext();
|
||||
@Test // GH-1923
|
||||
void mySqlDialectsequenceGenerationIsNotSupported() {
|
||||
|
||||
RelationalMappingContext relationalMappingContext = new RelationalMappingContext();
|
||||
MySqlDialect mySqlDialect = new MySqlDialect(IdentifierProcessing.NONE);
|
||||
NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
|
||||
|
||||
// and
|
||||
IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, mySqlDialect, operations);
|
||||
|
||||
NoSequenceEntity entity = new NoSequenceEntity();
|
||||
|
||||
// when
|
||||
Object processed = subject.onBeforeSave(entity, MutableAggregateChange.forSave(entity));
|
||||
|
||||
// then
|
||||
Assertions.assertThat(processed).isSameAs(entity);
|
||||
Assertions.assertThat(processed).usingRecursiveComparison().isEqualTo(entity);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EntityIsNotMarkedWithTargetSequence() {
|
||||
// given
|
||||
RelationalMappingContext relationalMappingContext = new RelationalMappingContext();
|
||||
@Test // GH-1923
|
||||
void entityIsNotMarkedWithTargetSequence() {
|
||||
|
||||
RelationalMappingContext relationalMappingContext = new RelationalMappingContext();
|
||||
PostgresDialect mySqlDialect = PostgresDialect.INSTANCE;
|
||||
NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
|
||||
|
||||
// and
|
||||
IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, mySqlDialect, operations);
|
||||
|
||||
NoSequenceEntity entity = new NoSequenceEntity();
|
||||
|
||||
// when
|
||||
Object processed = subject.onBeforeSave(entity, MutableAggregateChange.forSave(entity));
|
||||
|
||||
// then
|
||||
Assertions.assertThat(processed).isSameAs(entity);
|
||||
Assertions.assertThat(processed).usingRecursiveComparison().isEqualTo(entity);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EntityIdIsPopulatedFromSequence() {
|
||||
// given
|
||||
@Test // GH-1923
|
||||
void entityIdIsPopulatedFromSequence() {
|
||||
|
||||
RelationalMappingContext relationalMappingContext = new RelationalMappingContext();
|
||||
relationalMappingContext.getRequiredPersistentEntity(EntityWithSequence.class);
|
||||
|
||||
PostgresDialect mySqlDialect = PostgresDialect.INSTANCE;
|
||||
NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
|
||||
|
||||
// and
|
||||
long generatedId = 112L;
|
||||
when(operations.queryForObject(anyString(), anyMap(), any(RowMapper.class))).thenReturn(generatedId);
|
||||
|
||||
// and
|
||||
IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, mySqlDialect, operations);
|
||||
|
||||
EntityWithSequence entity = new EntityWithSequence();
|
||||
|
||||
// when
|
||||
Object processed = subject.onBeforeSave(entity, MutableAggregateChange.forSave(entity));
|
||||
|
||||
// then
|
||||
Assertions.assertThat(processed).isSameAs(entity);
|
||||
Assertions
|
||||
.assertThat(processed)
|
||||
@@ -109,7 +99,7 @@ class IdGeneratingBeforeSaveCallbackTest {
|
||||
static class EntityWithSequence {
|
||||
|
||||
@Id
|
||||
@TargetSequence(value = "id_seq", schema = "public")
|
||||
@Sequence(value = "id_seq", schema = "public")
|
||||
private Long id;
|
||||
|
||||
private Long name;
|
||||
|
||||
@@ -65,7 +65,7 @@ import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.MappedCollection;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.relational.core.mapping.TargetSequence;
|
||||
import org.springframework.data.relational.core.mapping.Sequence;
|
||||
import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
|
||||
import org.springframework.data.relational.core.sql.LockMode;
|
||||
@@ -126,9 +126,10 @@ public class JdbcRepositoryIntegrationTests {
|
||||
"id_Prop = " + entity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1923
|
||||
@EnabledOnFeature(value = TestDatabaseFeatures.Feature.SUPPORTS_SEQUENCES)
|
||||
public void saveEntityWithTargetSequenceSpecified() {
|
||||
|
||||
EntityWithSequence first = entityWithSequenceRepository.save(new EntityWithSequence("first"));
|
||||
EntityWithSequence second = entityWithSequenceRepository.save(new EntityWithSequence("second"));
|
||||
|
||||
@@ -139,9 +140,10 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(second.getName()).isEqualTo("second");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1923
|
||||
@EnabledOnFeature(value = TestDatabaseFeatures.Feature.SUPPORTS_SEQUENCES)
|
||||
public void batchInsertEntityWithTargetSequenceSpecified() {
|
||||
|
||||
Iterable<EntityWithSequence> results = entityWithSequenceRepository
|
||||
.saveAll(List.of(new EntityWithSequence("first"), new EntityWithSequence("second")));
|
||||
|
||||
@@ -1862,7 +1864,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
static class EntityWithSequence {
|
||||
|
||||
@Id
|
||||
@TargetSequence(sequence = "entity_sequence") private Long id;
|
||||
@Sequence(sequence = "ENTITY_SEQUENCE") private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package org.springframework.data.jdbc.testing;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.test.context.junit.jupiter.EnabledIf;
|
||||
|
||||
/**
|
||||
* Annotation that allows to disable a particular test to be executed on a particular database
|
||||
*
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ExtendWith(DisabledOnDatabaseExecutionCondition.class)
|
||||
public @interface DisabledOnDatabase {
|
||||
|
||||
/**
|
||||
* The database on which the test is not supposed to run on
|
||||
*/
|
||||
DatabaseType database();
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.springframework.data.jdbc.testing;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
|
||||
import org.junit.jupiter.api.extension.ExecutionCondition;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* {@link ExecutionCondition} for the {@link DisabledOnDatabase} annotation
|
||||
*
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
public class DisabledOnDatabaseExecutionCondition implements ExecutionCondition {
|
||||
|
||||
@Override
|
||||
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
|
||||
ApplicationContext applicationContext = SpringExtension.getApplicationContext(context);
|
||||
|
||||
MergedAnnotation<DisabledOnDatabase> disabledOnDatabaseMergedAnnotation = MergedAnnotations
|
||||
.from(context.getRequiredTestMethod(), MergedAnnotations.SearchStrategy.DIRECT)
|
||||
.get(DisabledOnDatabase.class);
|
||||
|
||||
DatabaseType database = disabledOnDatabaseMergedAnnotation.getEnum("database", DatabaseType.class);
|
||||
|
||||
if (ArrayUtils.contains(applicationContext.getEnvironment().getActiveProfiles(), database.getProfile())) {
|
||||
return ConditionEvaluationResult.disabled(
|
||||
"The test method '%s' is disabled for '%s' because of the @DisabledOnDatabase annotation".formatted(context.getRequiredTestMethod().getName(), database)
|
||||
);
|
||||
}
|
||||
return ConditionEvaluationResult.enabled("The test method '%s' is enabled".formatted(context.getRequiredTestMethod()));
|
||||
}
|
||||
}
|
||||
@@ -47,4 +47,4 @@ CREATE TABLE ENTITY_WITH_SEQUENCE
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;
|
||||
CREATE SEQUENCE `ENTITY_SEQUENCE` START WITH 1 INCREMENT BY 1 NO MAXVALUE;
|
||||
@@ -47,12 +47,12 @@ CREATE TABLE WITH_DELIMITED_COLUMN
|
||||
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
"ORG.XTUNIT.IDENTIFIER" VARCHAR(100),
|
||||
STYPE VARCHAR(100)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE ENTITY_WITH_SEQUENCE
|
||||
(
|
||||
ID BIGINT,
|
||||
ID NUMBER,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;
|
||||
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1;
|
||||
@@ -55,4 +55,4 @@ CREATE TABLE ENTITY_WITH_SEQUENCE
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ENTITY_SEQUENCE START WITH 1 INCREMENT BY 1 NO MAXVALUE;
|
||||
CREATE SEQUENCE "ENTITY_SEQUENCE" START WITH 1 INCREMENT BY 1 NO MAXVALUE;
|
||||
Reference in New Issue
Block a user