远程病例中的Lazy / Eager加载策略(JPA)

我遇到了像大多数尝试使用ORM进行远程处理的人一样的LazyLoadingexception。 在大多数情况下,切换到急切的提取解决了问题(延迟加载/非primefaces查询/线程安全/ n + 1问题……)。 但是如果你正在处理一个非常大的对象图,那么渴望获取也会有缺点。

在大多数用例中不需要加载整个对象图。 加载需要的更多数据(或从db加载它们并提取所需的子集)感觉很糟糕。

那么有什么替代方法可以解决这类问题(在运行时)?
我见过:

  • 将数据访问依赖项注入域对象,并让对象决定加载延迟或急切: 感觉不好 ! 域层应独立于任何服务。 域注入也是一项昂贵的操作。 域应该是无知的数据访问,应该使用或不使用数据访问。
  • 除了需要更多数据的用例之外,获取所有惰性内容:看起来效果更好但这种方式会强制许多客户端=>服务器/数据库往返。 懒惰字段的初始化也会遭受痛苦(尝试使用JPA)。 这种方式不具有通用性 ,并且受到上述相同的惰性限制。
  • 在Lazy类中封装持久性:更复杂,没有与ORM互操作的最佳实践。 膨胀的服务层(这么多“手写”代码感觉很糟糕 )。
  • 对每个用例使用完整的预测:我们将最终使用SQL并放弃ORM的好处。
  • DTO /虚拟代理层强制执行更多复杂性并使代码更难维护(Wormhole antipattern >> Bloat)。

我想了很多其他的方式。 也许通用投影white./black listning是一个解决方案。

Idea(黑名单):使用提取操作的边界定义类名列表。 如果属性匹配并且它是惰性的,则删除惰性(CGLIB)代理并使用null填充该值。 否则,简单地防止获取(并将值保留为null)。 因此,我们可以在DAO中设置明确的界限。

示例: ProductDao.findByName("Soap",Boundaries.BLACKLIST,"Category, Discount")最后两个参数也可以绑定到Boundaries对象中。

Idea(白名单):与黑名单一样,但您必须声明属性应加载到白名单中。

你怎么看待这样的解决方案? (可能的问题,限制,优点……)我应该如何在java中编写这个? 也许通过AOP匹配DAO方法(因为我能够修改cglib代理行为)?

  1. 您可以删除所有集合,而是使用NamedQueries 。 我们在一个项目(EJB + Swing)中使用了这种方法,并且它工作得很好 – 因此您可以确定要获取的确切数据。 NamedQueries是普通查询,将它们想象为PreparedStatement-s。 我们的想法是不使用查询创建/检索/更新/删除单个对象。 这个想法是你用查询来获取你的collections 。 例如,不是映射@ManyToMany List,而是定义一个获取该列表的NamedQuery。 因此,您可以单独获取集合数据,并且只在您需要时,而不是自动获取。

  2. 对传输的对象使用自定义代理(使用CGLIB) – 每当引用一个集合(通过其getter),尝试retreival,并捕获任何LazyInitializationException并为请求的数据调用服务器层。

  3. 就像前一个一样,但只做集合的代理,就像Hibernate在需要延迟初始化时代理它们一样。

  4. 另外,看一下Value List Handler模式 – 可能很有用。

(如果适合你的情况,你也可以使用hibernate.max_fetch_depth (如果使用Hibernate)和上面的组合。)

现在(2013年),如果您使用GraniteDS远程服务,则可以保持延迟加载。

这应该通过不初始化惰性关系并保持客户端的惰性状态来正确地序列化JPA或Hibernate实体。 如果您在客户端上访问这些关系,它将在后台透明地获取它们。

此外,GraniteDS似乎能够进行反向延迟加载,这意味着当您将修改后的对象发送回服务器时,它将不会返回未更改的实体,从而使必要的服务器通信非常有效。

我不是GraniteDS专家(但是)它似乎能够与JEE6和Spring服务层集成,并与所有最重要的JPA提供程序一起工作。

当然,您需要在服务接口后面隐藏基于GraniteDS的远程处理以最大化透明行为,但如果客户端也使用Spring(因此您可以根据环境需求注入服务),则可以轻松完成。

虽然需要一些工作,并且JAX-WS / JAXB需要这些库的最新版本,但这是一个非常优雅的解决方案:创建一个可以测试对象/集合是否已初始化的编组器。

如下所述: https : //forum.hibernate.org/viewtopic.php?f = 1&t = 998896