【问题标题】:NotInTransactionException when adding to mapped set添加到映射集时出现 NotInTransactionException
【发布时间】:2013-09-24 16:38:02
【问题描述】:

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

源代码可以在github 找到,尤其是failing test。以下是代码的相关部分:

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 或交易时遗漏了一些东西,但一直无法找到它。

【问题讨论】:

  • 刚接触 StackOverflow,有人能解释一下投票否决吗?
  • 在您的实体中,我会选择 @RelatedTo(elementClass = PersonNode.class, type = "CHILD") private Set&lt;Person&gt; children = new Hashset&lt;&gt;(); 并从 addChild() 中删除空检查
  • 这也没有解决问题。
  • 这只是一个建议,并不打算修复。

标签: spring neo4j spring-data spring-transactions spring-data-neo4j


【解决方案1】:

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

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

【讨论】:

  • 是的,我看到了这个问题。我确实想要这种行为——需要建立家庭和孩子之间的关系。所以需要一个 Set 。你回答的另一个问题我认为没有得到回答。 @AndyB(最初的提问者)刚刚指出他已经解决了他的问题,但没有具体说明解决方案是什么(只是关于修复自动装配)。
  • 哦,我太新手了,无法评论这个问题。我试图发布一个问题作为“答案”,但版主删除了它(毫无疑问是正确的;但我仍然没有追索权,所以我发布了这个问题)。
  • 好吧,在这种情况下,您只需要将 Set 的修改放入事务中。通过扩展事务手动扩展(注意 `family.addChild(FamilyServiceTest.this.neo4jTemplate.findOne(childId, PersonNode.class));` 正在事务外部运行)或通过在使用 @ 注释的方法内部进行修改交易。
  • 所以对于你的测试用例移动`tx.success(); tx.finish();` 就在断言应该解决问题之前
  • 测试中的事务只是为了持久化一个Person。测试运行 updateFamily(),这是一个使用 @Transactional 的服务调用。后者是失败的。
猜你喜欢
  • 1970-01-01
  • 2013-09-23
  • 1970-01-01
  • 1970-01-01
  • 2015-02-09
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
  • 2021-11-19
相关资源
最近更新 更多