在视图模式中打开会话

时间:2009-12-04 13:46:43

标签: java design-patterns session orm

我问这个问题给出了我选择的JPA(Hibernate实现),Spring和<插入MVC框架的开发框架 - Struts 1,Struts 2,Spring MVC,Stripes ...>。

我一直在考虑我的实体层中的关系 - 例如我有一个订单实体,它有许多订单行。我已经设置了我的应用程序,因此它急切地为每个订单加载订单行。如果我将获取策略设置为false,你认为这是一种懒惰的方法来解决我会遇到的延迟初始化问题吗?

我看到它的方式,在检索实体及其关联时我有以下选择:

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

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

  3. 不要映射JPA中的任何关联,以便我执行的每个查询都只返回我感兴趣的实体 - 这可能要求我无论如何都要有DTO,这将是一个难以维护的问题,我认为失败首先要有ORM的目的。

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

  5. 所以我的问题是,你何时以及在什么情况下会使用哪些选项?你总是坚持一种做法吗?

    我会问一位同事,但我认为,如果我甚至提到“开放式会议”这一术语,我会受到一片空白的欢迎:(我在这里真正需要的是一些资深或经验丰富的建议显影剂。

    谢谢你们!

6 个答案:

答案 0 :(得分:15)

在View中打开会话有一些问题

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

其他例子,按需阅读数据可能会转向许多“N + 1选择”问题,这会影响您的表现。


许多项目使用以下路径:

  1. 维护业务层的交易;在那一点加载你应该需要的一切。
  2. 表示层冒着LazyExceptions的风险:每个都被认为是编程错误,在测试期间被捕获,并通过在业务层中加载更多数据进行更正(您有机会有效地执行此操作,避免“N + 1选择”问题。
  3. 为避免为DTO创建额外的类,您可以在实体对象中加载数据。这是POJO方法的全部要点(现代数据访问层使用,甚至像Spring这样的集成技术)。

答案 1 :(得分:10)

我已成功解决了Open View In View -pattern(即Spring实现)的所有延迟初始化问题。我使用的技术与您完全相同。

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

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

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

N + 1选择

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

答案 2 :(得分:3)

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

我们在服务层管理了大部分交易,但在视图层中保持了hibernate会话的开放状态。这意味着视图中的延迟读取导致单独的读取事务。

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

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

答案 3 :(得分:1)

虽然DTO方法有一些好处。您必须事先考虑您需要哪些信息。在某些情况下,这将阻止您生成n + 1个选择语句。它还有助于查看在何处使用预先获取和/或优化的视图。

答案 4 :(得分:1)

我也会支持Open-Session-in-View模式,之前就已经完全相同了。

我在没有弹簧的情况下使用Stripes,并且在使用之前创建了一个手动过滤器。正如你所提到的,后端的编码事务逻辑变得很乱。当您将越来越多的对象映射到彼此时,急切地抓取所有东西变得可怕。

我想补充一点,你可能没有遇到的是Stripersist和Stripernate - Stripersist更具JPA味道 - 自动保湿过滤器可以让你的大量工作脱离你的肩膀。

使用Stripersist,您可以说/appContextRoot/actions/view/3之类的内容,它会在执行事件之前自动为ActionBean上的JPA实体添加id,其值为3。

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

答案 5 :(得分:0)

订单和订单行是否构成大量数据?他们是否参与需要实时响应的在线流程?如果是这样,你可能会考虑不使用渴望的提取 - 它确实在性能方面产生了很大的差异。如果数据量很小,则急切提取没有问题。

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

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