Spring3 @Transactional @Scheduled не привязан к БД?

Это мой первый раз, когда я пытаюсь использовать Spring3 @Scheduled , но обнаружил, что не могу зафиксировать в БД. Это мой код:

@Service
public class ServiceImpl implements Service , Serializable
{
  @Inject 
  private Dao dao;

  @Override
  @Scheduled(cron="0 0 * * * ?") 
  @Transactional(rollbackFor=Exception.class)
  public void hourly()
  {
    // get xxx from dao , modify it
    dao.update(xxx);
  }
}

Я думаю, что это должно работать, я вижу, что он запускается ежечасно и загружает xxx из БД, но данные не фиксируются в БД.

В весеннем xml было tx:annotation-driven:

<bean id="entityManagerFactoryApp" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitName" value="myapp"/>
</bean>
<bean id="transactionManagerApp" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactoryApp" />
</bean>

<tx:annotation-driven transaction-manager="transactionManagerApp" />

Может кто-нибудь сказать мне, что я пропустил здесь?

У меня есть одно "грязное" решение:

@Service
public class ServiceImpl implements Service , Serializable
{
  @Inject 
  private Dao dao;

  @Inject
  @Qualifier("transactionManagerApp")
  private PlatformTransactionManager txMgrApp;

  @Override
  @Scheduled(cron="0 0 * * * ?")
  @Transactional(rollbackFor=Exception.class)
  public void hourly()
  {
    final TransactionTemplate txTemplateApp = new TransactionTemplate(txMgrApp);
    txTemplateApp.execute(new TransactionCallbackWithoutResult()
    {
      @Override
      protected void doInTransactionWithoutResult(TransactionStatus status)
      {
        //get xxx from dao
        dao.update(xxx);
      }
    });
  }
}

Здесь он отлично работает , но он настолько избыточен , что затрудняет чтение кода. Интересно, почему TransactionManager не внедряется (и не открывается) в предыдущих фрагментах кода?

Большое спасибо !


person smallufo    schedule 26.03.2011    source источник
comment
Немного не по теме, но rollbackFor для @Transactional не нужен. Откат всегда выполняется неявно для исключений во время выполнения.   -  person Piotr Findeisen    schedule 26.03.2011
comment
Я попробовал ваше грязное решение, но оно не работает. есть идеи, почему?   -  person Dejell    schedule 07.05.2014


Ответы (4)


Вы, вероятно, поняли это или пошли дальше (я надеюсь на это), но для пользы других:

Аннотация @Transactional указывает Spring обернуть исходный bean-компонент ServiceImpl динамическим прокси-сервером, который также реализует 'Service' (по умолчанию Spring проксирует интерфейс, а не реализацию). Этот прокси будет прозрачно обрабатывать создание и фиксацию/откат транзакции при вызове hourly() на прокси. Однако, если вы вызовете hourly() непосредственно в своей реализации (что и происходит выше), прокси будет обойден, поэтому транзакции не будет.

http://blog.springsource.org/2012/05/23/understanding-proxy-usage-in-spring/

Решение либо

  1. Разграничьте транзакцию программно, как вы это делаете в своем «грязном» решении (в этом случае вам не нужны аннотации).
  2. Убедитесь, что ваш метод @Scheduled вызывает dao.update(xxx); через интерфейс службы, а не непосредственно в вашей реализации (таким образом, проходя через прокси-сервер). В основном вам нужно переместить метод @Scheduled в другой компонент.

Я надеюсь, что это достаточно ясно!

person Barry Pitman    schedule 18.10.2012
comment
так что относительно вашего второго решения - что вы имеете в виду? - person Dejell; 07.05.2014
comment
Сделал то, что говорит второе решение. Методы @@Transactional перенесены в другой bean-компонент, который был внедрен в bean-компонент @@Scheduled, и транзакции теперь активны! В противном случае я продолжал получать javax.persistence.TransactionRequiredException: нет доступного транзакционного EntityManager - person agelbess; 06.11.2014

Когда вы используете поддержку на основе аннотаций, она работает только с классами, созданными в этом контексте. Могу поспорить, что ServiceImpl не создается в том же контексте, что и ваш менеджер транзакций (ни напрямую, ни путем сканирования аннотаций).

person Andrew White    schedule 26.03.2011
comment
Спасибо, но как «принудительно» создать ServiceImpl в том же контексте, что и мой transactionManager? - person smallufo; 26.03.2011
comment
Либо создайте bean-компонент с этим классом явно, либо убедитесь, что ваш тег сканирования компонентов находится в той же конфигурации или родительской конфигурации вашего менеджера транзакций. - person Andrew White; 26.03.2011
comment
Привет, можно поконкретнее? Я не хочу вручную создавать serviceBean в файле XML. Но мне не ясно, что тег компонентного сканирования находится в той же конфигурации или родительском элементе txManager? Действительно, context:component-scan и ‹tx:annotation-driven transaction-manager=transactionManagerApp /› находятся в одной конфигурации! Но почему это не работает? - person smallufo; 27.03.2011
comment
Теперь вы на что-то; поскольку вы определяете сканирование компонентов и управление аннотациями в одном и том же файле конфигурации, я в недоумении. Я собираюсь дать вам +1 и надеюсь, что кто-то сможет лучше моего ответа. - person Andrew White; 27.03.2011

У меня была та же проблема, и, потратив на это время, я понял, что получил исключение после вызова dao.update() в каком-то несвязанном коде, который не проверял нулевое значение, поэтому он просто сломал транзакцию. Печать stackTrace не производилась, потому что Spring хорошо обработал ее (какой-то блок catch). Я потратил некоторое время на это. Итак, просто убедитесь, что ваш метод транзакции завершается до конца. Надеюсь, это поможет кому-то.

Йоси Лев

person ylev    schedule 26.04.2015

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

@Autowired 
private BeanFactory beanFactory;
  
@Scheduled(cron = "0 0 6 * * *")
@Transactional
public void deleteOldSystemMessages() {
    if (TransactionSynchronizationManager.isActualTransactionActive()) {
        System.out.println("the transaction is active");
    } else {
        beanFactory.getBean(getClass()).deleteOldSystemMessages();
    }
}

Это работает для всех аннотаций, которые вы хотите вызвать. Обратите внимание, что распространение транзакции должно быть НЕОБХОДИМО, что по умолчанию, если требуется, поскольку оно вызывает себя, этот способ запуска транзакций лучше запускать другие аннотированные методы, чем запускать себя....

person Kevin Guanche Darias    schedule 28.12.2020