Подтвердить что ты не робот

Почему @Transactional (распространение = Распространение .SUPPORTS, readOnly = true) исправить Hibernate Lazy Loading Exceptions?

Я переключил приложение, с которого я работаю, используя плейер времени загрузки AspectJ с использованием прокси-серверов Spring CGlib, и сразу после того, как я сделал это, было много частей кода, в которых я начал получать спящие ленивые загрузки, исключая случаи, когда в прошлом исключений не было.

Я смог разрешить эти ленивые исключения для загрузки, добавив @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) в кучу ранее открытых методов, у которых не было каких-либо транзакционных атрибутов, но называемых репозиториями w760 для чтения данных из базы данных.

Кто-нибудь знает, почему добавление @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) устраняет исключения для лэйзинга загрузки в спящем режиме и почему эти аннотации не требуются при вставке времени AspectJ, но они требуются без?

Обновление 2 Я считаю, что удаление AspectJ не было проблемой, но проблема заключалась в том, что я действительно не понимал реального поведения распространения SUPPORTS. В частности, как SUPPORTS взаимодействовали с JPA EntityManager, и поэтому я удалил кучу распространение SUPPORTS, которое вызвало ленивые ошибки загрузки. Прочитав исходный код для Spring Transaction Manager, все стало ясно, что делать. Основная идея, что документация Spring не очень хорошо указывает на то, что аннотации @Transactional используются в качестве точек синхронизации, которые связывают жизненный цикл EntityManager с началом и окончанием транзакционного метода. Также настоятельно рекомендуем эту серию статей на http://www.ibm.com/developerworks/java/library/j-ts1/ и этот пост в блоге http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/

Обновление 1

Это не относится к вызовам частных методов @Transactional, которые не проходят через прокси-сервер AOP. Эти проблемы происходят с общедоступными методами, вызываемыми из других служб.

Вот пример структуры кода, где я вижу возникшую проблему.

@Service
public class FooService 
{
   @Autowired
   private BarService barService;

   public void someMethodThatOnlyReads() {
      SomeResult result = this.barService.anotherMethodThatOnlyReads()

      // the following line blows up with a HibernateLazyLoadingEcxeption 
     // unless there is a @Transactional supports annotation on this method
      result.getEntity().followSomeRelationship(); 
    }

}

@Service
public class BarService 
{
   @Autowired
   private BarRepository barRepo;

   public SomeResult anotherMethodThatOnlyReads()
   {
      SomeEntity entity =  this.barRepo.findSomeEntity(1123);
      SomeResult result = new SomeResult();
      result.setEntity(entity);
      return result; 
    }
}

@Repository
public class BarRepository 
{
   @PersistenceContext
   private EntityManager em;

   public SomeEntity findSomeEntity(id Integer)
   {
      em.find(SomeEntity.class,id);
   }
}
4b9b3361

Ответ 1

Я предполагаю, что ваш код не использует OpenSessionInViewFilter или что-то подобное.

Без аннотации @Transactional сеанс Hibernate закрывается после выхода из метода BarRepository.findSomeEntity().

Когда вызывается метод @Transactional, и TransactionalInterceptor должным образом привязан к методу (через прокси cglib или любую другую конфигурацию AOP, которую вы имеете в контексте Spring), сеанс удерживается открытым Spring для всего аннотированного метода, тем самым предотвращая любые ленивые ошибки загрузки.

Если вы включите ведение журнала до DEBUG в org.springframework.transaction и org.springframework.orm.hibernate3 (или hibernate4, если вы находитесь в Hibernate 4) регистраторах, особенно в классе HibernateTransactionManager и org.springframework.transaction.support.AbstractPlatformTransactionManager, вы должны увидеть точно в каких точках в потоке кода Spring принимается решение о необходимости открытия и закрытия сеанса Hibernate. Журналы также должны показать, почему сеанс или транзакция открываются/закрываются в каждой точке.

Ответ 2

Я не совсем уверен, почему это происходит, но моя теория такова.

Когда вы переходите от плеерования AspectJ к прокси CGLIB, аннотации @Transactional, помещенные в методы, вызываемые из того же объекта, прекращают действовать. Это означает, что код внутри этих методов будет выполняться без транзакций (если у вас нет другого метода @Transacional в стеке вызовов, где @Transacional действительно вступает в силу).

Javadoc для Propagation.SUPPORTS говорит:

Примечание. Для менеджеров транзакций с синхронизацией транзакций PROPAGATION_SUPPORTS немного отличается от транзакции вообще, поскольку она определяет область транзакции, к которой будет применяться синхронизация. Как следствие, те же ресурсы (соединение JDBC, сеанс Hibernate и т.д.) будут совместно использоваться для всей указанной области. Обратите внимание, что это зависит от фактической конфигурации синхронизации диспетчера транзакций.

Итак, когда ваш код выполняет транзакцию без транзакций, Hibernate Session, используемый для загрузки объектов, не будет доступен для последующей инициализации ленивых свойств. Когда вы комментируете метод верхнего уровня в вашем стеке кода с помощью @Transactional(propagation = Propagation.SUPPORTS), Hibernate Session будет доступен до тех пор, пока вы не оставите этот метод.