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

时间:2015-01-10 20:47:39

标签: java jpa architecture eclipselink

我正在学习JPA。我的提供者是EclipseLink,我正在制作桌面应用程序,并使用应用程序管理的EntityManager。 在DB中,我有表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中使用refresh。在调用方法时,只使用a = em.find ...(注释行标记为1和3)。

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

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

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

  5. 第一个组合是最慢的,它需要大多数SQL查询。在刷新它加载一切。 第二个和第三个组合位于中间,刷新时只加载来自DB的更改。 第四个例子是最快的,它不需要对DB进行任何额外的查询。

    有人可以对所有这些混淆发表评论,这是如何以及为何有效?

    这样做的正确方法是什么,是否有办法在不创建新EM的情况下实现第四组合的效果,但是使用现有的?

    如果我想制作大而复杂的桌面应用程序,那可能会同时访问数据库,我应该采用什么方法?

    谢谢。

1 个答案:

答案 0 :(得分:1)

如果必须使用不同的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的任何更改,并从最新的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方法。