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 调用打印出比第一次更大的数字。
它们是:
在addB中使用选项1(直接持久化新实体),在addB中使用刷新。在调用方法中仅使用 a = em.find...(标有 1 和 3 的注释行)。
在 addB 中使用选项 1,但仅在调用方法中使用刷新(取消注释行 //3 保留行 //2 注释)。
在 addB 中使用选项 1 但不在 addB 中使用刷新,在调用方法中使用刷新,并且 "find" A 再次使用新的 em(取消所有 3 行的注释)。 (与 2 几乎相同。但由于性能而有趣)
在 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 方法。
我正在学习 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 调用打印出比第一次更大的数字。 它们是:
在addB中使用选项1(直接持久化新实体),在addB中使用刷新。在调用方法中仅使用 a = em.find...(标有 1 和 3 的注释行)。
在 addB 中使用选项 1,但仅在调用方法中使用刷新(取消注释行 //3 保留行 //2 注释)。
在 addB 中使用选项 1 但不在 addB 中使用刷新,在调用方法中使用刷新,并且 "find" A 再次使用新的 em(取消所有 3 行的注释)。 (与 2 几乎相同。但由于性能而有趣)
在 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 方法。