Java 远程处理案例中的延迟/急切加载策略(JPA)

Java 远程处理案例中的延迟/急切加载策略(JPA),java,orm,jpa,remoting,lazy-loading,Java,Orm,Jpa,Remoting,Lazy Loading,我遇到了懒散的加载异常,就像大多数尝试使用ORM远程处理的人一样。 在大多数情况下,切换到即时抓取可以解决问题(延迟加载/非原子查询/线程安全/n+1问题…)。但是,如果您处理的是一个非常大的对象图,那么即时抓取也有缺点 在大多数用例中不需要加载整个对象图。加载更多需要的数据(或者从数据库加载数据并提取所需的子集)感觉很糟糕 那么(在运行时)有什么替代方法来解决此类问题呢? 我看到: 将数据访问依赖项注入到域对象中,让对象决定加载懒惰或急切:感觉糟透了!域层应该独立于任何服务。域注入也是一项昂

我遇到了懒散的加载异常,就像大多数尝试使用ORM远程处理的人一样。 在大多数情况下,切换到即时抓取可以解决问题(延迟加载/非原子查询/线程安全/n+1问题…)。但是,如果您处理的是一个非常大的对象图,那么即时抓取也有缺点

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

那么(在运行时)有什么替代方法来解决此类问题呢?
我看到:

  • 将数据访问依赖项注入到域对象中,让对象决定加载懒惰或急切:感觉糟透了!域层应该独立于任何服务。域注入也是一项昂贵的操作。域应该是无数据访问的,并且应该在有或没有数据访问的情况下使用
  • 除了需要更多数据的用例之外,所有的东西都是惰性的:对于性能来说似乎更好,但这种方式会导致许多客户机=>服务器/数据库往返。惰性字段的初始化也会带来痛苦(使用JPA进行了尝试)。这种方式感觉不一般,并且受到上述相同的懒惰限制
  • 在惰性类中封装持久性:更复杂,没有与ORM互操作的最佳实践。臃肿的服务层(这么多“手工编写”的代码感觉很糟糕)
  • 对每个用例使用完整的投影:我们最终将使用SQL并放弃ORM的好处
  • DTO/虚拟代理层强制执行更高的复杂性,使代码更难维护(虫洞反模式>>膨胀)
我想了很多其他的方法。也许通用投影白/黑列表是一种解决方案

Idea(黑名单):为抓取操作定义一个带有边界的类名列表。如果某个属性匹配并且是惰性的,请删除惰性(CGLIB)代理并用null填充该值。否则,simple将阻止获取(并将值保留为null)。因此,我们可以在DAO中设置清晰的边界

示例:
ProductDao.findByName(“Soap”,边界。黑名单,“类别,折扣”)
最后两个参数也可以绑定到边界对象中

Idea(白名单):类似于黑名单,但您必须声明属性,并且应该将其加载到白名单中

你认为这样的解决方案怎么样?(可能的问题、限制、优势…) 我应该如何用java编写这个?也许可以通过AOP来匹配DAO方法(因为我可以在那里修改cglib代理行为)

  • 您可以去掉所有集合,改用
    namedquerys
    。我们在一个项目(EJB+Swing)中使用了这种方法,它工作得非常好——因此您可以确定要获取的确切数据。 NamedQueries是普通查询,将它们想象为PreparedStatement-s。其思想不是使用查询创建/检索/更新/删除单个对象。其思想是通过查询获取集合。例如,与其映射@ManyToMany列表,不如定义一个获取该列表的NamedQuery。因此,您可以单独获取收集数据,并且仅在需要时获取,而不是自动获取

  • 对传输的对象使用自定义代理(使用CGLIB)-每当引用集合时(通过其getter),尝试检索,捕获任何
    LazyInitializationException
    ,并为请求的数据调用服务器层

  • 与前一个一样,但只对集合进行代理,就像Hibernate在需要延迟初始化时对集合进行代理一样

  • 另外,看看这个模式——可能会有用


  • (如果适合您的情况,您还可以将(如果使用Hibernate)与上述方法结合使用。)

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

    如下所述:

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

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

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

    我还不是GraniteDS专家,但它似乎能够与JEE6和Spring服务层集成,并与所有最重要的JPA提供商合作


    当然,您需要将基于GraniteDS的远程处理隐藏在服务接口后面,以最大限度地提高透明行为,但如果客户端也使用Spring,这一点很容易做到(因此您可以根据环境的需要注入服务).

    如果将命名查询应用于每个域类,则CRUD操作的代码会重复。我使用的是spring编写的通用JPA DAO。避免hibernate依赖也是非常重要的。通过AOP的Decorator/Proxy看起来不错。但是您的方法没有与数据层(域/服务混合|域/数据访问混合)分离,因为域获取者应该进行服务器调用。我尽量避免像我的问题中所描述的那样注入到模型中(调用逻辑等)。例如,Hibernate在会话仍然存在时使用相同的思想来获取惰性集合-它生成包含逻辑的集合的代理。所以做这件事没什么大不了的。NamedQueries并不是CURD操作的复制品,而是@*ToMany注释(以及相关的东西)——有点夸张