我有两个@NodeEntities
使用简单映射通过 SDN 映射的,PersonNode
和FamilyNode
. FamilyNode
有@RelatedTo
收藏,孩子们。我还有一个FamilyService
(使用 Spring 的@Service
注释)在方法@Transactional
上带有注释updateFamily
。此方法加载FamilyNode
给定的 id,并使用回调接口修改节点。在回调的一种实现中,我将 a 添加PersonNode
到 children 集合中,这将生成NotInTransactionException
,特别是在 Neo4J 试图创建 和 之间的关系FamilyNode
时PersonNode
。
源代码可以在github上找到,尤其是失败的测试。以下是代码的相关位:
FamilyNode.java:
@NodeEntity
public class FamilyNode implements Family {
@Indexed(indexName = "families", unique = true)
private String id;
@GraphId
private Long identifier;
@RelatedTo(elementClass = PersonNode.class, type = "CHILD")
private Set<Person> children;
void addChild(Person child) {
if (this.children == null) {
this.children = new HashSet<>();
}
this.children.add(child);
}
}
PersonNode.java:
@NodeEntity
public class PersonNode implements Person {
@RelatedTo(elementClass = FamilyNode.class, type = "CHILD", direction = INCOMING)
private Family childOf;
@Indexed(indexName = "people", unique = true)
private String id;
@GraphId
private Long identifier;
}
FamilyRepository.java:
public interface FamilyRepository extends GraphRepository<Family> {
public FamilyNode findById(String id);
}
FamilyServiceImpl.java:
@Service
public class FamilyServiceImpl implements FamilyService {
@Autowired
private FamilyRepository families;
@Autowired
private Neo4jTemplate template;
@Override
public List<Family> getFamilies(String[] ids) {
List<Family> families = new ArrayList<>();
for (String id : ids) {
families.add(getFamily(id));
}
return families;
}
@Override
public Family getFamily(String id) {
return familyNode(id);
}
@Override
@Transactional
public Family createFamily(Family family) {
return lazyLoadRelationships((FamilyNode) this.families.save(family));
}
@Override
@Transactional
public Family updateFamily(String id, FamilyNodeUpdater updater) {
return this.families.save(updater.update(familyNode(id)));
}
private FamilyNode familyNode(String id) {
return lazyLoadRelationships(this.families.findById(id));
}
private FamilyNode lazyLoadRelationships(FamilyNode family) {
this.template.fetch(family.getFather());
this.template.fetch(family.getMother());
this.template.fetch(family.getChildren());
return family;
}
}
和失败的测试,FamilyServiceTest.java:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { TestConfig.class })
public class FamilyServiceTest {
@Configuration
@ComponentScan(basePackageClasses = { com.bonevich.ancestral.family.FamilyServiceImpl.class }, resourcePattern = "FamilyServiceImpl.class")
@EnableNeo4jRepositories(basePackageClasses = { com.bonevich.ancestral.family.FamilyNode.class })
static class TestConfig extends Neo4jConfiguration {
@Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabaseBuilder("/data/neo4j/ancestral-familyservicetest/")
.newGraphDatabase();
}
}
@Autowired
private FamilyService families;
@Autowired
private GraphDatabaseService graphDatabaseService;
@Autowired
private Neo4jTemplate neo4jTemplate;
@Test
public void testUpdateFamily() {
this.families.createFamily(FamilyNode.instance("testFamily"));
Transaction tx = this.graphDatabaseService.beginTx();
PersonNode person = PersonNode.instance("John", "Johanson", "M", "a_person");
PersonNode expectedChild = this.neo4jTemplate.save(person);
final long childId = expectedChild.getIdentifier();
tx.success();
tx.finish();
Family actualFamily = this.families.updateFamily("testFamily", new FamilyNodeUpdater() {
@Override
public FamilyNode update(FamilyNode family) {
family.addChild(FamilyServiceTest.this.neo4jTemplate.findOne(childId, PersonNode.class));
return family;
}
});
assertThat(actualFamily.getId(), is("testFamily"));
assertThat(actualFamily.getChildren().get(0), is((Person) expectedChild));
}
}
运行此测试会产生以下异常堆栈:
org.neo4j.graphdb.NotInTransactionException
at org.neo4j.kernel.impl.core.NoTransactionState.acquireWriteLock(NoTransactionState.java:43)
at org.neo4j.kernel.impl.transaction.LockType$2.acquire(LockType.java:51)
at org.neo4j.kernel.impl.core.NodeManager.getNodeForProxy(NodeManager.java:473)
at org.neo4j.kernel.InternalAbstractGraphDatabase$6.lookup(InternalAbstractGraphDatabase.java:733)
at org.neo4j.kernel.impl.core.NodeProxy.createRelationshipTo(NodeProxy.java:207)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.obtainSingleRelationship(RelationshipHelper.java:62)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createSingleRelationship(RelationshipHelper.java:142)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createAddedRelationships(RelationshipHelper.java:96)
at org.springframework.data.neo4j.fieldaccess.RelatedToFieldAccessor.createAddedRelationships(RelatedToFieldAccessor.java:78)
at org.springframework.data.neo4j.fieldaccess.RelatedToCollectionFieldAccessorFactory$RelatedToCollectionFieldAccessor.setValue(RelatedToCollectionFieldAccessorFactory.java:68)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.updateValue(ManagedFieldAccessorSet.java:112)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.update(ManagedFieldAccessorSet.java:100)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.add(ManagedFieldAccessorSet.java:126)
at com.bonevich.ancestral.family.FamilyNode.addChild(FamilyNode.java:124)
at com.bonevich.ancestral.family.FamilyServiceTest$1.update(FamilyServiceTest.java:64)
at com.bonevich.ancestral.family.FamilyServiceImpl.updateFamily(FamilyServiceImpl.java:42)
at com.bonevich.ancestral.family.FamilyServiceTest.testUpdateFamily(FamilyServiceTest.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
我必须相信我在配置 SDN 或事务时遗漏了一些东西,但一直无法追踪。