如何将UnitOfWork模式与IdentityMap模式一起使用?

gst*_*low 5 java enterprise unit-of-work identity-map

在 Martin Fowler 的书中,我读到了有关UnitOfWorkIdentityMap 模式的内容。

作者提到将 IdentityMap 放入 UnitOfWork 中是一个好主意。但怎么办呢?

据我了解IdentityMap,受会话限制,但作者没有提到这一点UnitOfWork

  1. 实例是否UnitOfWork受会话限制?

  2. 假设我们有客户和订单实体。

    public clas Client{
    
         List<Order> orders;
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

我们收到更新客户信息(电话号码)并添加新订单的请求:

这里需要多少个unitOfWork实例?每个会话?我们应该为客户和订单提供单独的实例吗?

我们这里需要多少个 IdentityMap 实例?对于每个实例?我们应该为客户和订单提供单独的实例吗?

每个unitOfWork 实例需要多少个IdentityMap 实例?

如果我们有 2 个并发请求怎么办?

Edw*_*rzo 3

\n

问:UnitOfWork实例是否受会话限制?

\n
\n

在马丁的相关书中的第 11 章中,您读到:

\n
\n

\xe2\x80\x9c工作单元跟踪您在业务事务期间所做的所有可能影响数据库的操作。当您完成后,它会计算出由于您的工作而需要更改数据库的所有操作。[...]

\n

\xe2\x80\x9c一旦您开始执行可能影响数据库的操作,您就会创建一个工作单元来跟踪更改。每次创建、更改或删除对象时,您都会告诉工作单元。您还可以让它知道您\xe2\x80\x99已读取的对象,以便它可以通过验证业务事务期间数据库上没有任何对象发生更改来检查不一致的读取。\xe2\x80\x9d

\n
\n

因此,UnitOfWork不需要绑定到会话。实例存在的范围UnitOfWork取决于您的设计。在同一本书中 Martin 的示例中,我们可以看到 UnitOfOWork 实例是根据 HTTP 请求创建的(我认为这是最经典的用法)。

\n
class UnitOfWorkServlet...\n\n\xc2\xa0\xc2\xa0\xc2\xa0final protected void doGet(HttpServletRequest request, HttpServletResponse response)\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0throws ServletException, IOException {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0try {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0UnitOfWork.newCurrent();\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0handleGet(request, response);\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0UnitOfWork.getCurrent().commit();\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0} finally {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0UnitOfWork.setCurrent(null);\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0}\n\n\xc2\xa0\xc2\xa0\xc2\xa0abstract void handleGet(HttpServletRequest request, HttpServletResponse response)\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0throws ServletException, IOException; \n
Run Code Online (Sandbox Code Playgroud)\n

在 JPA 规范中,工作单元可以具有两种不同的性质:事务范围的持久性上下文和扩展持久性上下文,但也可以手动处理它并获得应用程序管理的工作单元(请参阅另一个答案) 。您使用哪一种取决于用例。

\n
\n

问:我们需要多少个 IdentityMap 实例?

\n
\n

马丁·福勒也在书中回答了这个问题。在他的章节中IdentityMap有一个完整的章节是关于它的。

\n

书中写道:

\n
\n

\xe2\x80\x9c这里的决定在每个班级一张地图和整个会话一张地图之间有所不同。”

\n
\n

您可以将会话理解为本UnitOfWork例中的情况。他在书中后来解释道:

\n
\n

\xe2\x80\x9c如果您有多个映射,则明显的路线是每个类或每个表一个映射,如果您的数据库架构和对象模型相同,则这种方法效果很好。\xe2\x80\x9d

\n
\n

几段之后,马丁解释了将 放在哪里IdentityMap

\n
\n

\xe2\x80\x9c身份映射需要位于易于找到\xe2\x80\x99的位置。它们\xe2\x80\x99还与您\xe2\x80\x99正在工作的进程上下文相关联。您需要确保每个会话都有自己的实例,\xe2\x80\x99与任何其他会话\xe2\x80隔离\x99s 实例。因此,您需要将身份映射放在特定于会话的对象上。如果您\xe2\x80\x99正在使用工作单元,那么\xe2\x80\x99是迄今为止身份映射的最佳位置,因为工作单元是跟踪进出数据库的数据的主要位置。如果您没有工作单元,最好的选择是与会话绑定的注册表。\xe2\x80\x9d

\n
\n

所以,你已经知道了,如果你UnitOfWork绑定了一个请求,那么你IdentityMapsUnitOfWork.

\n
\n

那么,工作单元是否受到业务交易的限制?

\n
\n

是的。

\n

现在,“业务事务”的范围可能是短暂的,例如,绑定到 HTTP 请求的生命周期,如上面 Martin Fowler 的示例,其中 UnitOfWork 似乎绑定到每个请求的线程局部变量。

\n

或者“业务事务”可以包含多个请求,例如,在 JPA 中,有一个扩展持久性上下文的概念,其中 JPA 中的持久性上下文对应于一种UnitOfWork模式。在扩展持久性上下文中,业务事务会扩展更长的时间。一个典型的例子是购物车:当用户开始将商品放入购物车时,就会创建一个上下文/工作单元,并且在用户签出她的购物车(提交更改)之前,上下文不会被清除)或者她的会话过期(丢弃工作单元中的所有内容)。

\n

现在,还有应用程序管理的上下文的想法,这基本上意味着,在您认为合适的时候启动您的工作单元,并在您不再需要它时关闭它。例如,假设您有一个桌面应用程序和一个具有隔离并发性的小型数据库(即每个用户只管理自己的数据,并且不会与其他用户的数据发生冲突)。假设用户的数据完全适合计算机的内存。在这种情况下,您可以UnitOfWork在应用程序启动时启动您的应用程序,并将其用作数据库的一种缓存。在适当的时候,您可以将工作单元刷新到磁盘,例如当用户单击保存按钮时,但仍然保持其活动状态。当用户关闭应用程序时,UnitOfWork 将被丢弃。

\n

因此,您可以看到“商业交易”的实际含义或应存在多长时间存在很多细微差别UnitOfWork

\n

多个工作单元

\n

根据目前的解释,您可以拥有多个工作单元,其中包括以下原因:

\n
    \n
  • 每个请求一个UnitWork,如果您的应用程序处理并发请求,那么您将同时拥有多个工作单元实例。
  • \n
  • 每个扩展事务一个UnitOfWork,因此与用户的会话相关联。如果您有多个用户,则可以有多个工作单元。
  • \n
\n

然而,除此之外,我还发现了您可能希望在同一“业务事务”期间生成新工作单元的其他原因。

\n

当您执行一项业务交易时,您可能想要执行另一项完全独立的交易,这并不罕见。例如,假设为了给客户下订单,客户记录必须存在于数据库中,但是如果客户记录无法创建(例如,可能因为另一个客户有相同的冲突电子邮件),您仍然想要下订单订单处于待审核状态。

\n

因此,您在这里面临的问题是,如果您尝试在下订单的业务事务期间创建客户并且创建客户失败,则会污染您的订单事务,并且您的工作单元被迫回滚所有内容。在这种情况下,您可能希望生成一个新的工作单元,因此需要一个新的、单独的数据库事务来创建客户,并且如果该单独的工作单元无法创建客户,这不会污染您的订单创建单元工作,允许您采取措施在没有客户处于待审核状态的情况下仍然保留您的订单。

\n

我相信 JPA 的“上下文”概念是如何定义工作单元的一个很好的例子。我建议您研究他们的规范,特别是他们关于EntityManager.

\n