当前位置: 首页 > 知识库问答 >
问题:

Spring Data Neo4j 4返回缓存结果?

百里胜泫
2023-03-14

我不确定这是Neo4j问题还是Spring数据问题。我对Neo4j相当陌生,所以我只想确保我做的事情是正确的。我使用的是spring-data-neo4j:4.0。0.0版本,使用neo4j-community-2.3发布。1数据库实例。

现在的情况是,我从DB查询中得到了更多我期望得到的节点。如果我创建一个由3种不同类型的节点组成的图:

(NodeA)-[:NodeAIncludesNodeB]->(NodeB)-[:NodeBIncludesNodeC]->(NodeC)

然后我运行一个查询,得到一个节点,我在查询结果中收到从节点到节点的整个图。

似乎我从数据库中得到的是缓存结果,而不是实时结果。我之所以这么说,是因为如果我调用会话。上下文clear()创建图后,查询不再返回整个图,包括节点节点,但仍返回单个节点及其所有节点。

我在SpringDataNeo4j文档中找到了这句话(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;
  }  
}

存储库:

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"}]}]}

请注意,每个查询都返回相同的结果,即使除了最后两个查询之外,我只在所有查询中请求一个节点。

如果我取消对会话的注释。上下文()。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"}]}]}

请注意,整个图只有在我显式请求时才会返回,但是我仍然通过NodeA接收NodeB。

我需要填充对REST调用的响应,我不希望剥离无关对象,这样它们就不会出现在REST响应中。我必须打电话到会议室。上下文()。每次数据库访问后清除()以便不接收“缓存”节点?是否有更好的方法进行调用以接收更细粒度的结果?我可以完全关闭“缓存”吗?

共有1个答案

楮乐邦
2023-03-14

这是出于设计——查询确实会命中数据库以获取新数据,但是,如果实体在会话中已有相关节点,那么这些节点将被保留。请注意,某些测试方法的行为不同:

Iterable

NodeA oneNodeA=nodeARep.findByName("NodeA 1");//派生查找器,默认深度1,行为同上

NodeA-oneNodeA2=nodeARep。findByNameQuery(“节点1”) //自定义查询,它将只返回查询要求它返回的内容。

您将需要执行会话。clear()如果使用findAll或find-by-id,则后跟深度为0的加载。此处提供了详细说明https://jira.spring.io/browse/DATAGRAPH-642

 类似资料:
  • null 根据密钥在缓存中查找 如果找到,则从缓存返回结果 如果未找到,则返回NULL 我不想在cache-miss的情况下更新缓存,有没有办法使用spring注释来做到这一点

  • 我很清楚关于这个话题有多个问题,但我就是弄不懂它的意思。问题似乎是没有将新值添加到@cacheable列表中。 调试完问题后,我发现问题似乎出在钥匙上。 下面是代码片段 所以当我调用save方法时,用于缓存的关键字是incrementing integer,或者1,2,3...但是当我尝试获取所有文档时,缓存使用SimpleKey[]作为键。如果我尝试为@Cacheable使用相同的键,我会得到S

  • 返回 memoized(缓存的)函数。 使用 Object.create(null) 创建一个没有 Object.prototype 的空对象(这样如果输入值类似 'hasOwnProperty',那么这些属性就不会被解析)。 通过首先检查该特定输入值的函数输出是否已经被缓存,如果没有,则返回一个函数,该函数将作为单个参数提供给 memoized 函数。 const memoize = fn =>

  • 我读到了关于和的相互矛盾的语句。 这个答案说: 使用must-revalidate时,如果服务器不响应重新验证请求,浏览器/代理应该返回504错误。在没有缓存的情况下,它只显示缓存的内容 然而,“只显示缓存的内容”而不进行重新验证与https://datatracker.ietf.org/doc/html/RFC7234#section-5.2.1.4中的内容相矛盾: “no-cache”requ

  • 结果缓存 1,用于加速热门数据的访问速度,Dubbo 提供声明式缓存,以减少用户加缓存的工作量 2。 缓存类型 lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。 threadlocal 当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问。 jcache 与 JSR107 集成,可以桥接各种缓存实现。 缓存

  • 我在java Play Framework作业中使用Caffeine cache实现来缓存传出的http请求,我看到,尽管缓存已经启动并运行,但我的服务仍然每次都访问外部服务器,而不是返回缓存中的值。我看到记录在日志中的键总是相同的。 所以我看到没有设置,所以这可能意味着缓存立即过期。但我仍然不知道如何在配置中设置它 我没有更改我的java代码,如果我需要这样做,我找不到任何示例,或者我只应该有一