Skip to content

Commit 45a8a58

Browse files
committed
GH-2530 - Support lazy defined entities.
Closes #2530
1 parent 66be59c commit 45a8a58

File tree

5 files changed

+248
-5
lines changed

5 files changed

+248
-5
lines changed

src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jPersistentEntity.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ final class DefaultNeo4jPersistentEntity<T> extends BasicPersistentEntity<T, Neo
7878

7979
private final Set<NodeDescription<?>> childNodeDescriptions = new HashSet<>();
8080

81-
private NodeDescription<?> parentNodeDescription;
82-
8381
private final Lazy<Neo4jPersistentProperty> dynamicLabelsProperty;
8482

8583
private final Lazy<Boolean> isRelationshipPropertiesEntity;
8684

87-
private final Lazy<List<NodeDescription<?>>> childNodeDescriptionsInHierarchy;
85+
private NodeDescription<?> parentNodeDescription;
86+
87+
private List<NodeDescription<?>> childNodeDescriptionsInHierarchy;
8888

8989
DefaultNeo4jPersistentEntity(TypeInformation<T> information) {
9090
super(information);
@@ -96,7 +96,7 @@ final class DefaultNeo4jPersistentEntity<T> extends BasicPersistentEntity<T, Neo
9696
.filter(Neo4jPersistentProperty::isDynamicLabels).findFirst().orElse(null));
9797
this.isRelationshipPropertiesEntity = Lazy.of(() -> isAnnotationPresent(RelationshipProperties.class));
9898
this.idDescription = Lazy.of(this::computeIdDescription);
99-
this.childNodeDescriptionsInHierarchy = Lazy.of(this::computeChildNodeDescriptionInHierarchy);
99+
this.childNodeDescriptionsInHierarchy = computeChildNodeDescriptionInHierarchy();
100100
}
101101

102102
/*
@@ -514,11 +514,19 @@ public Collection<GraphPropertyDescription> getGraphPropertiesInHierarchy() {
514514
@Override
515515
public void addChildNodeDescription(NodeDescription<?> child) {
516516
this.childNodeDescriptions.add(child);
517+
updateChildNodeDescriptionCache();
518+
if (this.parentNodeDescription != null) {
519+
((DefaultNeo4jPersistentEntity<?>) this.parentNodeDescription).updateChildNodeDescriptionCache();
520+
}
521+
}
522+
523+
private void updateChildNodeDescriptionCache() {
524+
this.childNodeDescriptionsInHierarchy = computeChildNodeDescriptionInHierarchy();
517525
}
518526

519527
@Override
520528
public List<NodeDescription<?>> getChildNodeDescriptionsInHierarchy() {
521-
return childNodeDescriptionsInHierarchy.get();
529+
return childNodeDescriptionsInHierarchy;
522530
}
523531

524532
private List<NodeDescription<?>> computeChildNodeDescriptionInHierarchy() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2530;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
import org.springframework.data.neo4j.integration.issues.gh2530.domain.InitialEntities;
20+
21+
/**
22+
* The entity to get discovered later.
23+
*/
24+
@Node
25+
public class ConcreteImplementationTwo extends InitialEntities.SomethingInBetween implements InitialEntities.SpecialKind {
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2530;
17+
18+
import org.assertj.core.api.SoftAssertions;
19+
import org.junit.jupiter.api.BeforeEach;
20+
import org.junit.jupiter.api.Test;
21+
import org.neo4j.driver.Driver;
22+
import org.neo4j.driver.Session;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
27+
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
28+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
29+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
30+
import org.springframework.data.neo4j.integration.issues.gh2530.domain.InitialEntities;
31+
import org.springframework.data.neo4j.repository.Neo4jRepository;
32+
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
33+
import org.springframework.data.neo4j.test.BookmarkCapture;
34+
import org.springframework.data.neo4j.test.Neo4jExtension;
35+
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
36+
import org.springframework.transaction.PlatformTransactionManager;
37+
import org.springframework.transaction.annotation.EnableTransactionManagement;
38+
39+
import java.util.Collection;
40+
import java.util.Collections;
41+
42+
import static org.assertj.core.api.Assertions.assertThat;
43+
44+
/**
45+
* Testing lazy added entities
46+
*/
47+
@Neo4jIntegrationTest
48+
public class GH2530IT {
49+
50+
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
51+
52+
@Autowired
53+
SomethingInBetweenRepository repository;
54+
55+
@BeforeEach
56+
void setupData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
57+
58+
try (Session session = driver.session()) {
59+
session.run("MATCH (n) DETACH DELETE n").consume();
60+
bookmarkCapture.seedWith(session.lastBookmark());
61+
}
62+
}
63+
64+
@Test // GH-2530
65+
void shouldPutLazyFoundEntityIntoHierarchy() {
66+
InitialEntities.ConcreteImplementationOne cc1 = new InitialEntities.ConcreteImplementationOne();
67+
cc1.name = "CC1";
68+
repository.save(cc1);
69+
70+
InitialEntities.SpecialKind foundCC1 = (InitialEntities.SpecialKind) repository.findById(cc1.id).get();
71+
SoftAssertions softly = new SoftAssertions();
72+
softly.assertThat(foundCC1).as("type").isInstanceOf(InitialEntities.ConcreteImplementationOne.class);
73+
softly.assertThat(((InitialEntities.ConcreteImplementationOne) foundCC1).name).as("CC1").isNotEmpty();
74+
softly.assertAll();
75+
76+
ConcreteImplementationTwo cat = new ConcreteImplementationTwo();
77+
repository.save(cat);
78+
79+
InitialEntities.SpecialKind foundCC2 = (InitialEntities.SpecialKind) repository.findById(cat.id).get();
80+
assertThat(foundCC2).as("type").isInstanceOf(ConcreteImplementationTwo.class);
81+
}
82+
83+
interface SomethingInBetweenRepository extends Neo4jRepository<InitialEntities.SomethingInBetween, String> {}
84+
85+
@Configuration
86+
@EnableTransactionManagement
87+
@EnableNeo4jRepositories(considerNestedRepositories = true)
88+
static class Config extends AbstractNeo4jConfig {
89+
90+
@Bean
91+
public BookmarkCapture bookmarkCapture() {
92+
return new BookmarkCapture();
93+
}
94+
95+
@Override
96+
public PlatformTransactionManager transactionManager(
97+
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
98+
99+
BookmarkCapture bookmarkCapture = bookmarkCapture();
100+
return new Neo4jTransactionManager(driver, databaseNameProvider,
101+
Neo4jBookmarkManager.create(bookmarkCapture));
102+
}
103+
104+
@Override
105+
protected Collection<String> getMappingBasePackages() {
106+
return Collections.singleton(InitialEntities.class.getPackage().getName());
107+
}
108+
109+
@Bean
110+
public Driver driver() {
111+
112+
return neo4jConnectionSupport.getDriver();
113+
}
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2530.domain;
17+
18+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
19+
import org.springframework.data.neo4j.core.schema.Id;
20+
import org.springframework.data.neo4j.core.schema.Node;
21+
22+
/**
23+
* Collection of initial entities to get registered on startup.
24+
*/
25+
public class InitialEntities {
26+
27+
/**
28+
* Base
29+
*/
30+
public static abstract class BaseEntity {
31+
32+
@Id
33+
@GeneratedValue(generatorClass = SomeStringGenerator.class)
34+
public String id;
35+
36+
}
37+
38+
/**
39+
* This is where the repository accesses the domain.
40+
*/
41+
@Node
42+
public static abstract class SomethingInBetween extends BaseEntity {
43+
public String name;
44+
}
45+
46+
/**
47+
* Parallel node type
48+
*/
49+
@Node
50+
public interface SpecialKind {
51+
52+
}
53+
54+
/**
55+
* Concrete implementation registered on startup.
56+
*/
57+
@Node
58+
public static class ConcreteImplementationOne extends SomethingInBetween implements SpecialKind {
59+
60+
}
61+
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2530.domain;
17+
18+
import org.springframework.data.neo4j.core.schema.IdGenerator;
19+
20+
import java.util.UUID;
21+
22+
/**
23+
* Generator to mimic the reported behaviour.
24+
*/
25+
public class SomeStringGenerator implements IdGenerator<String> {
26+
27+
@Override
28+
public String generateId(String primaryLabel, Object entity) {
29+
return primaryLabel + UUID.randomUUID();
30+
}
31+
}

0 commit comments

Comments
 (0)