diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/MappingSupport.java b/src/main/java/org/springframework/data/neo4j/core/mapping/MappingSupport.java index e81f5414b..9383d43b3 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/MappingSupport.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/MappingSupport.java @@ -179,6 +179,9 @@ public static Object getRelationshipOrRelationshipPropertiesObject(Neo4jMappingC PersistentPropertyAccessor relationshipPropertiesAccessor = persistentEntity.getPropertyAccessor(relationshipPropertiesValue); relationshipPropertiesAccessor.setProperty(persistentEntity.getPersistentProperty(TargetNode.class), newRelationshipObject); newRelationshipObject = relationshipPropertiesAccessor.getBean(); + + // If we recreate or manipulate the object including it's accessor, we must update it in the holder as well. + entityHolder.setRelationshipProperties(newRelationshipObject); } return newRelationshipObject; } @@ -190,14 +193,17 @@ private MappingSupport() {} */ @API(status = API.Status.INTERNAL) public final static class RelationshipPropertiesWithEntityHolder { - private final PersistentPropertyAccessor relationshipPropertiesPropertyAccessor; - private final Object relationshipProperties; + + private final Neo4jPersistentEntity relationshipPropertiesEntity; + private PersistentPropertyAccessor relationshipPropertiesPropertyAccessor; + private Object relationshipProperties; private final Object relatedEntity; RelationshipPropertiesWithEntityHolder( Neo4jPersistentEntity relationshipPropertiesEntity, Object relationshipProperties, Object relatedEntity ) { + this.relationshipPropertiesEntity = relationshipPropertiesEntity; this.relationshipPropertiesPropertyAccessor = relationshipPropertiesEntity.getPropertyAccessor(relationshipProperties); this.relationshipProperties = relationshipProperties; this.relatedEntity = relatedEntity; @@ -211,6 +217,11 @@ public Object getRelationshipProperties() { return relationshipProperties; } + private void setRelationshipProperties(Object relationshipProperties) { + this.relationshipProperties = relationshipProperties; + this.relationshipPropertiesPropertyAccessor = relationshipPropertiesEntity.getPropertyAccessor(this.relationshipProperties); + } + public Object getRelatedEntity() { return relatedEntity; } @@ -231,5 +242,12 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(relationshipProperties, relatedEntity); } + + @Override + public String toString() { + return "RelationshipPropertiesWithEntityHolder{" + + "relationshipProperties=" + relationshipProperties + + '}'; + } } } diff --git a/src/test/java/org/springframework/data/neo4j/documentation/domain/MovieEntity.java b/src/test/java/org/springframework/data/neo4j/documentation/domain/MovieEntity.java index 1321042f4..19f4b3f88 100644 --- a/src/test/java/org/springframework/data/neo4j/documentation/domain/MovieEntity.java +++ b/src/test/java/org/springframework/data/neo4j/documentation/domain/MovieEntity.java @@ -44,7 +44,7 @@ public class MovieEntity { // tag::mapping.relationship.properties[] @Relationship(type = "ACTED_IN", direction = Direction.INCOMING) // <.> - private List actorsAndRoles; + private List actorsAndRoles = new ArrayList<>(); // end::mapping.relationship.properties[] @Relationship(type = "DIRECTED", direction = Direction.INCOMING) diff --git a/src/test/java/org/springframework/data/neo4j/documentation/domain/Roles.java b/src/test/java/org/springframework/data/neo4j/documentation/domain/Roles.java index 677442bda..1a01d1e59 100644 --- a/src/test/java/org/springframework/data/neo4j/documentation/domain/Roles.java +++ b/src/test/java/org/springframework/data/neo4j/documentation/domain/Roles.java @@ -41,8 +41,21 @@ public Roles(PersonEntity person, List roles) { this.roles = roles; } + // end::mapping.relationship.properties[] + public Long getId() { + return id; + } + // tag::mapping.relationship.properties[] + public List getRoles() { return roles; } + + @Override + public String toString() { + return "Roles{" + + "id=" + id + + '}' + this.hashCode(); + } } // end::mapping.relationship.properties[] diff --git a/src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java b/src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java index 73aa74aaa..b2c0cf262 100644 --- a/src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java +++ b/src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java @@ -23,24 +23,59 @@ import java.util.Optional; // end::faq.template-imperative-pt1[] -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.BeforeEach; // tag::faq.template-imperative-pt1[] import org.junit.jupiter.api.Test; +// end::faq.template-imperative-pt1[] +import org.neo4j.driver.Driver; +// tag::faq.template-imperative-pt1[] import org.springframework.beans.factory.annotation.Autowired; +// end::faq.template-imperative-pt1[] +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +// tag::faq.template-imperative-pt1[] import org.springframework.data.neo4j.core.Neo4jTemplate; +// end::faq.template-imperative-pt1[] +import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +// tag::faq.template-imperative-pt1[] import org.springframework.data.neo4j.documentation.domain.MovieEntity; import org.springframework.data.neo4j.documentation.domain.PersonEntity; import org.springframework.data.neo4j.documentation.domain.Roles; +// end::faq.template-imperative-pt1[] +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; +import org.springframework.data.neo4j.test.BookmarkCapture; +import org.springframework.data.neo4j.test.Neo4jExtension; +import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration; +import org.springframework.data.neo4j.test.Neo4jIntegrationTest; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +// tag::faq.template-imperative-pt1[] // end::faq.template-imperative-pt1[] /** * @author Michael J. Simons */ -@Disabled +@Neo4jIntegrationTest // tag::faq.template-imperative-pt2[] public class TemplateExampleTest { + // end::faq.template-imperative-pt2[] + + protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport; + + @BeforeEach + void setup(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { + try (var session = driver.session(bookmarkCapture.createSessionConfig()); var transaction = session.beginTransaction()) { + transaction.run("MATCH (n) detach delete n").consume(); + transaction.commit(); + bookmarkCapture.seedWith(session.lastBookmarks()); + } + } + + // tag::faq.template-imperative-pt2[] @Test void shouldSaveAndReadEntities(@Autowired Neo4jTemplate neo4jTemplate) { @@ -53,12 +88,45 @@ void shouldSaveAndReadEntities(@Autowired Neo4jTemplate neo4jTemplate) { movie.getActorsAndRoles().add(roles1); movie.getActorsAndRoles().add(roles2); - neo4jTemplate.save(movie); + MovieEntity result = neo4jTemplate.save(movie); + // end::mapping.relationship.properties[] + assertThat(result.getActorsAndRoles()).allSatisfy(relationship -> assertThat(relationship.getId()).isNotNull()); + // tag::mapping.relationship.properties[] Optional person = neo4jTemplate.findById("Dean Jones", PersonEntity.class); assertThat(person).map(PersonEntity::getBorn).hasValue(1931); assertThat(neo4jTemplate.count(PersonEntity.class)).isEqualTo(2L); } + + // end::faq.template-imperative-pt2[] + @Configuration + @EnableTransactionManagement + @EnableNeo4jRepositories(considerNestedRepositories = true) + static class Config extends Neo4jImperativeTestConfiguration { + + @Bean + public Driver driver() { + return neo4jConnectionSupport.getDriver(); + } + + @Bean + public BookmarkCapture bookmarkCapture() { + return new BookmarkCapture(); + } + + @Override + public PlatformTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider) { + + BookmarkCapture bookmarkCapture = bookmarkCapture(); + return new Neo4jTransactionManager(driver, databaseNameProvider, Neo4jBookmarkManager.create(bookmarkCapture)); + } + + @Override + public boolean isCypher5Compatible() { + return neo4jConnectionSupport.isCypher5SyntaxCompatible(); + } + } + // tag::faq.template-imperative-pt2[] } // end::faq.template-imperative-pt2[]