【问题标题】:Transactions and relationship entities mapping problems with Neo4j OGMNeo4j OGM 的事务和关系实体映射问题
【发布时间】:2017-02-12 08:47:29
【问题描述】:

使用的版本:spring-data-neo4j 4.2.0-BUILD-SNAPSHOT / neo4j-ogm 2.0.6-SNAPSHOT

我有正确获取关系实体的问题

以下 fetch 调用不会返回一致的结果(在同一事务中执行):

  1. session.query("MATCH (:A)-[b:HAS_B]-(:C) RETURN count(b) as count") 返回 1
  2. session.query("MATCH (:A)-[b:HAS_B]-(:C) RETURN b") 正确返回关系实体作为RelationshipModel对象
  3. session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b") 返回空值!

重要说明:当所有操作(create、fetch)都在同一个事务中完成时,似乎没问题。

我已经能够实现一种解决方法,方法是使用 session.query(String, Map) 来查询关系实体并自己将其映射到我的 POJO

@NodeEntity
public class A {
    public A () {}
    public A (String name) {
        this.name = name;
    }

    @GraphId
    private Long graphId;

    private String name;

    @Relationship(type="HAS_B", direction=Relationship.OUTGOING)
    private B b;
}

@RelationshipEntity(type="HAS_B")
public class B {
    public B () {}
    public B (String name, A a, C c) {
        this.name = name;
        this.a = a;
        this.c = c;
    }

    @GraphId
    private Long graphId;

    @StartNode
    private A a;

    @EndNode
    private C c;

    private String name;
}

@NodeEntity
public class C {
    public C () {}
    public C (String name) {
        this.name = name;
    }

    @GraphId
    private Long graphId;

    private String name;
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes={MyTest.TestConfiguration.class})
public class MyTest {
    @Autowired
    private MyBean myBean;

    @Configuration
    @EnableAutoConfiguration
    @EnableTransactionManagement
    @EnableNeo4jRepositories("com.nagra.ml.sp.cpm.core.repositories")
    public static class TestConfiguration {
        @Bean
        public org.neo4j.ogm.config.Configuration configuration() {
            org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
            config.driverConfiguration().setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver");
            return config;
        }
        @Bean
        public SessionFactory sessionFactory() {
            return new SessionFactory(configuration(), "com.nagra.ml.sp.cpm.model");
        }
        @Bean
        public Neo4jTransactionManager transactionManager() {
            return new Neo4jTransactionManager(sessionFactory());
        }
        @Bean
        public MyBean myBean() {
            return new MyBean();
        }
    }

    @Test
    public void alwaysFails() {
        myBean.delete();
        myBean.create("1");
        try { Thread.sleep(2000); } catch (InterruptedException e) {} //useless
        myBean.check("1"); // FAILS HERE !
    }

    @Test
    public void ok() {
        myBean.delete();
        myBean.createAndCheck("2");
    }
}

@Transactional(propagation = Propagation.REQUIRED)
public class MyBean {

    @Autowired
    private Session neo4jSession;

    public void delete() {
        neo4jSession.query("MATCH (n) DETACH DELETE n", new HashMap<>());
    }

    public void create(String suffix) {
        C c = new C("c"+suffix);
        neo4jSession.save(c);
        A a = new A("a"+suffix);
        neo4jSession.save(a);
        B bRel = new B("b"+suffix, a, c);
        neo4jSession.save(bRel);
    }

    public void check(String suffix) {
        //neo4jSession.clear(); //Not working even with this
        Number countBRels = (Number) neo4jSession.query("MATCH (:A)-[b:HAS_B]-(:C) WHERE b.name = 'b"+suffix+"' RETURN count(b) as count", new HashMap<>()).iterator().next().get("count");
        assertEquals(1, countBRels.intValue()); // OK
        Iterable<B> bRels = neo4jSession.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) WHERE b.name = 'b"+suffix+"' RETURN b", new HashMap<>());
        boolean relationshipFound = bRels.iterator().hasNext();
        assertTrue(relationshipFound); // FAILS HERE !
    }

    public void createAndCheck(String suffix) {
        create(suffix);
        check(suffix);
    }
}

【问题讨论】:

  • 只是确认:其他事务是否已提交?
  • 是的,根据 Neo4jTransactionManager 日志,前 2 个事务已正确提交(对于 delete() 和 create() 调用)。由于断言异常,第三个事务(用于 check())被回滚。而且我还可以在日志中看到为每个事务创建了一个新会话(“为 Neo4j OGM 事务打开了新会话 [org.neo4j.ogm.session.Neo4jSession@xxxx]”)
  • 哪些方法?我尝试用@Transactional 标记MyBean 中的方法,但它并没有解决问题。
  • 如果我也将我的测试方法 (alwaysFails()) 标记为 Transactional,它可以正常工作,因为一切都在同一个事务中完成。
  • 是的,我尝试将 @Transactional 放在 createAndCheck() (以及此类的所有其他方法)上,但效果不佳。 createAndCheck() 没有问题,就是我直接调用create() 再调用check() 出现问题。

标签: spring neo4j spring-data-neo4j-4 neo4j-ogm


【解决方案1】:

此查询session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b") 仅返回关系,但不返回开始节点或结束节点,因此 OGM 无法对其进行水合。您需要始终返回起始节点和结束节点以及 session.query(B.class, "MATCH (a:A)-[b:HAS_B]-(c:C) RETURN a,b,c") 之类的关系

当您在同一个事务中创建和获取数据时它似乎工作的原因是会话已经有ac 的缓存副本,因此b 可以通过缓存的开始和结束进行水合节点。

【讨论】:

    【解决方案2】:

    首先,请从 OGM 2.0.6-SNAPSHOT 升级到 2.1.0-SNAPSHOT。我注意到前者的一些异常行为可能是问题的一部分。

    现在开始测试。这里有几件事值得研究。

    • @DirtiesContext 的使用:您似乎没有触及上下文,如果您使用它来重置测试之间的上下文以便获得新的会话/事务,那么这是错误的方式。只需改用@Transactional。 Spring JUnit 运行器将以特殊方式处理此问题(请参阅下一点)。
    • 意识到事务测试会自动回滚:Jasper 是对的。 Spring 集成测试将always roll back by default。如果您想确保您的 JUnit 测试提交 then you will have to @Commit ithere 是一个关于如何设置测试的好例子。
    • 了解 Spring Transaction 代理的工作原理。除了所有这些混乱之外,您还必须确保您不会简单地将事务方法调用到同一类中的事务方法并期望 Spring 的事务行为适用。快速写下为什么可以看到here

    如果你解决了这些问题,一切都会好起来的。

    【讨论】:

    • 感谢您的反馈!我试过 2.1.0-SNAPSHOT,问题仍然存在。我试图删除@DirtiesContext,问题仍然存在。对于事务,由于事务注释是在我的 spring bean 上而不是在我的测试上配置的,因此事务被正确提交。而且我可以看到只有对 MyBean 方法的外部调用正在创建一个新事务,所以一切似乎都围绕事务完美运行。我怀疑这更多是与缓存和/或映射有关的问题。
    • 我的意思是您的测试设置不正确。如果您的设置是错误的,那么您如何尝试和测试它并不重要。事务隔离意味着不会看到当前执行事务之外的任何断言。测试中的每个调用都是一个单独的会话/事务。因为 Spring 回滚事务,所以在下一次调用中看不到任何内容。
    • 抱歉,我不明白为什么我的测试设置有误。让我自己解释一下。我的目标是在我的 SDN 应用程序上测试 REST API。每次完成 REST 调用时,都会创建一个新的会话/事务。我在 SO 中显示的设置是此设置的简化,其中 MyBean 充当我的 REST API 实现的角色。我不希望我的测试是事务性的。我希望我的测试对 MyBean 类的每次调用都会创建一个新的会话/事务 ....
    • ....而且您似乎确定我的事务正在回滚,但事实并非如此。我的测试不是事务性的,只有我的 spring bean 是。所以事务被提交,证明是我可以从另一个事务中获取一个事务中创建的数据。您似乎也忘记了要点,即我能够使用 session.query(String, Map) 从 Neo4j 获取数据,它返回一个包含正确数据的关系模型对象。不工作的是使用返回 null 的 session.query(Class, String, Map) 的相同查询。 ....
    • ....这证明数据实际上在数据库中,但没有正确映射/返回到我的 POJO 中。如果您仍然认为我的测试设置有误,请解释我应该如何实现包含多个事务的测试,否则说明我是如何完成的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2010-12-13
    相关资源
    最近更新 更多