使用具有类似于以下内容的类类层次结构的grails时:
abstract class Vehicle { ... }
class Car extends Vehicle { ... }
class Motorcycle extends Vehicle { ... }
以及以下服务:
class VehicleService {
def startRepairing(Car car) { ... }
def startRepairing(Motorcycle motorcycle) { ... }
}
我们经常在生产中遇到以下错误:
没有方法签名:VehicleService.startRepairing()适用于 参数类型:(Car _ $$ _ javassist_156)值:[Id:42343,Class: 汽车]。可能的解决方案: startRepairing(CAR)
我们相信这是因为我们从Vehicle
这样的集合中检索static hasMany = [vehicles: Vehicle]
实例,这导致代理实现抽象类Vehicle
而不是具体类({{ 1}},Car
等。)
我们过去常常从方法中删除参数类型作为解决方案,但我们宁愿拥有它 - 代码更清晰,方法重载是可能的,更友好的IDE ...
我们考虑的一个解决方案是当类型与任何其他方法不匹配时使用臭名昭着的GrailsHibernateUtil.unwrapIfProxy:
Motorcycle
但问题是,我们如何测试呢?在开发中运行代码时,我们很少发现javassist问题,甚至在生产中它似乎“随机”发生(或者更确切地说,由于条件逃避了我们的知识:)。
是否可以强制实例成为javassist代理?对于这类问题,一般来说什么是好策略?
答案 0 :(得分:6)
当您需要一个延迟加载的类实例时,Hibernate会创建代理。您需要的东西是期望类的实例或子类,并且一旦完全加载,它基本上就像一个急切加载的实例。 Hibernate使用字节码库创建类的子类以用作代理的方法效果很好。
所以对于一个1-1和一个'在1-many的一侧,延迟加载的实例将是一个代理。同样在延迟加载的集合中," extra-lazy",实例都将是代理。当你知道你只需要来自某些集合的数据时,这会更好地工作 - 到#34;填充"当需要按需加载集合时,查询只查找id,集合中的实例将只是存储了id的代理。如果你遍历整个系列,那么你就可以了。最终运行它的N + 1个查询,但是如果你只需要一些,那么一般来说,它应该比填充集合时为所有实例加载所有数据的资源更少,并且创建非-proxy集合成员(如果只需要一些成员)。
另一个容易看到代理的地方是使用load()
方法。 get()
查看先前加载的值的第1和第2(如果是活动的并且为域类启用),否则立即进入数据库,如果没有该id的记录,则返回null。它不会引发异常,因为它很容易知道它是否成功。但是,load()
只有在访问id以外的属性时才会访问数据库。如果没有记录,则抛出异常,因为您不一定接近(时间或代码方式)创建代理的初始load()
调用,也因为那里有一个隐含的假设,即通过延迟加载,你期望得到一个结果,所以在这种情况下,null是例外。