spring - Transacciones y entidades de relación mapeando problemas con Neo4j OGM
spring-data-neo4j-4 neo4j-ogm (2)
Versiones utilizadas: spring-data-neo4j 4.2.0-BUILD-SNAPSHOT / neo4j-ogm 2.0.6-SNAPSHOT
Tengo problemas para obtener correctamente las entidades de relación .
Las siguientes llamadas de recuperación no devuelven resultados consistentes (ejecutados en la misma transacción):
- session.query ("MATCH (: A) - [b: HAS_B] - (: C) CUENTA DEVUELTO (b) como conteo") devuelve 1
- session.query ("MATCH (: A) - [b: HAS_B] - (: C) RETORNO b") devuelve correctamente la entidad de relación como un objeto RelationshipModel
- session.query (B.class, "MATCH (: A) - [b: HAS_B] - (: C) RETORNO b") devuelve nulo!
Observación importante: cuando todas las operaciones (crear, captar) se realizan en la misma transacción, parece estar bien.
He podido implementar una solución mediante el uso de session.query (String, Map) para consultar la entidad de relación y asignarla yo mismo a mi 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);
}
}
Esta consulta session.query(B.class, "MATCH (:A)-[b:HAS_B]-(:C) RETURN b")
devuelve solo la relación, pero no el nodo de inicio o nodo final, por lo que el OGM no puede hidratar esta . Siempre debe devolver el nodo de inicio y final junto con la relación como session.query(B.class, "MATCH (a:A)-[b:HAS_B]-(c:C) RETURN a,b,c")
La razón por la que parece funcionar cuando ambos crean y recuperan datos en la misma transacción es porque la sesión ya tiene una copia en caché de a
c
y, por lo tanto, b
puede hidratarse con nodos de inicio y final en caché.
En primer lugar, actualice de OGM 2.0.6-SNAPSHOT a 2.1.0-SNAPSHOT. He notado algo de comportamiento impropio en el primero que podría ser una parte del problema.
Ahora a tu prueba. Aquí hay varias cosas que vale la pena investigar.
- Uso de
@DirtiesContext
: Parece que no estás tocando el contexto y si lo usas para restablecer el contexto entre las pruebas para que obtengas una nueva sesión / transacción, eso va por el camino equivocado. Simplemente use@Transactional
en@Transactional
lugar. El corredor de Spring JUnit tratará esto de una manera especial (ver el siguiente punto). - Consciente de que las pruebas transaccionales se retrotraen automáticamente: Jasper tiene razón. Las pruebas de integración de primavera siempre se retrotraerán por defecto . Si quiere asegurarse de que su prueba JUnit se compromete , tendrá que
@Commit
it . Un buen ejemplo de cómo configurar su prueba se puede ver aquí . - Saber cómo funcionan los proxies de transacciones de primavera. Además de toda esta confusión, debe asegurarse de no llamar simplemente al método transaccional al método transaccional en la misma clase y esperar que se aplique el comportamiento transaccional de Spring. Una rápida redacción de por qué se puede ver aquí .
Si abordas esos problemas, todo debería estar bien.