【发布时间】:2013-02-10 00:35:15
【问题描述】:
我在使用 Grails 中的代理对象时遇到了困难。 假设我有以下内容
class Order {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name="xxx", joinColumns = {@JoinColumn(name = "xxx")}, inverseJoinColumns = {@JoinColumn(name = "yyy")})
@OrderBy("id")
@Fetch(FetchMode.SUBSELECT)
private List<OrderItem> items;
}
class Customer {
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "xxx",insertable = false, nullable = false)
private OrderItem lastItem;
private Long lastOrderId;
}
在一些控制器类中
//this all happens during one hibernate session.
def currentCustomer = Customer.findById(id)
//at this point currentCustomer.lastItem is a javassist proxy
def lastOrder = Order.findById(current.lastOrderId)
//lastOrder.items is a proxy
//Some sample actions to initialise collections
lastOrder.items.each { println "${it.id}"}
迭代后lastOrder.items 仍然包含currentCustomer.lastItem 的代理。例如,如果 lastOrder.items 集合中有 4 个项目,它看起来像这样:
- 对象
- 对象
- javassist 代理(所有字段为空,包括 id 字段)。这与 currentCustomer.lastItem 中的对象相同。
- 对象
此外,这个代理对象的所有属性都设置为 null,并且在调用 getter 时它没有被初始化。我必须在 lastOrder.items 内的每个元素上手动调用 GrailsHibernateUtils.unwrapIdProxy() 以确保里面没有代理(这基本上会导致 EAGER 获取)。
这个代理对象会导致一些非常奇怪的异常,在测试阶段很难跟踪。
有趣的事实:如果我更改操作的顺序(首先加载订单,然后加载客户)lastOrder.items 中的每个元素都会被初始化。
问题是:有没有办法告诉 Hibernate 当它们被触摸时它应该初始化集合,不管集合中的任何元素是否已经在会话中被代理?
【问题讨论】:
-
如果
it是代理,那么println "${it.id}"不会触发初始化,您必须为此获取一个非ID 属性。 -
@IanRoberts 有官方文档参考吗?因为从我的角度来看,对于代理对象,初始化 Id 字段确实很有意义。特别是在使用ManyToOne的情况下(外键存储在父对象中)
-
Hibernate 中的代理是一个只知道 ID 的持有者。它可以响应获取 ID 的请求,而无需访问数据库,但是当您向它询问其他任何内容时,它会使用 ID(它已经知道)来查询数据库并加载填充了所有属性的真实对象。代理委托上的后续方法直接调用底层对象。
-
是的,你是对的。我误解了你的评论。问题是代理的id字段为空(在调试视图和控制台日志中显示)
-
lastOrder.items 是一个集合
,而 currentCustomer.lastItem 是一个订单。然而你说“lastOrder.items 仍然包含 currentCustomer.lastItem 的代理”。你能澄清一下你的意思吗?