Я не уверен, что это вопрос Neo4j или вопрос с Spring Data. Я довольно новичок в Neo4j, поэтому я просто хочу убедиться, что все правильно. Я использую spring-data-neo4j: 4.0.0.RELEASE с экземпляром DB neo4j-community-2.3.1.Весенние данные Neo4j 4отверждающиеся кешированные результаты?
Ситуация в том, что я получаю больше узлов, которые я ожидаю от запросов БД. Если я создаю граф, состоящий из 3-х различных типов узлов:
(NodeA)-[:NodeAIncludesNodeB]->(NodeB)-[:NodeBIncludesNodeC]->(NodeC)
, а затем я выполнить запрос, чтобы получить единый узел узла А я получаю весь граф от узла А к NodeC в результатах запроса.
Кажется, что я получаю кешированные результаты вместо живых результатов из БД. Причина, по которой я говорю это, заключается в том, что если я вызову session.context.clear() после создания графика, запрос больше не возвращает весь граф, включая NodeC, но он все равно возвращает одиночный NodeA вместе со всеми его узлами NodeB ,
Я нашел эту цитату в документации Spring Data Neo4j (http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/):
Заметим, однако, что сессия не всегда возвращают кэшируются объекты так нет никакого риска получения устаревших данных о нагрузке ; он всегда попадает в базу данных .
Я создал небольшой пример приложения для иллюстрации:
сущностей Классы:
@NodeEntity
public class NodeA extends BaseNode {
private String name;
@Relationship(type = "NodeAIncludesNodeB", direction = "OUTGOING")
private Set<NodeB> bNodes;
public NodeA() {}
public NodeA(String name) {
this.name = name;
}
//getters, setter, equals and hashcode omitted for brevity
}
@NodeEntity
public class NodeB extends BaseNode {
private String name;
@Relationship(type = "NodeBIncludesNodeC", direction = "OUTGOING")
private Set<NodeC> cNodes;
public NodeB() {}
public NodeB(String name) {
this.name = name;
}
}
@NodeEntity
public class NodeC extends BaseNode {
private String name;
public NodeC() {}
public NodeC(String name) {
this.name = name;
}
}
Repository:
public interface NodeARepository extends GraphRepository<NodeA> {
public NodeA findByName(String name);
@Query("MATCH (n:NodeA) WHERE n.name = {nodeName} RETURN n")
public NodeA findByNameQuery(@Param("nodeName") String name);
@Query("MATCH (n:NodeA)-[r:NodeAIncludesNodeB]->() WHERE n.name = {nodeName} RETURN r")
public NodeA findByNameWithBNodes(@Param("nodeName") String name);
@Query("MATCH (n:NodeA)-[r1:NodeAIncludesNodeB]->()-[r2:NodeBIncludesNodeC]->() WHERE n.name = {nodeName} RETURN r1,r2")
public NodeA findByNameWithBAndCNodes(@Param("nodeName") String name);
}
Тест Применение:
@SpringBootApplication
public class ScratchApp implements CommandLineRunner {
@Autowired
NodeARepository nodeARep;
@Autowired
Session session;
@SuppressWarnings("unused")
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(ScratchApp.class, args);
}
@Override
public void run(String...strings) {
ObjectMapper mapper = new ObjectMapper();
NodeA nodeA = new NodeA("NodeA 1");
NodeB nodeB1 = new NodeB("NodeB 1");
NodeC nodeC1 = new NodeC("NodeC 1");
NodeC nodeC2 = new NodeC("NodeC 2");
Set<NodeC> b1CNodes = new HashSet<NodeC>();
b1CNodes.add(nodeC1);
b1CNodes.add(nodeC2);
nodeB1.setcNodes(b1CNodes);
NodeB nodeB2 = new NodeB("NodeB 2");
NodeC nodeC3 = new NodeC("NodeC 3");
NodeC nodeC4 = new NodeC("NodeC 4");
Set<NodeC> b2CNodes = new HashSet<NodeC>();
b2CNodes.add(nodeC3);
b2CNodes.add(nodeC4);
nodeB2.setcNodes(b2CNodes);
Set<NodeB> aBNodes = new HashSet<NodeB>();
aBNodes.add(nodeB1);
aBNodes.add(nodeB2);
nodeA.setbNodes(aBNodes);
nodeARep.save(nodeA);
// ((Neo4jSession)session).context().clear();
try {
Iterable<NodeA> allNodeAs = nodeARep.findAll();
System.out.println(mapper.writeValueAsString(allNodeAs));
// ((Neo4jSession)session).context().clear();
Iterable<NodeA> allNodeAs2 = nodeARep.findAll();
System.out.println(mapper.writeValueAsString(allNodeAs2));
NodeA oneNodeA = nodeARep.findByName("NodeA 1");
System.out.println(mapper.writeValueAsString(oneNodeA));
NodeA oneNodeA2 = nodeARep.findByNameQuery("NodeA 1");
System.out.println(mapper.writeValueAsString(oneNodeA2));
NodeA oneNodeA3 = session.load(NodeA.class, oneNodeA.getId());
System.out.println(mapper.writeValueAsString(oneNodeA3));
// ((Neo4jSession)session).context().clear();
NodeA oneNodeA4 = nodeARep.findByNameWithBNodes("NodeA 1");
System.out.println(mapper.writeValueAsString(oneNodeA4));
NodeA oneNodeA5 = nodeARep.findByNameWithBAndCNodes("NodeA 1");
System.out.println(mapper.writeValueAsString(oneNodeA5));
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Вот результаты тестовой программы:
[{"id":20154,"name":"NodeA 1","bNodes":[{"id":20160,"name":"NodeB 1","cNodes":[{"id":20155,"name":"NodeC 1"},{"id":20156,"name":"NodeC 2"}]},{"id":20157,"name":"NodeB 2","cNodes":[{"id":20158,"name":"NodeC 3"},{"id":20159,"name":"NodeC 4"}]}]}]
[{"id":20154,"name":"NodeA 1","bNodes":[{"id":20160,"name":"NodeB 1","cNodes":[{"id":20155,"name":"NodeC 1"},{"id":20156,"name":"NodeC 2"}]},{"id":20157,"name":"NodeB 2","cNodes":[{"id":20158,"name":"NodeC 3"},{"id":20159,"name":"NodeC 4"}]}]}]
{"id":20154,"name":"NodeA 1","bNodes":[{"id":20160,"name":"NodeB 1","cNodes":[{"id":20155,"name":"NodeC 1"},{"id":20156,"name":"NodeC 2"}]},{"id":20157,"name":"NodeB 2","cNodes":[{"id":20158,"name":"NodeC 3"},{"id":20159,"name":"NodeC 4"}]}]}
{"id":20154,"name":"NodeA 1","bNodes":[{"id":20160,"name":"NodeB 1","cNodes":[{"id":20155,"name":"NodeC 1"},{"id":20156,"name":"NodeC 2"}]},{"id":20157,"name":"NodeB 2","cNodes":[{"id":20158,"name":"NodeC 3"},{"id":20159,"name":"NodeC 4"}]}]}
{"id":20154,"name":"NodeA 1","bNodes":[{"id":20160,"name":"NodeB 1","cNodes":[{"id":20155,"name":"NodeC 1"},{"id":20156,"name":"NodeC 2"}]},{"id":20157,"name":"NodeB 2","cNodes":[{"id":20158,"name":"NodeC 3"},{"id":20159,"name":"NodeC 4"}]}]}
{"id":20154,"name":"NodeA 1","bNodes":[{"id":20157,"name":"NodeB 2","cNodes":[{"id":20158,"name":"NodeC 3"},{"id":20159,"name":"NodeC 4"}]},{"id":20160,"name":"NodeB 1","cNodes":[{"id":20155,"name":"NodeC 1"},{"id":20156,"name":"NodeC 2"}]}]}
{"id":20154,"name":"NodeA 1","bNodes":[{"id":20157,"name":"NodeB 2","cNodes":[{"id":20159,"name":"NodeC 4"},{"id":20158,"name":"NodeC 3"}]},{"id":20160,"name":"NodeB 1","cNodes":[{"id":20156,"name":"NodeC 2"},{"id":20155,"name":"NodeC 1"}]}]}
Обратите внимание, что каждый запрос возвращает тот же результат, несмотря на то, что я только запрашивающий один узел во всех, кроме двух последних запросов. .
Если я раскомментировать session.context() ясно() вызывает здесь результаты:
[{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":null},{"id":20167,"name":"NodeB 1","cNodes":null}]}]
[{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":null},{"id":20167,"name":"NodeB 1","cNodes":null}]}]
{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":null},{"id":20167,"name":"NodeB 1","cNodes":null}]}
{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":null},{"id":20167,"name":"NodeB 1","cNodes":null}]}
{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":null},{"id":20167,"name":"NodeB 1","cNodes":null}]}
{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":null},{"id":20167,"name":"NodeB 1","cNodes":null}]}
{"id":20161,"name":"NodeA 1","bNodes":[{"id":20164,"name":"NodeB 2","cNodes":[{"id":20165,"name":"NodeC 3"},{"id":20166,"name":"NodeC 4"}]},{"id":20167,"name":"NodeB 1","cNodes":[{"id":20163,"name":"NodeC 2"},{"id":20162,"name":"NodeC 1"}]}]}
Обратите внимание, что весь граф возвращается только, когда я прошу его в явном виде, однако я до сих пор получающего NodeB с NodeA.
Мне нужно заполнить ответ на вызов REST, и мне бы не пришлось снимать посторонние объекты, чтобы они не отображались в ответе REST. Должен ли я сделать вызов session.context(). Clear() после каждого доступа к БД, чтобы я не получал «кэшированные» узлы? Есть ли лучший способ сделать звонок для получения более тонкого результата? Можно ли вообще отключить «кеширование»?
Спасибо, Луанна. Я включил все различные методы тестирования, чтобы показать, что результаты были одинаковыми, хотя ожидаемое поведение теста должно было быть иным. Как я уже сказал, я должен вернуть результаты своих запросов БД в качестве ответов на вызовы REST, потребители ответов REST не ожидают, что связанные узлы будут возвращены, поэтому я должен их разбить. Вызов session.clear() после того, как каждый доступ к БД не идеален, но я думаю, нам придется жить с этим пока. Я хотел бы отдать свой голос за устранение такого поведения, как указано в приведенной вами ссылке Джиры. :) –
как продолжение, можете ли вы рассказать мне, является ли сеанс (aka mapping context) глобальным для всех запросов к БД? То есть если другой запрос вносит изменения в NodeB, и я запрашиваю NodeA, я получаю устаревший узел NodeB от моего сеанса с запросом NodeA или получаю обновленный NodeB из другого потока? –
Жизнь сеанса может управляться вами. См. Http://docs.spring.io/spring-data/neo4j/docs/4.0.0.RELEASE/reference/html/#_session_bean Обычно вы хотите, чтобы он жил так долго, как единица работы, поэтому запрос или сеанс, охваченный веб-приложением. Чтобы избежать проблем с целостностью данных, вам нужны свежие данные в начале каждой единицы работы, либо путем повторной выборки данных, либо путем обновления сеанса (очистка/получение нового). – Luanne