From 2cad20f4b7394d87b736341f9ea148cd513f2b4e Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 1 Sep 2020 12:20:46 +0200 Subject: [PATCH] DATAGRAPH-1212 - Improve fallback IsNewStrategy. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change replaces the previous fallback IsNewStrategy with a backport from SDN/RX respectivley SDN 6. This takes much better care of handling externally assigned and primitive ids together with version properties and fails fast when it can’t cater for a scenario. In addition to the required OGM update, this makes it possible to audit entities with externally generated or assigned ids. For this scenario, tests have been added as well. --- .../mapping/DefaultNeo4jIsNewStrategy.java | 146 ++++++++++++++++++ .../neo4j/mapping/Neo4jMappingContext.java | 2 +- .../neo4j/mapping/Neo4jPersistentEntity.java | 40 +---- .../mapping/Neo4jPersistentProperty.java | 2 +- .../JavaConfigurationAuditingTests.java | 43 +++++- .../auditing/domain/UserCustomIdStrategy.java | 95 ++++++++++++ .../UserCustomIdStrategyRepository.java | 27 ++++ .../DefaultNeo4jIsNewStrategyTests.java | 133 ++++++++++++++++ .../AssignedWithPrimitiveVersion.java | 44 ++++++ .../datagraph1212/AssignedWithVersion.java | 44 ++++++ .../datagraph1212/AssignedWithoutVersion.java | 35 +++++ .../ExternallyGeneratedNonPrimitive.java | 38 +++++ .../ExternallyGeneratedPrimitive.java | 29 ++++ .../InternallyGeneratedNonPrimitive.java | 33 ++++ .../InternallyGeneratedPrimitive.java | 33 ++++ .../WeAllAreFloatingStrategy.java | 34 ++++ 16 files changed, 742 insertions(+), 36 deletions(-) create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategy.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/domain/UserCustomIdStrategy.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/repository/UserCustomIdStrategyRepository.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategyTests.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithPrimitiveVersion.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithVersion.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithoutVersion.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedNonPrimitive.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedPrimitive.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedNonPrimitive.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedPrimitive.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/WeAllAreFloatingStrategy.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategy.java new file mode 100644 index 000000000..ed9fcbaef --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategy.java @@ -0,0 +1,146 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping; + +import java.util.function.Function; + +import org.neo4j.ogm.id.InternalIdStrategy; +import org.neo4j.ogm.metadata.ClassInfo; +import org.neo4j.ogm.metadata.MetaData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.support.IsNewStrategy; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * Implementation of a {@link IsNewStrategy} that follows our supported identifiers and generators. Entities will be + * treated as new: + * + *

+ * An entity will not be treated as new + *

+ * + * @author Michael J. Simons + * @since 5.1.20 + */ +class DefaultNeo4jIsNewStrategy implements IsNewStrategy { + + private static final Logger log = LoggerFactory.getLogger(DefaultNeo4jIsNewStrategy.class); + + static IsNewStrategy basedOn(Neo4jPersistentEntity entity, MetaData ogmMetadata) { + + Assert.notNull(entity, "Entity meta data must not be null."); + + ClassInfo classInfo = ogmMetadata.classInfo(entity.getType()); + boolean internallyGeneratedId = classInfo.hasIdentityField(); + boolean externallyGeneratedId = + classInfo.idStrategyClass() != null && InternalIdStrategy.class != classInfo.idStrategyClass(); + boolean assignedId = !internallyGeneratedId && classInfo.idStrategyClass() == null; + + Class valueType; + if (classInfo.hasIdentityField()) { + valueType = classInfo.identityField().type(); + } else if (classInfo.hasPrimaryIndexField()) { + valueType = classInfo.primaryIndexField().type(); + } else { + throw new IllegalStateException( + String.format("Required identifier property not found for %s!", entity.getType())); + } + + if (externallyGeneratedId && valueType.isPrimitive()) { + throw new IllegalArgumentException(String.format("Cannot use %s with externally generated, primitive ids.", + DefaultNeo4jIsNewStrategy.class.getName())); + } + + Function valueLookup; + Neo4jPersistentProperty versionProperty = entity.getVersionProperty(); + if (assignedId) { + if (versionProperty == null) { + log.warn("Instances of " + entity.getType() + + " with an assigned id will always be treated as new without version property!"); + valueType = Void.class; + valueLookup = source -> null; + } else { + valueType = versionProperty.getType(); + valueLookup = source -> entity.getPropertyAccessor(source).getProperty(versionProperty); + } + } else { + valueLookup = source -> entity.getIdentifierAccessor(source).getIdentifier(); + } + + return new DefaultNeo4jIsNewStrategy(internallyGeneratedId, externallyGeneratedId, assignedId, valueType, + valueLookup); + } + + private final boolean internallyGeneratedId; + + private final boolean externallyGeneratedId; + + private final boolean assignedId; + + private final Class valueType; + + private @Nullable final Function valueLookup; + + private DefaultNeo4jIsNewStrategy(boolean internallyGeneratedId, boolean externallyGeneratedId, boolean assignedId, + Class valueType, @Nullable Function valueLookup) { + this.internallyGeneratedId = internallyGeneratedId; + this.externallyGeneratedId = externallyGeneratedId; + this.assignedId = assignedId; + this.valueType = valueType; + this.valueLookup = valueLookup; + } + + /* + * (non-Javadoc) + * @see IsNewStrategy#isNew(Object) + */ + @Override + public boolean isNew(Object entity) { + + Object value = valueLookup.apply(entity); + if (internallyGeneratedId) { + return value == null || (value instanceof Long && ((Long) value) < 0); + } else if (externallyGeneratedId) { + return value == null; + } else if (assignedId) { + if (valueType != null && !valueType.isPrimitive()) { + return value == null; + } + + if (Number.class.isInstance(value)) { + return ((Number) value).longValue() == 0; + } + } + + throw new IllegalArgumentException( + String.format("Could not determine whether %s is new! Unsupported identifier or version property!", + entity)); + + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jMappingContext.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jMappingContext.java index b7b6bfa30..eb963fe03 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jMappingContext.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jMappingContext.java @@ -127,7 +127,7 @@ public class Neo4jMappingContext extends AbstractMappingContext Neo4jPersistentEntity createPersistentEntity(TypeInformation typeInformation) { logger.debug("Creating Neo4jPersistentEntity from type information: {}", typeInformation); - return new Neo4jPersistentEntity<>(typeInformation); + return new Neo4jPersistentEntity<>(typeInformation, this.metaData); } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentEntity.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentEntity.java index 654f9dc6f..0d22cf49c 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentEntity.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentEntity.java @@ -15,11 +15,12 @@ */ package org.springframework.data.neo4j.mapping; +import org.neo4j.ogm.metadata.MetaData; import org.springframework.data.mapping.MappingException; -import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty.PropertyType; import org.springframework.data.support.IsNewStrategy; +import org.springframework.data.util.Lazy; import org.springframework.data.util.TypeInformation; /** @@ -47,13 +48,17 @@ import org.springframework.data.util.TypeInformation; */ public class Neo4jPersistentEntity extends BasicPersistentEntity { + private final Lazy fallbackIsNewStrategy; + /** * Constructs a new {@link Neo4jPersistentEntity} based on the given type information. * * @param information The {@link TypeInformation} upon which to base this persistent entity. */ - Neo4jPersistentEntity(TypeInformation information) { + Neo4jPersistentEntity(TypeInformation information, MetaData metaData) { + super(information); + this.fallbackIsNewStrategy = Lazy.of(() -> DefaultNeo4jIsNewStrategy.basedOn(this, metaData)); } /* @@ -62,7 +67,7 @@ public class Neo4jPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity entity; - - private Neo4jIsNewStrategy(Neo4jPersistentEntity entity) { - this.entity = entity; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.support.IsNewStrategy#isNew(java.lang.Object) - */ - @Override - public boolean isNew(Object bean) { - - PersistentProperty> property = entity.getRequiredIdProperty(); - Object value = entity.getPropertyAccessor(bean).getProperty(property); - - return value == null || (value instanceof Long && ((Long) value) < 0); - } - } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java index 74b60826a..534a683e8 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java @@ -187,6 +187,6 @@ public class Neo4jPersistentProperty extends AnnotationBasedPersistentProperty createAssociation() { - return new Association(this, null); + return new Association<>(this, null); } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/JavaConfigurationAuditingTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/JavaConfigurationAuditingTests.java index 5509927b1..13f24b810 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/JavaConfigurationAuditingTests.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/JavaConfigurationAuditingTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.neo4j.auditing; -import static java.util.Optional.*; -import static org.assertj.core.api.Assertions.*; +import static java.util.Optional.of; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -30,6 +31,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; import org.springframework.data.neo4j.annotation.EnableNeo4jAuditing; import org.springframework.data.neo4j.auditing.domain.User; +import org.springframework.data.neo4j.auditing.domain.UserCustomIdStrategy; +import org.springframework.data.neo4j.auditing.repository.UserCustomIdStrategyRepository; import org.springframework.data.neo4j.auditing.repository.UserRepository; import org.springframework.data.neo4j.test.Neo4jIntegrationTest; import org.springframework.test.context.ContextConfiguration; @@ -57,6 +60,8 @@ public class JavaConfigurationAuditingTests { @Autowired private UserRepository userRepository; + @Autowired private UserCustomIdStrategyRepository userCustomIdStrategyRepository; + @Test public void whenSaveEntity_thenSetCreatedAndCreatedBy() { User user = new User("John Doe"); @@ -89,4 +94,38 @@ public class JavaConfigurationAuditingTests { assertThat(found.getModified()).isNotNull().isCloseTo(LocalDateTime.now(), within(1, ChronoUnit.SECONDS)); assertThat(found.getModifiedBy()).isEqualTo("userId"); } + + @Test // DATAGRAPH-1212 + public void shouldAuditEntitiesWithCustomIdStrategyOnCreation() { + + UserCustomIdStrategy user = new UserCustomIdStrategy("John Doe"); + userCustomIdStrategyRepository.save(user); + + Optional loaded = userCustomIdStrategyRepository.findById(user.getId()); + assertThat(loaded).hasValueSatisfying(found -> { + + assertThat(found.getCreated()).isNotNull().isCloseTo(LocalDateTime.now(), within(1, ChronoUnit.SECONDS)); + assertThat(found.getCreatedBy()).isEqualTo("userId"); + + assertThat(found.getModified()).isNull(); + assertThat(found.getModifiedBy()).isNull(); + }); + } + + @Test // DATAGRAPH-1212 + public void shouldAuditEntitiesWithCustomIdStrategyOnUpdate() { + + UserCustomIdStrategy user = new UserCustomIdStrategy("John Doe"); + userCustomIdStrategyRepository.save(user); + + user.setName("Johan Doe"); + userCustomIdStrategyRepository.save(user); + + Optional loaded = userCustomIdStrategyRepository.findById(user.getId()); + assertThat(loaded).hasValueSatisfying(found -> { + + assertThat(found.getModified()).isNotNull().isCloseTo(LocalDateTime.now(), within(1, ChronoUnit.SECONDS)); + assertThat(found.getModifiedBy()).isEqualTo("userId"); + }); + } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/domain/UserCustomIdStrategy.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/domain/UserCustomIdStrategy.java new file mode 100644 index 000000000..894c3960b --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/domain/UserCustomIdStrategy.java @@ -0,0 +1,95 @@ +/* + * Copyright 2011-2020 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.neo4j.auditing.domain; + +import java.time.LocalDateTime; +import java.util.UUID; + +import org.neo4j.ogm.annotation.GeneratedValue; +import org.neo4j.ogm.annotation.Id; +import org.neo4j.ogm.annotation.NodeEntity; +import org.neo4j.ogm.annotation.typeconversion.Convert; +import org.neo4j.ogm.id.UuidStrategy; +import org.neo4j.ogm.typeconversion.UuidStringConverter; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; + +@NodeEntity +public class UserCustomIdStrategy { + + @Id @GeneratedValue(strategy = UuidStrategy.class) @Convert(UuidStringConverter.class) + UUID id; + + String name; + + @CreatedDate LocalDateTime created; + + @CreatedBy String createdBy; + + @LastModifiedDate LocalDateTime modified; + + @LastModifiedBy String modifiedBy; + + public UserCustomIdStrategy(String name) { + this.name = name; + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDateTime getCreated() { + return created; + } + + public void setCreated(LocalDateTime created) { + this.created = created; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getModified() { + return modified; + } + + public void setModified(LocalDateTime modified) { + this.modified = modified; + } + + public String getModifiedBy() { + return modifiedBy; + } + + public void setModifiedBy(String modifiedBy) { + this.modifiedBy = modifiedBy; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/repository/UserCustomIdStrategyRepository.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/repository/UserCustomIdStrategyRepository.java new file mode 100644 index 000000000..b61e1f1e8 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/auditing/repository/UserCustomIdStrategyRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2011-2020 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.neo4j.auditing.repository; + +import java.util.UUID; + +import org.springframework.data.neo4j.auditing.domain.User; +import org.springframework.data.neo4j.auditing.domain.UserCustomIdStrategy; +import org.springframework.data.neo4j.repository.Neo4jRepository; + +/** + * @author Michael J. Simons + */ +public interface UserCustomIdStrategyRepository extends Neo4jRepository {} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategyTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategyTests.java new file mode 100644 index 000000000..2392b979b --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/DefaultNeo4jIsNewStrategyTests.java @@ -0,0 +1,133 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +import java.util.UUID; + +import org.junit.Test; +import org.neo4j.ogm.metadata.MetaData; +import org.springframework.data.neo4j.mapping.datagraph1212.AssignedWithPrimitiveVersion; +import org.springframework.data.neo4j.mapping.datagraph1212.AssignedWithVersion; +import org.springframework.data.neo4j.mapping.datagraph1212.AssignedWithoutVersion; +import org.springframework.data.neo4j.mapping.datagraph1212.ExternallyGeneratedNonPrimitive; +import org.springframework.data.neo4j.mapping.datagraph1212.ExternallyGeneratedPrimitive; +import org.springframework.data.neo4j.mapping.datagraph1212.InternallyGeneratedNonPrimitive; +import org.springframework.data.neo4j.mapping.datagraph1212.InternallyGeneratedPrimitive; +import org.springframework.data.support.IsNewStrategy; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class DefaultNeo4jIsNewStrategyTests { + + MetaData metaData = new MetaData("org.springframework.data.neo4j.mapping.datagraph1212"); + + Neo4jMappingContext mappingContext = new Neo4jMappingContext(metaData); + + @Test + public void shouldDealWithNonPrimitiveInternalIds() { + InternallyGeneratedNonPrimitive a = new InternallyGeneratedNonPrimitive(null); + InternallyGeneratedNonPrimitive b = new InternallyGeneratedNonPrimitive(1L); + + IsNewStrategy strategy = DefaultNeo4jIsNewStrategy + .basedOn(mappingContext.getPersistentEntity(InternallyGeneratedNonPrimitive.class), metaData); + assertThat(strategy.isNew(a)).isTrue(); + assertThat(strategy.isNew(b)).isFalse(); + } + + @Test + public void shouldDealWithPrimitiveInternalIds() { + InternallyGeneratedPrimitive a = new InternallyGeneratedPrimitive(-1L); + InternallyGeneratedPrimitive b = new InternallyGeneratedPrimitive(0L); + InternallyGeneratedPrimitive c = new InternallyGeneratedPrimitive(1L); + + IsNewStrategy strategy = DefaultNeo4jIsNewStrategy + .basedOn(mappingContext.getPersistentEntity(InternallyGeneratedPrimitive.class), metaData); + assertThat(strategy.isNew(a)).isTrue(); + assertThat(strategy.isNew(b)).isFalse(); + assertThat(strategy.isNew(c)).isFalse(); + } + + @Test + public void shouldDealWithNonPrimitiveExternalIds() { + ExternallyGeneratedNonPrimitive a = new ExternallyGeneratedNonPrimitive(null); + ExternallyGeneratedNonPrimitive b = new ExternallyGeneratedNonPrimitive(UUID.randomUUID()); + + IsNewStrategy strategy = DefaultNeo4jIsNewStrategy + .basedOn(mappingContext.getPersistentEntity(ExternallyGeneratedNonPrimitive.class), metaData); + assertThat(strategy.isNew(a)).isTrue(); + assertThat(strategy.isNew(b)).isFalse(); + } + + @Test + public void doesntNeedToDealWithPrimitiveExternalIds() { + + assertThatIllegalArgumentException() + .isThrownBy(() -> DefaultNeo4jIsNewStrategy + .basedOn(mappingContext.getPersistentEntity(ExternallyGeneratedPrimitive.class), metaData)) + .withMessage( + "Cannot use org.springframework.data.neo4j.mapping.DefaultNeo4jIsNewStrategy with externally generated, primitive ids."); + } + + @Test + public void shouldAlwaysTreatEntitiesAsNewWithoutVersionAndAssignedIds() { + + Neo4jMappingContext context = new Neo4jMappingContext(metaData); + + IsNewStrategy strategy = DefaultNeo4jIsNewStrategy + .basedOn(context.getPersistentEntity(AssignedWithoutVersion.class), metaData); + assertThat(strategy.isNew(new AssignedWithoutVersion())).isTrue(); + assertThat(strategy.isNew(new AssignedWithoutVersion("someId"))).isTrue(); + } + + @Test + public void shouldDealWithVersionAndAssignedIds() { + + AssignedWithVersion a = new AssignedWithVersion(); + AssignedWithVersion b = new AssignedWithVersion("someId"); + AssignedWithVersion c = new AssignedWithVersion("someId", 1); + + Neo4jMappingContext context = new Neo4jMappingContext(metaData); + + IsNewStrategy strategy = DefaultNeo4jIsNewStrategy + .basedOn(context.getPersistentEntity(AssignedWithVersion.class), metaData); + + assertThat(strategy.isNew(a)).isTrue(); + assertThat(strategy.isNew(b)).isTrue(); + assertThat(strategy.isNew(c)).isFalse(); + } + + @Test + public void shouldDealWithPrimitiveVersionAndAssignedIds() { + AssignedWithPrimitiveVersion a = new AssignedWithPrimitiveVersion(); + AssignedWithPrimitiveVersion b = new AssignedWithPrimitiveVersion("someId"); + AssignedWithPrimitiveVersion c = new AssignedWithPrimitiveVersion("someId", 1); + + Neo4jMappingContext context = new Neo4jMappingContext(metaData); + + IsNewStrategy strategy = DefaultNeo4jIsNewStrategy + .basedOn(context.getPersistentEntity(AssignedWithPrimitiveVersion.class), metaData); + + assertThat(strategy.isNew(a)).isTrue(); + assertThat(strategy.isNew(b)).isTrue(); + assertThat(strategy.isNew(c)).isFalse(); + + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithPrimitiveVersion.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithPrimitiveVersion.java new file mode 100644 index 000000000..79a1b926d --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithPrimitiveVersion.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import org.neo4j.ogm.annotation.Id; +import org.neo4j.ogm.annotation.Version; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class AssignedWithPrimitiveVersion { + + @Id + private String id; + + @Version + private int version; + + public AssignedWithPrimitiveVersion() { + } + + public AssignedWithPrimitiveVersion(String id) { + this.id = id; + } + + public AssignedWithPrimitiveVersion(String id, int version) { + this.id = id; + this.version = version; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithVersion.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithVersion.java new file mode 100644 index 000000000..d0c65db76 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithVersion.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import org.neo4j.ogm.annotation.Id; +import org.neo4j.ogm.annotation.Version; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class AssignedWithVersion { + + @Id + private String id; + + @Version + private Integer version; + + public AssignedWithVersion() { + } + + public AssignedWithVersion(String id) { + this.id = id; + } + + public AssignedWithVersion(String id, Integer version) { + this.id = id; + this.version = version; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithoutVersion.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithoutVersion.java new file mode 100644 index 000000000..491f06a3f --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/AssignedWithoutVersion.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import org.neo4j.ogm.annotation.Id; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class AssignedWithoutVersion { + + @Id + private String id; + + public AssignedWithoutVersion() { + } + + public AssignedWithoutVersion(String id) { + this.id = id; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedNonPrimitive.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedNonPrimitive.java new file mode 100644 index 000000000..22924775b --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedNonPrimitive.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import java.util.UUID; + +import org.neo4j.ogm.annotation.GeneratedValue; +import org.neo4j.ogm.annotation.Id; +import org.neo4j.ogm.annotation.typeconversion.Convert; +import org.neo4j.ogm.id.UuidStrategy; +import org.neo4j.ogm.typeconversion.UuidStringConverter; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class ExternallyGeneratedNonPrimitive { + + @Id @GeneratedValue(strategy = UuidStrategy.class) @Convert(UuidStringConverter.class) + private UUID id; + + public ExternallyGeneratedNonPrimitive(UUID id) { + this.id = id; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedPrimitive.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedPrimitive.java new file mode 100644 index 000000000..8770d8ce0 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/ExternallyGeneratedPrimitive.java @@ -0,0 +1,29 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import org.neo4j.ogm.annotation.GeneratedValue; +import org.neo4j.ogm.annotation.Id; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class ExternallyGeneratedPrimitive { + + @Id @GeneratedValue(strategy = WeAllAreFloatingStrategy.class) + private float id; +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedNonPrimitive.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedNonPrimitive.java new file mode 100644 index 000000000..2234839a5 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedNonPrimitive.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import org.neo4j.ogm.annotation.GeneratedValue; +import org.neo4j.ogm.annotation.Id; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class InternallyGeneratedNonPrimitive { + + @Id @GeneratedValue + private Long id; + + public InternallyGeneratedNonPrimitive(Long id) { + this.id = id; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedPrimitive.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedPrimitive.java new file mode 100644 index 000000000..248c566f3 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/InternallyGeneratedPrimitive.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import org.neo4j.ogm.annotation.GeneratedValue; +import org.neo4j.ogm.annotation.Id; + +/** + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public class InternallyGeneratedPrimitive { + + @Id @GeneratedValue + private long id; + + public InternallyGeneratedPrimitive(long id) { + this.id = id; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/WeAllAreFloatingStrategy.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/WeAllAreFloatingStrategy.java new file mode 100644 index 000000000..06473a314 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/datagraph1212/WeAllAreFloatingStrategy.java @@ -0,0 +1,34 @@ +/* + * Copyright 2011-2020 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.neo4j.mapping.datagraph1212; + +import java.util.concurrent.ThreadLocalRandom; + +import org.neo4j.ogm.id.IdStrategy; + +/** + * Don't use this at home! + * + * @author Michael J. Simons + * @soundtrack Metallica - Helping Hands… Live & Acoustic At The Masonic + */ +public final class WeAllAreFloatingStrategy implements IdStrategy { + + @Override + public Object generateId(Object entity) { + return ThreadLocalRandom.current().nextFloat(); + } +}