java - JPA - 跨 EntityManager 获取更新/同步的实体(刷新)

标签 java jpa architecture eclipselink

我正在学习 JPA。我的提供商是 EclipseLink,我正在制作桌面应用程序,并使用应用程序管理的 EntityManager。 在数据库中,我有表 B 引用表 A,这意味着实体类 A 具有 B 列表(并且它还有一些其他列表)。 这是简化的情况: 我有四种方法/场景可以使下面的方法所做的更改在调用方法中可见,这就是 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 中使用刷新,在调用方法中使用刷新,并使用 new em 再次“查找”A(取消所有 3 行注释)。 (几乎与 2 相同,但由于性能而有趣)

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

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

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

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

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

谢谢。

最佳答案

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

有很多解决方案可以满足您的需求,但如果您的客户端生命周期很长,您可能不想在其生命周期中保留单个 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 的任何更改,并从最新的 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 方法。

关于java - JPA - 跨 EntityManager 获取更新/同步的实体(刷新),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27881058/

相关文章:

java - Neo4J IN 运算符

java - 获取 java.lang.IllegalStateException : Neither BindingResult nor plain target object for bean name 'command' available as request attribute

java - 如何使用java替换Json中的字符串

java - 使用转换为 NULL 的转换器生成正确的 SQL?

java - 我应该包含 JPA 的 main 方法吗?

xml - API设计: Expressing search criteria in XML

.net - 为什么在 .NET 应用程序中如此多地使用接口(interface)?

java - 如何在 Java 中与智能卡交互?

java - JPA 实体被忽略

architecture - 微服务架构——跨域聊天