在视图模式中打开会话

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

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

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

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

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

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

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

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

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

多谢你们!

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

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

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

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

N + 1选择

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

Open View in View存在一些问题

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

其他例子,按需读取数据可能会转向许多“N + 1选择”问题 ,这会导致您的性能下降。


许多项目使用以下路径:

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

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

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

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

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

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

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

我也将重点放在Open-Session-in-View模式之后,之前一直在同一条船上。

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

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

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

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

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

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

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