JPA - 跨 EntityManagers 获取 updated/synced 个实体(刷新)

JPA - Get updated/synced entities across EntityManagers (refresh)

我正在学习 JPA。我的提供商是 EclipseLink,我正在制作桌面应用程序,并使用应用程序管理的 EntityManager。 在 DB 中,我有 table B 引用 table A,这意味着实体 class A 有 B 的列表(它还有一些其他列表)。 这是简化的情况: 我有四个 ways/scenarios 可以在调用方法中使下面的方法所做的更改可见,它是 EM。

void addB(A a, String desc){
   EntityManager em = factory.createEntityManager();
   em.getTransaction().begin();
   mngedA = em.find(A.class, a.getId());
   B b = new B();
   b.setDesc(desc);
   // option 1:
   b.setA(mngedA);
   em.persist(b);

   // option 2:
   a.getBList().add(b);

   em.getTransaction.commit();
   em.refresh(a); //needed in some scenarios
   em.close();

}

在调用方法中我有这个:

em = factory.createEntityManager();
A a = em.find(A.class, id);
println(a.getBList().size());
addB(a, "value1");

//em = factory.createEntityManager(); //1
//a = em.find(A.class, id);           //2
//em.refresh(a);                      //3

println(a.getBList().size());

标记的行可以注释和取消注释,我想出了 4 种组合,使第二次 println 调用打印出比第一次更大的数字。 它们是:

  1. 在addB中使用选项1(直接持久化新实体),在addB中使用刷新。在调用方法中仅使用 a = em.find...(标有 1 和 3 的注释行)。

  2. 在 addB 中使用选项 1,但仅在调用方法中使用刷新(取消注释行 //3 保留行 //2 注释)。

  3. 在 addB 中使用选项 1 但不在 addB 中使用刷新,在调用方法中使用刷新,并且 "find" A 再次使用新的 em(取消所有 3 行的注释)。 (与 2 几乎相同。但由于性能而有趣)

  4. 在 addB 中使用选项 2,根本不使用刷新,取消调用方法中所有 3 行的注释。

第一个组合最慢,需要最多 SQL 个查询。刷新时它会加载所有内容。 第二个和第三个组合在中间,刷新时它只加载来自数据库的更改。 第四个示例最快,它不需要对数据库进行任何额外查询。

有人可以对所有这些困惑发表评论吗,这是如何以及为什么有效的?

正确的方法是什么?有没有办法在不创建新 EM 而使用现有的情况下实现第四种组合的效果?

如果我想制作大型复杂的桌面应用程序,可能对数据库有并发访问,我应该采用什么方法?

谢谢。

如果您必须使用不同的 EntityManager 上下文,请记住在它之外加载的实体不是它的一部分,因此对 mngedA 所做的更改不会反映到 A 中,除非您在事务完成后强制刷新。 EntityManagers 被设计为类似于事务范围使用,因此单独的 EntityManagers 有意彼此隔离。

对于您想要的解决方案有很多,但如果您的客户端寿命很长,您可能不希望在其整个生命周期内保留单个 EM,因为它确实包含一个缓存,该缓存会被填满并变为陈旧。相反,您可能希望根据需要获取一个用于读取,并在完成后将其清除,或者将其丢弃并根据需要获取新的。类似于:

A saveAndAddB(A a, String desc){
   EntityManager em = factory.createEntityManager();
   em.getTransaction().begin();
   mngedA = em.merge(a);
   B b = new B();
   b.setDesc(desc);
   b.setA(mngedA);
   mngedA.getBList().add(b);;
   em.persist(b);
   em.getTransaction.commit();
   em.close();
   return mngedA;
}

这将保存对 A 的任何更改,并 return 来自最新 EntityManager 的最新副本。如果需要,您的应用程序将继续使用此 A 实例:

  em = factory.createEntityManager();
  A a = em.find(A.class, id);
  em.close();//no longer needed
  println(a.getBList().size());
  a = addB(a, "value1");

  println(a.getBList().size());

如果您的流程是短暂的,另一种方法是将 EM 传递到方法中,以便可以在同一事务中获取所有更改,但更常见的是传递分离的 A 实例并将它们合并到按要求交易。如果您不想获取对 A 的更改,则可以将 A 的 ID 传递给 addB 方法。