Понимание сеанса транзакций с отложенной загрузкой в ​​Spring JPA Hibernate - PullRequest
3 голосов
/ 26 марта 2012

Я хотел бы получить некоторые разъяснения относительно отложенной загрузки и границ сеанса и т. Д.

Моя структура кода выглядит следующим образом

@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);

    }
}

Теперь, когда мы пытаемся получить доступ к B в объекте A внутриметод

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?

Даже если у нас есть SUPPORTS наDBLayer, он создает отдельную «сессию».

Любые указатели для правильного понимания очень мне помогут.

Спасибо.

Ответы [ 2 ]

16 голосов
/ 26 марта 2012

При отложенной загрузке, когда вы запрашиваете объект a типа A, вы получаете объект a типа A. a.getB() однако, не будет иметь тип B, вместо этого a.getB() является прокси для B, который может быть разрешена позже (это ленивая часть загрузки), но только в контексте постоянства, в котором живет.

Ваша вторая реализация делает именно это: она разрешает B, вызывая a.getB().getName(), пока вы все еще находитесь в @Transaction. Hibernate теперь может сделать второй запрос к базе данных, чтобы получить B, и теперь a.getB() действительно имеет тип B и остается таким, поэтому вы можете использовать его вне контекста постоянства.

Ваша первая реализация пропускает это. A извлекается из базы данных, блок @Transactional заканчивается, затем вы вызываете a.getB().getName(), но теперь контекст постоянства исчез, a.getB() не может быть извлечен из базы данных, и выдается исключение.

0 голосов
/ 18 сентября 2013

Что произойдет, если вы добавите @Transactional (SUPPORTS) в LayerB? ПОДДЕРЖКА распространения означает, что метод присоединяется к транзакции вызывающего. По идее он присоединится к тому же созданному переходу LayerC. А так как метод getAForId LayerDB выполняется в рамках одной транзакции, это означает, что они имеют одинаковый контекст сохранения, не должно быть проблем с получением имени B. Я просто догадываюсь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...