我正在学习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调用打印的数量大于第一次。 这些是:
在addB中使用选项1(直接保留新实体),在addB中使用refresh。在调用方法时,只使用a = em.find ...(注释行标记为1和3)。
在addB中使用选项1,但仅在调用方法中使用refresh(取消注释行// 3保持行// 2注释)。
在addB中使用选项1但不要在addB中使用refresh,在调用方法中使用refresh,并且"找到"再次使用新的em(取消注释所有3行)。 (几乎与2.相同,但因性能而有趣)
在addB中使用选项2,根本不使用refresh,在调用方法中取消注释所有3行。
第一个组合是最慢的,它需要大多数SQL查询。在刷新它加载一切。 第二个和第三个组合位于中间,刷新时只加载来自DB的更改。 第四个例子是最快的,它不需要对DB进行任何额外的查询。
有人可以对所有这些混淆发表评论,这是如何以及为何有效?
这样做的正确方法是什么,是否有办法在不创建新EM的情况下实现第四组合的效果,但是使用现有的?
如果我想制作大而复杂的桌面应用程序,那可能会同时访问数据库,我应该采用什么方法?
谢谢。
答案 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方法。