我遇到了像大多数尝试使用ORM进行远程处理的人一样的LazyLoading异常。 在大多数情况下,切换到急切的提取解决了问题(延迟加载/非原子查询/线程安全/ n + 1问题......)。但是,如果你正在处理一个非常大的对象图,那么急切的提取也会有缺点。
在大多数用例中不需要加载整个对象图。加载需要的更多数据(或从db加载它们并提取所需的子集)感觉很糟糕。
那么有什么替代方法可以解决这类问题(在运行时)? 我见过:
我想到了另一种方式。也许通用投影white./black listning是一个解决方案。
Idea(黑名单):使用提取操作的边界定义类名列表。如果属性匹配并且它是惰性的,则删除惰性(CGLIB)代理并使用null填充该值。否则,简单地防止获取(并将值保留为null)。因此,我们可以在DAO中设置明确的界限。
示例:ProductDao.findByName("Soap",Boundaries.BLACKLIST,"Category, Discount")
最后两个参数也可以绑定到Boundaries对象中。
Idea(白名单):与黑名单一样,但您必须声明属性应加载到白名单中。
您如何看待这样的解决方案? (可能的问题,限制,优势......) 我应该如何在java中写这个?也许通过AOP来匹配DAO方法(因为我能够在那里修改cglib代理行为)?
答案 0 :(得分:6)
您可以删除所有收藏品并使用NamedQueries
代替。我们在一个项目(EJB + Swing)中使用了这种方法,并且它工作得很好 - 因此您可以确定要获取的确切数据。
NamedQueries是普通查询,将它们想象为PreparedStatement-s。我们的想法是不使用查询创建/检索/更新/删除单个对象。我们的想法是您使用查询来获取集合。例如,不是映射@ManyToMany List,而是定义一个获取该列表的NamedQuery。因此,您可以单独获取集合数据,并且仅在您需要时,而不是自动获取。
对传输的对象使用自定义代理(使用CGLIB) - 每当引用一个集合(通过其getter),尝试retreival,并捕获任何LazyInitializationException
并调用服务器层以获取请求的数据。
就像前一个一样,但只创建集合的代理,就像Hibernate在需要延迟初始化时代理它们一样。
另外,看看Value List Handler模式 - 可能有用。
(如果适合您的情况,您也可以使用hibernate.max_fetch_depth
(如果使用Hibernate)以及上述组合。)
答案 1 :(得分:1)
如今(2013年),如果您使用GraniteDS远程服务,则可以保持延迟加载。
这应该通过不初始化惰性关系并保持客户端的惰性状态来正确地序列化JPA或Hibernate实体。如果您在客户端上访问这些关系,它将在后台透明地获取它们。
此外,GraniteDS似乎能够进行反向延迟加载,这意味着当您将修改后的对象发送回服务器时,它不会返回未更改的实体,从而使必要的服务器通信非常有效。
我不是GraniteDS专家(但是)它似乎能够与JEE6和Spring服务层集成,并与所有最重要的JPA提供商合作。
当然,您需要在服务接口后面隐藏基于GraniteDS的远程处理以最大化透明行为,但如果客户端也使用Spring(因此您根据环境需求注入服务),则可以轻松完成。
答案 2 :(得分:0)
虽然需要一些工作,并且JAX-WS / JAXB需要这些库的最新版本,但这是一个非常优雅的解决方案:创建一个可以测试对象/集合是否已初始化的编组器。
如下所述: https://forum.hibernate.org/viewtopic.php?f=1&t=998896