1

我有两个@NodeEntities使用简单映射通过 SDN 映射的,PersonNodeFamilyNode. FamilyNode@RelatedTo收藏,孩子们。我还有一个FamilyService(使用 Spring 的@Service注释)在方法@Transactional上带有注释updateFamily。此方法加载FamilyNode给定的 id,并使用回调接口修改节点。在回调的一种实现中,我将 a 添加PersonNode到 children 集合中,这将生成NotInTransactionException,特别是在 Neo4J 试图创建 和 之间的关系FamilyNodePersonNode

源代码可以在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 或事务时遗漏了一些东西,但一直无法追踪。

4

1 回答 1

2

我最近在这里回答了这个问题: Spring Data Neo4J - NotInTransactionException while modifying set of nodeEntity

简而言之,这与这些列表是由 SDN 支持的特殊列表有关,任何修改都会立即保存到数据库中。如果你想防止这种行为,你应该在你的模型类中使用 Iterable。如果您在阅读链接后还有其他问题,请将它们作为评论发布。

于 2013-09-25T12:35:18.287 回答