在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@支持重用当前会话和事务。延迟初始化将创建一个代理对象,但在此上下文中,代理对象仍将使用当前会话和事务。