Neo4j OGM 的事务和关系实体映射问题
Transactions and relationship entities mapping problems with Neo4j OGM
使用的版本:spring-data-neo4j 4.2.0-BUILD-SNAPSHOT / neo4j-ogm 2.0.6-SNAPSHOT
我在正确获取关系实体方面遇到问题。
以下提取调用不会return产生一致的结果(在同一事务中执行):
- session.query("MATCH (:A)-[b:HAS_B]-(:C) RETURN count(b) as count") returns 1
- session.query("MATCH (:A)-[b:HAS_B]-(:C) RETURN b") 正确地return将关系实体作为 RelationshipModel 对象
- session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b") returns null !
重要提示:当所有操作(创建、获取)都在同一个事务中完成时,似乎没问题。
我已经能够通过使用 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);
}
}
首先,请从 OGM 2.0.6-SNAPSHOT 升级到 2.1.0-SNAPSHOT。我注意到前者的一些异常行为可能是问题的一部分。
现在开始测试。这里发生了几件值得研究的事情。
@DirtiesContext
的使用:您似乎没有触及上下文,如果您使用它来重置测试之间的上下文以便获得新的 Session/Transaction 那么就是这样错误的方法。只需使用 @Transactional
即可。 Spring JUnit 运行程序将以特殊方式处理此问题(请参阅下一点)。
- 意识到事务测试会自动回滚:Jasper 是对的。 Spring 集成测试将 always roll back by default. If you want to make sure your JUnit test commits then you will have to
@Commit
it. A good example of how to set up your test can be seen here。
- 了解Spring 交易代理的工作原理。除了所有这些混乱之外,您还必须确保您不会在同一 class 中简单地将事务方法调用到事务方法并期望应用 Spring 的事务行为。快速写下为什么可以看到 here.
如果你解决了这些问题,一切都会好起来的。
这个查询 session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b")
return 只是关系,而不是开始节点或结束节点,因此 OGM 无法补充这一点。您需要始终 return 开始和结束节点以及 session.query(B.class, "MATCH (a:A)-[b:HAS_B]-(c:C) RETURN a,b,c")
等关系
当您在同一个事务中创建和获取数据时它似乎起作用的原因是会话已经有 a
和 c
的缓存副本,因此 b
可以与缓存的开始和结束节点混合。
使用的版本:spring-data-neo4j 4.2.0-BUILD-SNAPSHOT / neo4j-ogm 2.0.6-SNAPSHOT
我在正确获取关系实体方面遇到问题。
以下提取调用不会return产生一致的结果(在同一事务中执行):
- session.query("MATCH (:A)-[b:HAS_B]-(:C) RETURN count(b) as count") returns 1
- session.query("MATCH (:A)-[b:HAS_B]-(:C) RETURN b") 正确地return将关系实体作为 RelationshipModel 对象
- session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b") returns null !
重要提示:当所有操作(创建、获取)都在同一个事务中完成时,似乎没问题。
我已经能够通过使用 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);
}
}
首先,请从 OGM 2.0.6-SNAPSHOT 升级到 2.1.0-SNAPSHOT。我注意到前者的一些异常行为可能是问题的一部分。
现在开始测试。这里发生了几件值得研究的事情。
@DirtiesContext
的使用:您似乎没有触及上下文,如果您使用它来重置测试之间的上下文以便获得新的 Session/Transaction 那么就是这样错误的方法。只需使用@Transactional
即可。 Spring JUnit 运行程序将以特殊方式处理此问题(请参阅下一点)。- 意识到事务测试会自动回滚:Jasper 是对的。 Spring 集成测试将 always roll back by default. If you want to make sure your JUnit test commits then you will have to
@Commit
it. A good example of how to set up your test can be seen here。 - 了解Spring 交易代理的工作原理。除了所有这些混乱之外,您还必须确保您不会在同一 class 中简单地将事务方法调用到事务方法并期望应用 Spring 的事务行为。快速写下为什么可以看到 here.
如果你解决了这些问题,一切都会好起来的。
这个查询 session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b")
return 只是关系,而不是开始节点或结束节点,因此 OGM 无法补充这一点。您需要始终 return 开始和结束节点以及 session.query(B.class, "MATCH (a:A)-[b:HAS_B]-(c:C) RETURN a,b,c")
当您在同一个事务中创建和获取数据时它似乎起作用的原因是会话已经有 a
和 c
的缓存副本,因此 b
可以与缓存的开始和结束节点混合。