【问题标题】:Open Session In View Pattern在视图模式中打开会话
【发布时间】:2010-12-23 06:05:12
【问题描述】:

鉴于我选择的 JPA(Hibernate 实现)、Spring 和 的开发框架,我问这个问题。

我一直在思考我的实体层中的关系——例如,我有一个包含许多订单行的订单实体。我已经设置了我的应用程序,以便它急切地加载每个订单的订单行。如果我将 fetch 策略设置为 false,您是否认为这是一种解决延迟初始化问题的懒惰方法?

在我看来,在检索实体及其关联时,我有以下选择:

  1. 使用 Open Session In View 模式在每个请求上创建会话并在返回响应之前提交事务。

  2. 实现一个 DTO(数据传输对象)层,以便我执行的每个 DAO 查询都返回正确初始化的 DTO。我不太喜欢这个选项,因为根据我的经验,我发现它会创建大量的样板复制代码并且变得难以维护。

  3. 不要在 JPA 中映射任何关联,以便我执行的每个查询只返回我感兴趣的实体 - 这可能需要我拥有 DTO 并且维护起来很痛苦,我认为失败首先拥有 ORM 的目的。

  4. 急切地获取所有(或大多数关联) - 在上面的示例中,当我检索订单时始终获取所有订单行。

所以我的问题是,您会在什么时候以及在什么情况下使用这些选项中的哪一个?你总是坚持一种方式吗?

我会问一位同事,但我认为,如果我什至提到“Open Session in View”这个词,我会被空白的目光打招呼:(我在这里真正想要的是来自资深或非常有经验的一些建议开发者。

谢谢大家!

【问题讨论】:

    标签: java design-patterns session orm


    【解决方案1】:

    我已经在一个项目中成功使用了 Open-Session-in-View 模式。然而,我最近在“Spring In Practice”中读到了一个有趣的潜在问题,即如果您在较低层管理事务,同时在视图层中保持 Hibernate 会话打开。

    我们在服务层管理大部分事务,但在视图层保持休眠会话打开。这意味着视图中的惰性读取会导致单独的读取事务。

    我们在服务层管理我们的交易,以最大限度地减少交易持续时间。例如,我们的一些服务调用导致数据库事务和对外部服务的 Web 服务调用。我们不希望在等待 Web 服务调用响应时打开我们的事务。

    由于我们的系统从未投入生产,我不确定它是否存在任何真正的问题,但我怀疑视图可能会尝试延迟加载已被其他人删除的对象。

    【讨论】:

      【解决方案2】:

      我也将全力支持 Open-Session-in-View 模式,我之前也曾在同一条船上。

      我在没有弹簧的情况下使用 Stripes,并且在此之前创建了一个手动过滤器,效果很好。正如您所提到的,后端的编码事务逻辑很快就会变得混乱。当您将越来越多的对象相互映射时,急切地获取所有内容变得非常糟糕。

      我想补充一点,您可能没有遇到过 Stripersist 和 Stripernate - Stripersist 更具有 JPA 风格 - 自动补水过滤器可以减轻您的大量工作。

      使用 Stripersist,您可以说出 /appContextRoot/actions/view/3 之类的内容,它会在事件执行之前自动对 ID 为 3 的 ActionBean 上的 JPA 实体进行水合。

      Stripersist 在stripes-stuff package on sourceforge。我现在将它用于所有新项目,因为它很干净并且在必要时可以轻松支持多个数据源。

      【讨论】:

        【解决方案3】:

        订单和订单行是否包含大量数据?他们是否参与需要实时响应的在线流程?如果是这样,您可能会考虑不使用急切获取 - 它确实会在性能上产生巨大差异。如果数据量小,Eager fetching没有问题。

        关于使用 DTO,它可能是一个可行的实现。 如果您的业务层由您自己的应用程序(即小型 Web 应用程序及其业务逻辑)在内部使用,则最好在视图中使用您自己的实体并使用视图模式中的开放会话,因为它更简单。

        如果您的实体被许多应用程序(即在您的公司中提供服务的后端应用程序)使用,那么使用 DTO 会很有趣,因为您不会将模型暴露给您的客户。暴露它可能意味着您将难以重构您的模型,因为这可能意味着与您的客户签订合同。 DTO 会使这更容易,因为你有另一层 抽象。这可能有点奇怪,因为 EJB3 理论上会消除对 DTO 的需求。

        【讨论】:

          【解决方案4】:

          我已经通过 Open Session In View 模式(即 Spring 实现)成功地解决了我所有的延迟初始化问题。我使用的技术和你完全一样。

          使用这种模式,我可以完全映射实体关系,而不必担心在 dao 中获取子实体。大多。在 90% 的情况下,该模式解决了视图中的延迟初始化需求。在某些情况下,您必须“手动”初始化关系。这些案例很少见,而且在我的案例中总是涉及非常非常复杂的映射。

          在使用 Open Entity Manager In View 模式时,正确定义实体关系,尤其是传播和事务设置非常重要。如果这些配置不正确,当某些实体在视图中延迟初始化并且由于会话已经关闭而失败时,将会出现与关闭会话相关的错误。

          我肯定会选择选项 1。有时可能需要选项 2,但我认为绝对没有理由使用选项 3。选项 4 也是不行的。急切地获取所有内容会破坏任何视图的性能,该视图只需要列出一些父实体的一些属性(在这种情况下是订单)。

          N+1 选择

          在开发过程中,由于在视图中初始化了一些关系,将会有 N+1 次选择。但这不是放弃这种模式的理由。只需在这些问题出现时解决这些问题,然后再将代码交付给生产环境。使用 OEMIV 模式解决这些问题与使用任何其他模式一样容易:添加正确的 dao 或服务方法,修复控制器以调用不同的 finder 方法,可能向数据库添加视图等。

          【讨论】:

          • 您好,您说:“在使用 Open Entity Manager In View 模式时,正确定义实体关系,尤其是传播和事务设置非常重要。”请澄清我已经追踪了 3 天的会话关闭问题并且我正在拔头发,需要做些什么来避免会话关闭。
          【解决方案5】:

          Open Session in View 存在一些问题

          例如,如果事务失败,您可能在提交时知道为时已晚,一旦您几乎完成了页面的呈现(可能响应已经提交,因此您无法更改页面!)...如果您之前知道该错误,您将遵循不同的流程并最终呈现不同的页面...

          其他示例,按需读取数据可能会导致许多“N+1 选择”问题,从而影响您的性能。


          许多项目使用以下路径:

          1. 在业务层维护交易;加载你应该需要的一切。
          2. 表示层存在 LazyExceptions 的风险:每个都被认为是一个编程错误,在测试期间被捕获,并通过 在业务层加载更多数据进行纠正(您有机会有效地做到这一点,避免“N+1 选择”问题)。

          为避免为 DTO 创建额外的类,您可以将数据加载到实体对象本身中。这是 POJO 方法的全部要点(现代数据访问层使用,甚至是 Spring 等集成技术)。

          【讨论】:

          • 您有点夸大了 N+1 选择问题。使用 Open Session In View Pattern 时同样容易修复。只需添加适当的 dao 或服务方法,可能还有数据库视图等。然后使用这些来获取先前在视图中延迟初始化的数据。没有必要仅仅因为可能(并且将会)有 N+1 个选择而放弃 Open Session In View 模式。只需在它们出现时修复它们。
          • @kosoant 我理解你的观点,并认为你在某些情况下是对的。但是让视图直接调用 DAO 而不涉及业务层有一些缺点。在我们的项目和许多其他大项目中,业务层还有其他无法绕过的职责(安全、日志记录等)......
          【解决方案6】:

          不过,DTO 方法有一些好处。你必须事先考虑你需要什么信息。在某些情况下,这会阻止您生成 n+1 选择语句。它还有助于查看在哪里使用急切获取和/或优化的视图。

          【讨论】:

            猜你喜欢
            • 2014-11-23
            • 2011-04-11
            • 1970-01-01
            • 1970-01-01
            • 2011-01-14
            • 2015-08-14
            • 2011-03-10
            • 1970-01-01
            • 2022-01-24
            相关资源
            最近更新 更多