问:UnitOfWork
实例是否受会话限制?
在马丁所讨论的书的第 11 章中,您读到:
“工作单元会跟踪您在可能影响数据库的业务交易期间所做的一切。完成后,它会计算出根据您的工作更改数据库需要做的所有事情。[...]
“一旦你开始做一些可能影响数据库的事情,你就会创建一个工作单元来跟踪变化。每次创建、更改或删除对象时,您都会告诉工作单元。您还可以让它知道您已读取的对象,以便它可以通过验证在业务事务期间数据库上没有任何对象发生更改来检查不一致的读取。”</p>
因此,UnitOfWork
不需要绑定到会话。实例存在的程度UnitOfWork
取决于您的设计。在同一本书中 Martin 的示例中,我们可以看到 UnitOfOWork 实例是根据 HTTP 请求创建的(我相信这是最经典的用法)。
class UnitOfWorkServlet...
final protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
UnitOfWork.newCurrent();
handleGet(request, response);
UnitOfWork.getCurrent().commit();
} finally {
UnitOfWork.setCurrent(null);
}
}
abstract void handleGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
在 JPA 规范中,工作单元可以具有两种不同的性质:事务范围的持久性上下文、扩展的持久性上下文,但也可以手动处理它并获得应用程序管理的工作单元(请参阅this other answer) . 您使用哪一种取决于用例。
问:我们需要多少个 IdentityMap 实例?
Martin Fowler 在书中也回答了这个问题。在他的部分中IdentityMap
有一个完整的部分。
书中写道:
“这里的决定在每个班级一张地图和整个课程一张地图之间变化。”
在这种情况下,您可以将会话理解为UnitOfWork
。在本书的后面,他解释道:
“如果您有多个映射,则明显的路线是每个类或每个表一个映射,如果您的数据库模式和对象模型相同,则效果很好。”</p>
几段后,马丁解释了在哪里放置IdentityMap
:
“身份地图需要放在容易找到的地方。它们还与您正在使用的流程上下文相关联。您需要确保每个会话都有自己的实例,该实例与任何其他会话的实例隔离。因此,您需要将身份映射放在特定于会话的对象上。如果您使用的是工作单元,那么这是迄今为止标识映射的最佳位置,因为工作单元是跟踪进出数据库的数据的主要位置。如果您没有工作单元,最好的选择是绑定到会话的注册表。”</p>
所以,你有它,如果你UnitOfWork
被绑定到一个请求,那么你将IdentityMaps
在你的那个实例中拥有一个或多个UnitOfWork
.
那么,工作单元是否受业务事务的约束?
是的。
现在“业务事务”的范围可能是短暂的,例如绑定到 HTTP 请求的生命周期,如上面 Martin Fowler 的示例,其中 UnitOfWork 似乎绑定到每个请求的线程局部变量。
或者“业务事务”可以包含多个请求,例如,在 JPA 中有一个扩展持久性上下文的概念,其中 JPA 中的持久性上下文对应于一个UnitOfWork
模式。在扩展的持久性上下文中,业务事务会延长更长的时间。一个典型的例子是购物车:当用户开始将商品放入她的购物车时,会创建一个上下文/工作单元,并且在用户签出她的购物车(提交更改)之前,上下文不会被清除或者她的会话过期(丢弃工作单元中的所有内容)。
现在,还有应用程序管理上下文的想法,这基本上意味着,在您认为合适时启动您的工作单元,并在您不再需要它时关闭它。例如,假设您有一个桌面应用程序和一个具有隔离并发性的小型数据库(即每个用户只管理自己的数据,而不会与其他用户的数据发生冲突)。假设用户的数据可以完美地放入计算机的内存中。在这种情况下,您可以UnitOfWork
在应用程序的开头启动您的应用程序并将其用作数据库的一种缓存。在适当的时候,您可以将 UnitOfWork 刷新到磁盘,例如当用户单击保存按钮时,但仍保持它处于活动状态。当用户关闭应用程序时,UnitOfWork 将被丢弃。
因此,您可以看到“商业交易”的实际含义或UnitOfWork
应该存在多长时间存在很多细微差别。
多个工作单元
根据到目前为止的解释,您可以拥有多个工作单元,其中包括以下原因:
- 每个请求一个
UnitWork
,如果您的应用程序处理并发请求,那么您将同时拥有多个工作单元实例。
- 每个扩展事务一个
UnitOfWork
,因此与用户会话相关联。如果您有多个用户,则可以有多个工作单元。
然而,除此之外,我还发现了您可能希望在同一“业务事务”期间生成新工作单元的其他原因。
当您执行一项业务交易时,您可能希望执行另一项完全独立的交易,这并不罕见。例如,假设要为客户下订单,客户记录必须存在于数据库中,但如果客户记录创建失败(例如,可能因为另一个客户有相同的冲突电子邮件),您仍然想下订单处于待审核状态。
因此,您在这里面临的问题是,如果您在下订单的业务交易期间尝试创建客户并且创建客户失败,那么这会污染您的订单交易并且您的工作单元被迫回滚所有内容。在这种情况下,您可能希望产生一个新的工作单元,因此产生一个新的、单独的数据库事务来创建客户,如果该单独的工作单元未能创建客户,这不会污染您的订单创建单元工作,允许您采取措施在没有客户处于待审核状态的情况下仍然保留您的订单。
我相信 JPA 的“上下文”概念是如何定义工作单元的一个很好的例子。我建议你研究他们的规范,特别是他们关于EntityManager
.