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