From eb25cc3ed44e05a22b0ee5c442e3b4107003e72e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 9 Apr 2025 10:30:32 +0200 Subject: [PATCH] Move Sequence to RelationalPersistentProperty. Sequence details are now maintained on the property level instead of using the entity level. This is a more accurate representation of the underlying model and that properties are annotated and not entities. It also allows future extension of expanding sequence support to general properties. Extract delegate for sequence generation. Move types to org.springframework.data.jdbc.core.convert to resolve package cycles. See #2003 Original pull request: #2005 --- .../convert/IdGeneratingEntityCallback.java | 71 ++++++ .../SequenceEntityCallbackDelegate.java | 102 +++++++++ .../IdGeneratingBeforeSaveCallback.java | 112 ---------- .../config/AbstractJdbcConfiguration.java | 8 +- .../IdGeneratingEntityCallbackTest.java} | 18 +- ...epositoryIdGenerationIntegrationTests.java | 4 +- .../data/jdbc/testing/TestConfiguration.java | 9 +- .../core/conversion/IdValueSource.java | 5 +- .../BasicRelationalPersistentEntity.java | 36 --- .../BasicRelationalPersistentProperty.java | 32 ++- .../EmbeddedRelationalPersistentEntity.java | 6 - .../EmbeddedRelationalPersistentProperty.java | 6 + .../mapping/RelationalPersistentEntity.java | 8 - .../mapping/RelationalPersistentProperty.java | 17 ++ ...icRelationalPersistentEntityUnitTests.java | 56 ----- ...RelationalPersistentPropertyUnitTests.java | 207 +++++++++++++----- 16 files changed, 395 insertions(+), 302 deletions(-) create mode 100644 spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallback.java create mode 100644 spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java delete mode 100644 spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java rename spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/{mapping/IdGeneratingBeforeSaveCallbackTest.java => convert/IdGeneratingEntityCallbackTest.java} (86%) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallback.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallback.java new file mode 100644 index 000000000..7d6f988d0 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallback.java @@ -0,0 +1,71 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.convert; + +import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.relational.core.conversion.MutableAggregateChange; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; +import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; +import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.util.Assert; + +/** + * Callback for generating identifier values through a database sequence. + * + * @author Mikhail Polivakha + * @author Mark Paluch + * @since 3.5 + */ +public class IdGeneratingEntityCallback implements BeforeSaveCallback { + + private final MappingContext, ? extends RelationalPersistentProperty> context; + private final SequenceEntityCallbackDelegate delegate; + + public IdGeneratingEntityCallback( + MappingContext, ? extends RelationalPersistentProperty> context, Dialect dialect, + NamedParameterJdbcOperations operations) { + + this.context = context; + this.delegate = new SequenceEntityCallbackDelegate(dialect, operations); + } + + @Override + public Object onBeforeSave(Object aggregate, MutableAggregateChange aggregateChange) { + + Assert.notNull(aggregate, "aggregate must not be null"); + + RelationalPersistentEntity entity = context.getRequiredPersistentEntity(aggregate.getClass()); + + if (!entity.hasIdProperty()) { + return aggregate; + } + + RelationalPersistentProperty property = entity.getRequiredIdProperty(); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(aggregate); + + if (!entity.isNew(aggregate) || delegate.hasValue(property, accessor) || !property.hasSequence()) { + return aggregate; + } + + delegate.generateSequenceValue(property, accessor); + + return accessor.getBean(); + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java new file mode 100644 index 000000000..00efd7fcf --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java @@ -0,0 +1,102 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.convert; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.data.util.ReflectionUtils; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; +import org.springframework.util.NumberUtils; + +/** + * Support class for generating identifier values through a database sequence. + * + * @author Mikhail Polivakha + * @author Mark Paluch + * @since 3.5 + * @see org.springframework.data.relational.core.mapping.Sequence + */ +class SequenceEntityCallbackDelegate { + + private static final Log LOG = LogFactory.getLog(SequenceEntityCallbackDelegate.class); + private final static MapSqlParameterSource EMPTY_PARAMETERS = new MapSqlParameterSource(); + + private final Dialect dialect; + private final NamedParameterJdbcOperations operations; + + public SequenceEntityCallbackDelegate(Dialect dialect, NamedParameterJdbcOperations operations) { + this.dialect = dialect; + this.operations = operations; + } + + @SuppressWarnings("unchecked") + protected void generateSequenceValue(RelationalPersistentProperty property, + PersistentPropertyAccessor accessor) { + + Object sequenceValue = getSequenceValue(property); + + if (sequenceValue == null) { + return; + } + + Class targetType = ClassUtils.resolvePrimitiveIfNecessary(property.getType()); + if (sequenceValue instanceof Number && Number.class.isAssignableFrom(targetType)) { + sequenceValue = NumberUtils.convertNumberToTargetClass((Number) sequenceValue, + (Class) targetType); + } + + accessor.setProperty(property, sequenceValue); + } + + protected boolean hasValue(PersistentProperty property, PersistentPropertyAccessor propertyAccessor) { + + Object identifier = propertyAccessor.getProperty(property); + + if (property.getType().isPrimitive()) { + + Object primitiveDefault = ReflectionUtils.getPrimitiveDefault(property.getType()); + return !primitiveDefault.equals(identifier); + } + + return identifier != null; + } + + private @Nullable Object getSequenceValue(RelationalPersistentProperty property) { + + SqlIdentifier sequence = property.getSequence(); + + if (sequence != null && !dialect.getIdGeneration().sequencesSupported()) { + LOG.warn(""" + Aggregate type '%s' is marked for sequence usage but configured dialect '%s' + does not support sequences. Falling back to identity columns. + """.formatted(property.getOwner().getType(), ClassUtils.getQualifiedName(dialect.getClass()))); + return null; + } + + String sql = dialect.getIdGeneration().createSequenceQuery(sequence); + return operations.queryForObject(sql, EMPTY_PARAMETERS, (rs, rowNum) -> rs.getObject(1)); + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java deleted file mode 100644 index a05503eba..000000000 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallback.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.springframework.data.jdbc.core.mapping; - -import java.util.Optional; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.PersistentPropertyAccessor; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.relational.core.conversion.MutableAggregateChange; -import org.springframework.data.relational.core.dialect.Dialect; -import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; -import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback; -import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.data.util.ReflectionUtils; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.NumberUtils; - -/** - * Callback for generating identifier values through a database sequence. - * - * @author Mikhail Polivakha - * @author Mark Paluch - * @since 3.5 - * @see org.springframework.data.relational.core.mapping.Sequence - */ -public class IdGeneratingBeforeSaveCallback implements BeforeSaveCallback { - - private static final Log LOG = LogFactory.getLog(IdGeneratingBeforeSaveCallback.class); - private final static MapSqlParameterSource EMPTY_PARAMETERS = new MapSqlParameterSource(); - - private final MappingContext, ? extends RelationalPersistentProperty> mappingContext; - private final Dialect dialect; - private final NamedParameterJdbcOperations operations; - - public IdGeneratingBeforeSaveCallback( - MappingContext, ? extends RelationalPersistentProperty> mappingContext, - Dialect dialect, NamedParameterJdbcOperations operations) { - this.mappingContext = mappingContext; - this.dialect = dialect; - this.operations = operations; - } - - @Override - public Object onBeforeSave(Object aggregate, MutableAggregateChange aggregateChange) { - - Assert.notNull(aggregate, "aggregate must not be null"); - - RelationalPersistentEntity entity = mappingContext.getRequiredPersistentEntity(aggregate.getClass()); - - if (!entity.hasIdProperty()) { - return aggregate; - } - - RelationalPersistentProperty idProperty = entity.getRequiredIdProperty(); - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(aggregate); - - if (!entity.isNew(aggregate) || hasIdentifierValue(idProperty, accessor)) { - return aggregate; - } - - potentiallyFetchIdFromSequence(idProperty, entity, accessor); - return accessor.getBean(); - } - - private boolean hasIdentifierValue(PersistentProperty idProperty, - PersistentPropertyAccessor propertyAccessor) { - - Object identifier = propertyAccessor.getProperty(idProperty); - - if (idProperty.getType().isPrimitive()) { - - Object primitiveDefault = ReflectionUtils.getPrimitiveDefault(idProperty.getType()); - return !primitiveDefault.equals(identifier); - } - - return identifier != null; - } - - @SuppressWarnings("unchecked") - private void potentiallyFetchIdFromSequence(PersistentProperty idProperty, - RelationalPersistentEntity persistentEntity, PersistentPropertyAccessor accessor) { - - Optional idSequence = persistentEntity.getIdSequence(); - - if (idSequence.isPresent() && !dialect.getIdGeneration().sequencesSupported()) { - LOG.warn(""" - Aggregate type '%s' is marked for sequence usage but configured dialect '%s' - does not support sequences. Falling back to identity columns. - """.formatted(persistentEntity.getType(), ClassUtils.getQualifiedName(dialect.getClass()))); - return; - } - - idSequence.map(s -> dialect.getIdGeneration().createSequenceQuery(s)).ifPresent(sql -> { - - Object idValue = operations.queryForObject(sql, EMPTY_PARAMETERS, (rs, rowNum) -> rs.getObject(1)); - - Class targetType = ClassUtils.resolvePrimitiveIfNecessary(idProperty.getType()); - if (idValue instanceof Number && Number.class.isAssignableFrom(targetType)) { - accessor.setProperty(idProperty, - NumberUtils.convertNumberToTargetClass((Number) idValue, (Class) targetType)); - } else { - accessor.setProperty(idProperty, idValue); - } - }); - } -} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java index 0a8a1c7a8..ac4483069 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java @@ -25,6 +25,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; @@ -38,7 +39,6 @@ import org.springframework.data.jdbc.core.JdbcAggregateOperations; import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.*; import org.springframework.data.jdbc.core.dialect.JdbcDialect; -import org.springframework.data.jdbc.core.mapping.IdGeneratingBeforeSaveCallback; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -121,7 +121,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware { } /** - * Creates a {@link IdGeneratingBeforeSaveCallback} bean using the configured + * Creates a {@link IdGeneratingEntityCallback} bean using the configured * {@link #jdbcMappingContext(Optional, JdbcCustomConversions, RelationalManagedTypes)} and * {@link #jdbcDialect(NamedParameterJdbcOperations)}. * @@ -129,9 +129,9 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware { * @since 3.5 */ @Bean - public IdGeneratingBeforeSaveCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext, + public IdGeneratingEntityCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations, Dialect dialect) { - return new IdGeneratingBeforeSaveCallback(mappingContext, dialect, operations); + return new IdGeneratingEntityCallback(mappingContext, dialect, operations); } /** diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallbackTest.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java similarity index 86% rename from spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallbackTest.java rename to spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java index aaea4ad03..240d47f59 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/IdGeneratingBeforeSaveCallbackTest.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024-2025 the original author or authors. + * Copyright 2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jdbc.core.mapping; +package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; @@ -41,13 +41,13 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; /** - * Unit tests for {@link IdGeneratingBeforeSaveCallback} + * Unit tests for {@link IdGeneratingEntityCallback} * * @author Mikhail Polivakha * @author Mark Paluch */ @MockitoSettings(strictness = Strictness.LENIENT) -class IdGeneratingBeforeSaveCallbackTest { +class IdGeneratingEntityCallbackTest { @Mock NamedParameterJdbcOperations operations; RelationalMappingContext relationalMappingContext; @@ -64,7 +64,7 @@ class IdGeneratingBeforeSaveCallbackTest { NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class); - IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, + IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, MySqlDialect.INSTANCE, operations); EntityWithSequence processed = (EntityWithSequence) subject.onBeforeSave(new EntityWithSequence(), @@ -76,7 +76,7 @@ class IdGeneratingBeforeSaveCallbackTest { @Test // GH-1923 void entityIsNotMarkedWithTargetSequence() { - IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, + IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, MySqlDialect.INSTANCE, operations); NoSequenceEntity processed = (NoSequenceEntity) subject.onBeforeSave(new NoSequenceEntity(), @@ -92,7 +92,7 @@ class IdGeneratingBeforeSaveCallbackTest { when(operations.queryForObject(anyString(), any(SqlParameterSource.class), any(RowMapper.class))) .thenReturn(generatedId); - IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, + IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, PostgresDialect.INSTANCE, operations); EntityWithSequence processed = (EntityWithSequence) subject.onBeforeSave(new EntityWithSequence(), @@ -108,7 +108,7 @@ class IdGeneratingBeforeSaveCallbackTest { when(operations.queryForObject(anyString(), any(SqlParameterSource.class), any(RowMapper.class))) .thenReturn(generatedId); - IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, + IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, PostgresDialect.INSTANCE, operations); EntityWithIntSequence processed = (EntityWithIntSequence) subject.onBeforeSave(new EntityWithIntSequence(), @@ -124,7 +124,7 @@ class IdGeneratingBeforeSaveCallbackTest { when(operations.queryForObject(anyString(), any(SqlParameterSource.class), any(RowMapper.class))) .thenReturn(generatedId); - IdGeneratingBeforeSaveCallback subject = new IdGeneratingBeforeSaveCallback(relationalMappingContext, + IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, PostgresDialect.INSTANCE, operations); EntityWithUuidSequence processed = (EntityWithUuidSequence) subject.onBeforeSave(new EntityWithUuidSequence(), diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java index 0e8ece8be..75fdc528a 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java @@ -35,7 +35,7 @@ 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.core.convert.IdGeneratingEntityCallback; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.data.jdbc.testing.IntegrationTest; @@ -66,7 +66,7 @@ class JdbcRepositoryIdGenerationIntegrationTests { @Autowired SimpleSeqRepository simpleSeqRepository; @Autowired PersistableSeqRepository persistableSeqRepository; @Autowired PrimitiveIdSeqRepository primitiveIdSeqRepository; - @Autowired IdGeneratingBeforeSaveCallback idGeneratingCallback; + @Autowired IdGeneratingEntityCallback idGeneratingCallback; @Test // DATAJDBC-98 void idWithoutSetterGetsSet() { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java index e6ffebf37..ea3e5482c 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java @@ -24,6 +24,7 @@ import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mockito.Mockito; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -37,7 +38,6 @@ import org.springframework.context.annotation.Profile; import org.springframework.data.convert.CustomConversions; import org.springframework.data.jdbc.core.convert.*; import org.springframework.data.jdbc.core.dialect.JdbcDialect; -import org.springframework.data.jdbc.core.mapping.IdGeneratingBeforeSaveCallback; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; import org.springframework.data.jdbc.repository.config.DialectResolver; @@ -45,7 +45,6 @@ import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.mapping.callback.EntityCallback; import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.mapping.model.SimpleTypeHolder; -import org.springframework.data.relational.RelationalManagedTypes; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.DefaultNamingStrategy; import org.springframework.data.relational.core.mapping.NamingStrategy; @@ -182,15 +181,15 @@ public class TestConfiguration { } /** - * Creates a {@link IdGeneratingBeforeSaveCallback} bean using the configured + * Creates a {@link IdGeneratingEntityCallback} bean using the configured * {@link #jdbcDialect(NamedParameterJdbcOperations)}. * * @return must not be {@literal null}. */ @Bean - public IdGeneratingBeforeSaveCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext, + public IdGeneratingEntityCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations, Dialect dialect) { - return Mockito.spy(new IdGeneratingBeforeSaveCallback(mappingContext, dialect, operations)); + return Mockito.spy(new IdGeneratingEntityCallback(mappingContext, dialect, operations)); } @Bean diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/IdValueSource.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/IdValueSource.java index 047fa4353..d863d8240 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/IdValueSource.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/IdValueSource.java @@ -49,12 +49,13 @@ public enum IdValueSource { */ public static IdValueSource forInstance(Object instance, RelationalPersistentEntity persistentEntity) { - if (persistentEntity.getIdSequence().isPresent()) { + RelationalPersistentProperty idProperty = persistentEntity.getIdProperty(); + + if (idProperty != null && idProperty.hasSequence()) { return IdValueSource.PROVIDED; } Object idValue = persistentEntity.getIdentifierAccessor(instance).getIdentifier(); - RelationalPersistentProperty idProperty = persistentEntity.getIdProperty(); if (idProperty == null) { return IdValueSource.NONE; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java index cc84a9501..99b48363f 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java @@ -17,8 +17,6 @@ package org.springframework.data.relational.core.mapping; import java.util.Optional; -import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.core.annotation.MergedAnnotations; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.util.Lazy; @@ -47,9 +45,6 @@ class BasicRelationalPersistentEntity extends BasicPersistentEntity tableName; private final @Nullable Expression tableNameExpression; - - private final Lazy idSequenceName; - private final Lazy> schemaName; private final @Nullable Expression schemaNameExpression; private final ExpressionEvaluator expressionEvaluator; @@ -91,8 +86,6 @@ class BasicRelationalPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity getIdSequence() { - return idSequenceName.getOptional(); - } - @Override public String toString() { return String.format("BasicRelationalPersistentEntity<%s>", getType()); } - private @Nullable SqlIdentifier determineSequenceName() { - - RelationalPersistentProperty idProperty = getIdProperty(); - - if (idProperty != null && idProperty.isAnnotationPresent(Sequence.class)) { - - Sequence requiredAnnotation = idProperty.getRequiredAnnotation(Sequence.class); - - MergedAnnotation targetSequence = MergedAnnotations.from(requiredAnnotation) - .get(Sequence.class); - - String sequence = targetSequence.getString("sequence"); - String schema = targetSequence.getString("schema"); - - SqlIdentifier sequenceIdentifier = SqlIdentifier.quoted(sequence); - if (StringUtils.hasText(schema)) { - sequenceIdentifier = SqlIdentifier.from(SqlIdentifier.quoted(schema), sequenceIdentifier); - } - - return sequenceIdentifier; - } else { - return null; - } - } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java index 4aa0fe335..0538a9810 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java @@ -52,12 +52,13 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent private final Lazy columnName; private final boolean hasExplicitColumnName; private final @Nullable Expression columnNameExpression; + private final SqlIdentifier sequence; private final Lazy> collectionIdColumnName; private final Lazy collectionKeyColumnName; private final @Nullable Expression collectionKeyColumnNameExpression; private final boolean isEmbedded; - private final String embeddedPrefix; + private final NamingStrategy namingStrategy; private boolean forceQuote = true; private ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator(EvaluationContextProvider.DEFAULT); @@ -80,7 +81,6 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent Assert.notNull(namingStrategy, "NamingStrategy must not be null"); this.isEmbedded = isAnnotationPresent(Embedded.class); - this.embeddedPrefix = Optional.ofNullable(findAnnotation(Embedded.class)) // .map(Embedded::prefix) // .orElse(""); @@ -126,6 +126,8 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent this.columnNameExpression = null; } + this.sequence = determineSequenceName(); + if (collectionIdColumnName == null) { collectionIdColumnName = Lazy.of(Optional.empty()); } @@ -269,8 +271,34 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent return findAnnotation(InsertOnlyProperty.class) != null; } + @Nullable + @Override + public SqlIdentifier getSequence() { + return this.sequence; + } + private boolean isListLike() { return isCollectionLike() && !Set.class.isAssignableFrom(this.getType()); } + private @Nullable SqlIdentifier determineSequenceName() { + + if (isAnnotationPresent(Sequence.class)) { + + Sequence annotation = getRequiredAnnotation(Sequence.class); + + String sequence = annotation.sequence(); + String schema = annotation.schema(); + + SqlIdentifier sequenceIdentifier = SqlIdentifier.quoted(sequence); + if (StringUtils.hasText(schema)) { + sequenceIdentifier = SqlIdentifier.from(SqlIdentifier.quoted(schema), sequenceIdentifier); + } + + return sequenceIdentifier; + } else { + return null; + } + } + } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java index 5e23e1d51..27359f759 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java @@ -17,7 +17,6 @@ package org.springframework.data.relational.core.mapping; import java.lang.annotation.Annotation; import java.util.Iterator; -import java.util.Optional; import org.springframework.core.env.Environment; import org.springframework.data.mapping.*; @@ -56,11 +55,6 @@ class EmbeddedRelationalPersistentEntity implements RelationalPersistentEntit throw new MappingException("Embedded entity does not have an id column"); } - @Override - public Optional getIdSequence() { - return Optional.empty(); - } - @Override public void addPersistentProperty(RelationalPersistentProperty property) { throw new UnsupportedOperationException(); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java index 7a0c42c0e..bcfc2ddb5 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java @@ -105,6 +105,12 @@ class EmbeddedRelationalPersistentProperty implements RelationalPersistentProper return delegate.isInsertOnly(); } + @Nullable + @Override + public SqlIdentifier getSequence() { + return delegate.getSequence(); + } + @Override public String getName() { return delegate.getName(); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java index f111d77bc..49e9b929c 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java @@ -15,8 +15,6 @@ */ package org.springframework.data.relational.core.mapping; -import java.util.Optional; - import org.springframework.data.mapping.model.MutablePersistentEntity; import org.springframework.data.relational.core.sql.SqlIdentifier; @@ -55,10 +53,4 @@ public interface RelationalPersistentEntity extends MutablePersistentEntity getIdSequence(); - } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java index 1aaceaa77..6d05df923 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java @@ -25,6 +25,7 @@ import org.springframework.lang.Nullable; * @author Jens Schauder * @author Oliver Gierke * @author Bastian Wilhelm + * @author Mark Paluch */ public interface RelationalPersistentProperty extends PersistentProperty { @@ -94,4 +95,20 @@ public interface RelationalPersistentProperty extends PersistentProperty entity = mappingContext.getRequiredPersistentEntity(DummySubEntity.class); - - assertThat(entity.getIdSequence()).isEmpty(); - } - - @Test // GH-1923 - void determineSequenceName() { - - RelationalPersistentEntity persistentEntity = mappingContext - .getRequiredPersistentEntity(EntityWithSequence.class); - - assertThat(persistentEntity.getIdSequence()).contains(SqlIdentifier.quoted("my_seq")); - } - - @Test // GH-1923 - void determineSequenceNameFromValue() { - - RelationalPersistentEntity persistentEntity = mappingContext - .getRequiredPersistentEntity(EntityWithSequenceValueAlias.class); - - assertThat(persistentEntity.getIdSequence()).contains(SqlIdentifier.quoted("my_seq")); - } - - @Test // GH-1923 - void determineSequenceNameWithSchemaSpecified() { - - RelationalPersistentEntity persistentEntity = mappingContext - .getRequiredPersistentEntity(EntityWithSequenceAndSchema.class); - - assertThat(persistentEntity.getIdSequence()) - .contains(SqlIdentifier.from(SqlIdentifier.quoted("public"), SqlIdentifier.quoted("my_seq"))); - } - @Test // DATAJDBC-294 void considerIdColumnName() { @@ -239,26 +203,6 @@ class BasicRelationalPersistentEntityUnitTests { @Column("renamedId") Long id; } - @Table("entity_with_sequence") - static class EntityWithSequence { - @Id - @Sequence(sequence = "my_seq") Long id; - } - - @Table("entity_with_sequence_value_alias") - static class EntityWithSequenceValueAlias { - @Id - @Column("myId") - @Sequence(value = "my_seq") Long id; - } - - @Table("entity_with_sequence_and_schema") - static class EntityWithSequenceAndSchema { - @Id - @Column("myId") - @Sequence(sequence = "my_seq", schema = "public") Long id; - } - @Table() static class DummyEntityWithEmptyAnnotation { @Id diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java index dd9ac027c..86f3c5385 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; +import org.springframework.data.relational.core.sql.SqlIdentifier; /** * Unit tests for the {@link BasicRelationalPersistentProperty}. @@ -40,14 +41,15 @@ import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; * @author Florian Lüdiger * @author Bastian Wilhelm * @author Kurt Niemi + * @author Mark Paluch */ -public class BasicRelationalPersistentPropertyUnitTests { +class BasicRelationalPersistentPropertyUnitTests { - RelationalMappingContext context = new RelationalMappingContext(); - RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class); + private RelationalMappingContext context = new RelationalMappingContext(); + private RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class); @Test // DATAJDBC-106 - public void detectsAnnotatedColumnName() { + void detectsAnnotatedColumnName() { assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo(quoted("dummy_name")); assertThat(entity.getRequiredPersistentProperty("localDateTime").getColumnName()) @@ -55,7 +57,7 @@ public class BasicRelationalPersistentPropertyUnitTests { } @Test // DATAJDBC-218 - public void detectsAnnotatedColumnAndKeyName() { + void detectsAnnotatedColumnAndKeyName() { RelationalPersistentProperty listProperty = entity.getRequiredPersistentProperty("someList"); @@ -91,7 +93,7 @@ public class BasicRelationalPersistentPropertyUnitTests { } @Test // DATAJDBC-111 - public void detectsEmbeddedEntity() { + void detectsEmbeddedEntity() { final RelationalPersistentEntity requiredPersistentEntity = context .getRequiredPersistentEntity(DummyEntity.class); @@ -120,7 +122,7 @@ public class BasicRelationalPersistentPropertyUnitTests { } @Test // DATAJDBC-259 - public void classificationOfCollectionLikeProperties() { + void classificationOfCollectionLikeProperties() { RelationalPersistentProperty listOfString = entity.getRequiredPersistentProperty("listOfString"); RelationalPersistentProperty arrayOfString = entity.getRequiredPersistentProperty("arrayOfString"); @@ -150,11 +152,45 @@ public class BasicRelationalPersistentPropertyUnitTests { softly.assertAll(); } + @Test // GH-1923 + void entityWithNoSequence() { + + RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class); + + assertThat(entity.getRequiredIdProperty().getSequence()).isNull(); + } + + @Test // GH-1923 + void determineSequenceName() { + + RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(EntityWithSequence.class); + + assertThat(persistentEntity.getRequiredIdProperty().getSequence()).isEqualTo(SqlIdentifier.quoted("my_seq")); + } + + @Test // GH-1923 + void determineSequenceNameFromValue() { + + RelationalPersistentEntity persistentEntity = context + .getRequiredPersistentEntity(EntityWithSequenceValueAlias.class); + + assertThat(persistentEntity.getRequiredIdProperty().getSequence()).isEqualTo(SqlIdentifier.quoted("my_seq")); + } + + @Test // GH-1923 + void determineSequenceNameWithSchemaSpecified() { + + RelationalPersistentEntity persistentEntity = context + .getRequiredPersistentEntity(EntityWithSequenceAndSchema.class); + + assertThat(persistentEntity.getRequiredIdProperty().getSequence()) + .isEqualTo(SqlIdentifier.from(SqlIdentifier.quoted("public"), SqlIdentifier.quoted("my_seq"))); + } + @SuppressWarnings("unused") - private static class DummyEntity { + static class DummyEntity { - @Id - private final Long id; + @Id private final Long id; private final SomeEnum someEnum; private final LocalDateTime localDateTime; private final ZonedDateTime zonedDateTime; @@ -166,8 +202,7 @@ public class BasicRelationalPersistentPropertyUnitTests { private final OtherEntity[] arrayOfEntity; @MappedCollection(idColumn = "dummy_column_name", - keyColumn = "dummy_key_column_name") - private List someList; + keyColumn = "dummy_key_column_name") private List someList; // DATACMNS-106 private @Column("dummy_name") String name; @@ -177,17 +212,14 @@ public class BasicRelationalPersistentPropertyUnitTests { public static String littleBobbyTablesValue = "--; DROP ALL TABLES;--"; @Column(value = "#{T(org.springframework.data.relational.core.mapping." + "BasicRelationalPersistentPropertyUnitTests$DummyEntity" - + ").spelExpression1Value}") - private String spelExpression1; + + ").spelExpression1Value}") private String spelExpression1; @Column(value = "#{T(org.springframework.data.relational.core.mapping." + "BasicRelationalPersistentPropertyUnitTests$DummyEntity" - + ").littleBobbyTablesValue}") - private String littleBobbyTables; + + ").littleBobbyTablesValue}") private String littleBobbyTables; @Column( - value = "--; DROP ALL TABLES;--") - private String poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot; + value = "--; DROP ALL TABLES;--") private String poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot; // DATAJDBC-111 private @Embedded(onEmpty = OnEmpty.USE_NULL) EmbeddableEntity embeddableEntity; @@ -195,7 +227,9 @@ public class BasicRelationalPersistentPropertyUnitTests { // DATAJDBC-111 private @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix") EmbeddableEntity prefixedEmbeddableEntity; - public DummyEntity(Long id, SomeEnum someEnum, LocalDateTime localDateTime, ZonedDateTime zonedDateTime, List listOfString, String[] arrayOfString, List listOfEntity, OtherEntity[] arrayOfEntity) { + public DummyEntity(Long id, SomeEnum someEnum, LocalDateTime localDateTime, ZonedDateTime zonedDateTime, + List listOfString, String[] arrayOfString, List listOfEntity, + OtherEntity[] arrayOfEntity) { this.id = id; this.someEnum = someEnum; this.localDateTime = localDateTime; @@ -219,59 +253,59 @@ public class BasicRelationalPersistentPropertyUnitTests { return null; } - public Long getId() { + Long getId() { return this.id; } - public SomeEnum getSomeEnum() { + SomeEnum getSomeEnum() { return this.someEnum; } - public ZonedDateTime getZonedDateTime() { + ZonedDateTime getZonedDateTime() { return this.zonedDateTime; } - public List getListOfString() { + List getListOfString() { return this.listOfString; } - public String[] getArrayOfString() { + String[] getArrayOfString() { return this.arrayOfString; } - public List getListOfEntity() { + List getListOfEntity() { return this.listOfEntity; } - public OtherEntity[] getArrayOfEntity() { + OtherEntity[] getArrayOfEntity() { return this.arrayOfEntity; } - public List getSomeList() { + List getSomeList() { return this.someList; } - public String getName() { + String getName() { return this.name; } - public String getSpelExpression1() { + String getSpelExpression1() { return this.spelExpression1; } - public String getLittleBobbyTables() { + String getLittleBobbyTables() { return this.littleBobbyTables; } - public String getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot() { + String getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot() { return this.poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot; } - public EmbeddableEntity getEmbeddableEntity() { + EmbeddableEntity getEmbeddableEntity() { return this.embeddableEntity; } - public EmbeddableEntity getPrefixedEmbeddableEntity() { + EmbeddableEntity getPrefixedEmbeddableEntity() { return this.prefixedEmbeddableEntity; } @@ -291,7 +325,8 @@ public class BasicRelationalPersistentPropertyUnitTests { this.littleBobbyTables = littleBobbyTables; } - public void setPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(String poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot) { + public void setPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot( + String poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot) { this.poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot; } @@ -304,16 +339,21 @@ public class BasicRelationalPersistentPropertyUnitTests { } public boolean equals(final Object o) { - if (o == this) return true; - if (!(o instanceof DummyEntity)) return false; + if (o == this) + return true; + if (!(o instanceof DummyEntity)) + return false; final DummyEntity other = (DummyEntity) o; - if (!other.canEqual((Object) this)) return false; + if (!other.canEqual((Object) this)) + return false; final Object this$id = this.getId(); final Object other$id = other.getId(); - if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false; + if (this$id == null ? other$id != null : !this$id.equals(other$id)) + return false; final Object this$someEnum = this.getSomeEnum(); final Object other$someEnum = other.getSomeEnum(); - if (this$someEnum == null ? other$someEnum != null : !this$someEnum.equals(other$someEnum)) return false; + if (this$someEnum == null ? other$someEnum != null : !this$someEnum.equals(other$someEnum)) + return false; final Object this$localDateTime = this.getLocalDateTime(); final Object other$localDateTime = other.getLocalDateTime(); if (this$localDateTime == null ? other$localDateTime != null : !this$localDateTime.equals(other$localDateTime)) @@ -326,42 +366,55 @@ public class BasicRelationalPersistentPropertyUnitTests { final Object other$listOfString = other.getListOfString(); if (this$listOfString == null ? other$listOfString != null : !this$listOfString.equals(other$listOfString)) return false; - if (!java.util.Arrays.deepEquals(this.getArrayOfString(), other.getArrayOfString())) return false; + if (!java.util.Arrays.deepEquals(this.getArrayOfString(), other.getArrayOfString())) + return false; final Object this$listOfEntity = this.getListOfEntity(); final Object other$listOfEntity = other.getListOfEntity(); if (this$listOfEntity == null ? other$listOfEntity != null : !this$listOfEntity.equals(other$listOfEntity)) return false; - if (!java.util.Arrays.deepEquals(this.getArrayOfEntity(), other.getArrayOfEntity())) return false; + if (!java.util.Arrays.deepEquals(this.getArrayOfEntity(), other.getArrayOfEntity())) + return false; final Object this$someList = this.getSomeList(); final Object other$someList = other.getSomeList(); - if (this$someList == null ? other$someList != null : !this$someList.equals(other$someList)) return false; + if (this$someList == null ? other$someList != null : !this$someList.equals(other$someList)) + return false; final Object this$name = this.getName(); final Object other$name = other.getName(); - if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false; + if (this$name == null ? other$name != null : !this$name.equals(other$name)) + return false; final Object this$spelExpression1 = this.getSpelExpression1(); final Object other$spelExpression1 = other.getSpelExpression1(); - if (this$spelExpression1 == null ? other$spelExpression1 != null : !this$spelExpression1.equals(other$spelExpression1)) + if (this$spelExpression1 == null ? other$spelExpression1 != null + : !this$spelExpression1.equals(other$spelExpression1)) return false; final Object this$littleBobbyTables = this.getLittleBobbyTables(); final Object other$littleBobbyTables = other.getLittleBobbyTables(); - if (this$littleBobbyTables == null ? other$littleBobbyTables != null : !this$littleBobbyTables.equals(other$littleBobbyTables)) + if (this$littleBobbyTables == null ? other$littleBobbyTables != null + : !this$littleBobbyTables.equals(other$littleBobbyTables)) return false; - final Object this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = this.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(); - final Object other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = other.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(); - if (this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot == null ? other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot != null : !this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot.equals(other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot)) + final Object this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = this + .getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(); + final Object other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = other + .getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(); + if (this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot == null + ? other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot != null + : !this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot + .equals(other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot)) return false; final Object this$embeddableEntity = this.getEmbeddableEntity(); final Object other$embeddableEntity = other.getEmbeddableEntity(); - if (this$embeddableEntity == null ? other$embeddableEntity != null : !this$embeddableEntity.equals(other$embeddableEntity)) + if (this$embeddableEntity == null ? other$embeddableEntity != null + : !this$embeddableEntity.equals(other$embeddableEntity)) return false; final Object this$prefixedEmbeddableEntity = this.getPrefixedEmbeddableEntity(); final Object other$prefixedEmbeddableEntity = other.getPrefixedEmbeddableEntity(); - if (this$prefixedEmbeddableEntity == null ? other$prefixedEmbeddableEntity != null : !this$prefixedEmbeddableEntity.equals(other$prefixedEmbeddableEntity)) + if (this$prefixedEmbeddableEntity == null ? other$prefixedEmbeddableEntity != null + : !this$prefixedEmbeddableEntity.equals(other$prefixedEmbeddableEntity)) return false; return true; } - protected boolean canEqual(final Object other) { + boolean canEqual(final Object other) { return other instanceof DummyEntity; } @@ -390,8 +443,10 @@ public class BasicRelationalPersistentPropertyUnitTests { result = result * PRIME + ($spelExpression1 == null ? 43 : $spelExpression1.hashCode()); final Object $littleBobbyTables = this.getLittleBobbyTables(); result = result * PRIME + ($littleBobbyTables == null ? 43 : $littleBobbyTables.hashCode()); - final Object $poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = this.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(); - result = result * PRIME + ($poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot == null ? 43 : $poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot.hashCode()); + final Object $poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = this + .getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot(); + result = result * PRIME + ($poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot == null ? 43 + : $poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot.hashCode()); final Object $embeddableEntity = this.getEmbeddableEntity(); result = result * PRIME + ($embeddableEntity == null ? 43 : $embeddableEntity.hashCode()); final Object $prefixedEmbeddableEntity = this.getPrefixedEmbeddableEntity(); @@ -400,11 +455,20 @@ public class BasicRelationalPersistentPropertyUnitTests { } public String toString() { - return "BasicRelationalPersistentPropertyUnitTests.DummyEntity(id=" + this.getId() + ", someEnum=" + this.getSomeEnum() + ", localDateTime=" + this.getLocalDateTime() + ", zonedDateTime=" + this.getZonedDateTime() + ", listOfString=" + this.getListOfString() + ", arrayOfString=" + java.util.Arrays.deepToString(this.getArrayOfString()) + ", listOfEntity=" + this.getListOfEntity() + ", arrayOfEntity=" + java.util.Arrays.deepToString(this.getArrayOfEntity()) + ", someList=" + this.getSomeList() + ", name=" + this.getName() + ", spelExpression1=" + this.getSpelExpression1() + ", littleBobbyTables=" + this.getLittleBobbyTables() + ", poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot=" + this.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot() + ", embeddableEntity=" + this.getEmbeddableEntity() + ", prefixedEmbeddableEntity=" + this.getPrefixedEmbeddableEntity() + ")"; + return "BasicRelationalPersistentPropertyUnitTests.DummyEntity(id=" + this.getId() + ", someEnum=" + + this.getSomeEnum() + ", localDateTime=" + this.getLocalDateTime() + ", zonedDateTime=" + + this.getZonedDateTime() + ", listOfString=" + this.getListOfString() + ", arrayOfString=" + + java.util.Arrays.deepToString(this.getArrayOfString()) + ", listOfEntity=" + this.getListOfEntity() + + ", arrayOfEntity=" + java.util.Arrays.deepToString(this.getArrayOfEntity()) + ", someList=" + + this.getSomeList() + ", name=" + this.getName() + ", spelExpression1=" + this.getSpelExpression1() + + ", littleBobbyTables=" + this.getLittleBobbyTables() + + ", poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot=" + + this.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot() + ", embeddableEntity=" + + this.getEmbeddableEntity() + ", prefixedEmbeddableEntity=" + this.getPrefixedEmbeddableEntity() + ")"; } } - static class WithMappedCollection { + private static class WithMappedCollection { @MappedCollection(idColumn = "#{'id_col'}", keyColumn = "#{'key_col'}") private List someList; } @@ -422,15 +486,18 @@ public class BasicRelationalPersistentPropertyUnitTests { this.embeddedTest = embeddedTest; } - public String getEmbeddedTest() { + String getEmbeddedTest() { return this.embeddedTest; } public boolean equals(final Object o) { - if (o == this) return true; - if (!(o instanceof EmbeddableEntity)) return false; + if (o == this) + return true; + if (!(o instanceof EmbeddableEntity)) + return false; final EmbeddableEntity other = (EmbeddableEntity) o; - if (!other.canEqual((Object) this)) return false; + if (!other.canEqual((Object) this)) + return false; final Object this$embeddedTest = this.getEmbeddedTest(); final Object other$embeddedTest = other.getEmbeddedTest(); if (this$embeddedTest == null ? other$embeddedTest != null : !this$embeddedTest.equals(other$embeddedTest)) @@ -438,7 +505,7 @@ public class BasicRelationalPersistentPropertyUnitTests { return true; } - protected boolean canEqual(final Object other) { + boolean canEqual(final Object other) { return other instanceof EmbeddableEntity; } @@ -457,4 +524,24 @@ public class BasicRelationalPersistentPropertyUnitTests { @SuppressWarnings("unused") private static class OtherEntity {} + + @Table("entity_with_sequence") + static class EntityWithSequence { + @Id + @Sequence(sequence = "my_seq") Long id; + } + + @Table("entity_with_sequence_value_alias") + static class EntityWithSequenceValueAlias { + @Id + @Column("myId") + @Sequence(value = "my_seq") Long id; + } + + @Table("entity_with_sequence_and_schema") + static class EntityWithSequenceAndSchema { + @Id + @Column("myId") + @Sequence(sequence = "my_seq", schema = "public") Long id; + } }