在Spring JPA Hibernate中理解具有延迟加载的事务会话
我想得到一些关于延迟加载和会话边界等方面的澄清 我的代码结构如下在Spring JPA Hibernate中理解具有延迟加载的事务会话,spring,exception,jpa,lazy-loading,spring-transactions,Spring,Exception,Jpa,Lazy Loading,Spring Transactions,我想得到一些关于延迟加载和会话边界等方面的澄清 我的代码结构如下 @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) Lay
@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上有支持,它是否会创建一个单独的“会话”
任何正确理解的建议都会对我有很大帮助
谢谢。使用延迟加载,当您请求类型a的对象a时,您会得到类型a的对象a。
a.getB()
但是,将不会是类型B,而是a.getB()
是B的代理,可以稍后解析(这是延迟加载部分),但只能在a所在的持久性上下文中解析
您的第二个实现就是这样做的:当您仍在@Transaction
中时,它通过调用a.getB().getName()
来解析B。Hibernate现在可以向数据库发出第二个请求来获取B,现在a。getB()实际上是类型B,并且保持这种状态,因此您可以在持久性上下文之外使用它
您的第一个实现跳过了这一点。从数据库中提取A,事务块结束,然后调用A.getB().getName()
,但现在持久性上下文消失了,A.getB()
无法从数据库中提取,并引发异常。如果将@Transactional(SUPPORTS)添加到LayerB,会发生什么?
传播支持意味着该方法加入调用方的事务。
根据想法,它将加入创建的同一交易层。
由于LayerDB的getAForId方法在同一事务中运行,这意味着它们具有相同的perstance上下文,因此获取B名称应该不会有问题。
我只是猜测。根据我的尝试,组件A调用模块X模块X调用组件B,其中组件A和B由spring管理,由A启动的事务不能由组件B使用,因为无法通过非spring管理的模块X传播。这理解正确吗?谢谢。我就是这么想的。A和B的代理对象始终存在,但只需要在spring管理的组件中获取。但是假设有两个spring组件X和Y,它们都具有事务性。现在让我们假设X调用Y只得到A,而B配置为延迟加载。由于X也是事务性的,所以当控件返回到X时,我还能执行a.getB().getName()吗?是的,这是我的理解。如果一个方法是事务性的,那么将为它调用的所有方法建立持久性上下文,不管它们是否是事务性的。唯一的例外情况是,如果被调用的方法调用不同的事务,例如,如果调用方是事务性的(Context1),而被调用方是事务性的(Context2),或者如果被调用方自己进行事务管理。很抱歉,此回答是错误的。LayerC设置事务,因此存在当前打开的会话和打开的事务。Annotation@支持重用当前会话和事务。延迟初始化将创建一个代理对象,但在此上下文中,代理对象仍将使用当前会话和事务。