Spring 数据 Neo4j 4 返回缓存结果?
Spring Data Neo4j 4returning cached results?
我不确定这是 Neo4j 问题还是 Spring 数据问题。我是 Neo4j 的新手,所以我只想确保我做的事情是正确的。我将 spring-data-neo4j:4.0.0.RELEASE 与 neo4j-community-2.3.1 数据库实例一起使用。
情况是我得到了更多我期望从数据库查询返回的节点。如果我创建一个由 3 种不同类型的节点组成的图:
(NodeA)-[:NodeAIncludesNodeB]->(NodeB)-[:NodeBIncludesNodeC]->(NodeC)
然后我 运行 查询以获取单个 NodeA 节点 我在查询结果中收到从 NodeA 到 NodeC 的整个图。
我似乎从数据库中获取了缓存结果而不是实时结果。我这样说的原因是因为如果我在创建图形后调用 session.context.clear(),查询不再 return 包含 NodeC 的整个图形,但它仍然 return单个 NodeA 及其所有 NodeB。
我在 Spring Data Neo4J 文档 (http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/) 中找到了这句话:
Note, however, that the Session does not ever return cached objects so
there’s no risk of getting stale data on load; it always hits the
database.
我创建了一个小示例应用程序来说明:
实体类:
@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;
}
}
存储库:
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"}]}]}
请注意,每个查询 return 的结果都是相同的,尽管除了最后两个查询之外,我只请求一个节点。
如果我取消注释 session.context().clear() 调用,结果如下:
[{"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"}]}]}
请注意,整个图表仅在我明确请求时才 return 编辑,但我仍然收到带有 NodeA 的 NodeB。
我需要填充对 REST 调用的响应,我宁愿不必去除无关的对象,这样它们就不会出现在 REST 响应中。我是否必须在每次访问数据库后调用 session.context().clear() 以便我不会收到 "cached" 节点?有没有更好的方法来调用以接收更细粒度的结果?我可以完全关闭 "caching" 吗?
这是设计使然 - 查询确实会访问数据库以获取新数据,但是,如果实体在会话中已经有相关节点,那么这些节点将被保留。请注意,您的某些测试方法的行为不同:
Iterable<NodeA> allNodeAs = nodeARep.findAll();
//默认深度1,所以它会从图中加载相关节点,一跳远
NodeA oneNodeA = nodeARep.findByName("NodeA 1");
//Derived finder, default depth 1, same behavior as above
NodeA oneNodeA2 = nodeARep.findByNameQuery("NodeA 1");
//自定义查询,它只会return查询要求的内容。
如果您使用的是 findAll 或 find-by-id,则需要执行 session.clear()
后跟深度为 0 的加载。此处提供了详细说明 https://jira.spring.io/browse/DATAGRAPH-642
我不确定这是 Neo4j 问题还是 Spring 数据问题。我是 Neo4j 的新手,所以我只想确保我做的事情是正确的。我将 spring-data-neo4j:4.0.0.RELEASE 与 neo4j-community-2.3.1 数据库实例一起使用。
情况是我得到了更多我期望从数据库查询返回的节点。如果我创建一个由 3 种不同类型的节点组成的图:
(NodeA)-[:NodeAIncludesNodeB]->(NodeB)-[:NodeBIncludesNodeC]->(NodeC)
然后我 运行 查询以获取单个 NodeA 节点 我在查询结果中收到从 NodeA 到 NodeC 的整个图。
我似乎从数据库中获取了缓存结果而不是实时结果。我这样说的原因是因为如果我在创建图形后调用 session.context.clear(),查询不再 return 包含 NodeC 的整个图形,但它仍然 return单个 NodeA 及其所有 NodeB。
我在 Spring Data Neo4J 文档 (http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/) 中找到了这句话:
Note, however, that the Session does not ever return cached objects so there’s no risk of getting stale data on load; it always hits the database.
我创建了一个小示例应用程序来说明:
实体类:
@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;
}
}
存储库:
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"}]}]}
请注意,每个查询 return 的结果都是相同的,尽管除了最后两个查询之外,我只请求一个节点。
如果我取消注释 session.context().clear() 调用,结果如下:
[{"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"}]}]}
请注意,整个图表仅在我明确请求时才 return 编辑,但我仍然收到带有 NodeA 的 NodeB。
我需要填充对 REST 调用的响应,我宁愿不必去除无关的对象,这样它们就不会出现在 REST 响应中。我是否必须在每次访问数据库后调用 session.context().clear() 以便我不会收到 "cached" 节点?有没有更好的方法来调用以接收更细粒度的结果?我可以完全关闭 "caching" 吗?
这是设计使然 - 查询确实会访问数据库以获取新数据,但是,如果实体在会话中已经有相关节点,那么这些节点将被保留。请注意,您的某些测试方法的行为不同:
Iterable<NodeA> allNodeAs = nodeARep.findAll();
//默认深度1,所以它会从图中加载相关节点,一跳远
NodeA oneNodeA = nodeARep.findByName("NodeA 1");
//Derived finder, default depth 1, same behavior as above
NodeA oneNodeA2 = nodeARep.findByNameQuery("NodeA 1");
//自定义查询,它只会return查询要求的内容。
如果您使用的是 findAll 或 find-by-id,则需要执行 session.clear()
后跟深度为 0 的加载。此处提供了详细说明 https://jira.spring.io/browse/DATAGRAPH-642