我想对延迟加载和会话边界等进行一些澄清。
我的代码结构如下
@Entity
class A {
....
@OneToOne(fetch=LAZY)
private B b;
..
}
@Entity
class B {
private id;
private name;
}
@Transactional(SUPPORTS)
ADao {
A findById(int id);
}
@Transactional(SUPPORTS)
LayerDB {
A getAForId(int i) {
return adao.findById(i);
}
}
//Note that there is no transactional attribute here
LayerB {
public boolean doSomethingWithAandB(int aId) {
A a = LayerDB.getAForId(aId);
if(a.getB().getName().equals("HIGH"))
return true;
return false;
}
}
//start transaction here
@Transaction(REQUIRED)
LayerC {
LayerB layerb;
private handleRequest(int id) {
layerb.doSomethingWithAandB(id);
}
}
现在我们尝试在方法
中访问实体A中的B.doSomethingWithAandB
尝试访问B时出现延迟初始化异常。
即使该方法在LayerC中创建的事务中,仍然会出现以下异常
Exception : org.hibernate.LazyInitializationException: could not initialize proxy - no Session
但是将以下两种方法改为:
@Transactional(SUPPORTS)
LayerDB {
A getAForId(int i) {
A a = adao.findById(i);
a.getB().getName();
return a;
}
}
//Note that there is no transactional attribute here
LayerB {
public boolean doSomethingWithAandB(int aId) {
A a = LayerDB.getAForId(aId);
if(a.getB().getName().equals("HIGH"))
return true;
return false;
}
}
为什么不使用在LayerC中创建的事务/会话?
即使我们在DBLayer上有SUPPORTS,它是否会创建一个单独的“会话”。
正确理解的任何指示都会对我有所帮助。
谢谢。
答案 0 :(得分:16)
使用延迟加载时,当您请求类型A的对象a时,您将获得类型为A的对象a。a.getB()
但是,不会是类型B,而a.getB()
是代理B可以在以后解决(这是延迟加载部分),但仅限于生存的持久化上下文。
您的第二个实现就是这样:当您仍在a.getB().getName()
时,它会通过调用@Transaction
来解析B. Hibernate现在可以向数据库发出第二个请求以获取B,现在a.getB()
实际上是B类并保持这种状态,因此您可以在持久化上下文之外使用它。
你的第一个实现会跳过它。从数据库中获取A,@Transactional
块结束,然后调用a.getB().getName()
,但现在持久化上下文消失,无法从数据库中获取a.getB()
,并且异常是抛出。
答案 1 :(得分:0)
如果您将@Transactional(SUPPORTS)添加到LayerB会发生什么? 传播SUPPORT意味着该方法加入调用者的事务。 根据想法,它将加入相同的Transation LayerC创建。 而且由于LayerDB的getAForId方法在同一个事务中运行,这意味着它们具有相同的持久化上下文,因此获取B名称应该没有问题。 我只是在猜测。