Spring3的@Transactional @Scheduled没有承诺DB?

时间:2011-03-26 17:12:10

标签: spring dependency-injection scheduling transactional spring-3

这是我第一次尝试Spring3的@Scheduled,但发现我无法承诺DB。这是我的代码:

@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,但数据不会提交给数据库。

spring的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);
      }
    });
  }
}

这里工作正常,但它太多了,使代码更难阅读。 我想知道为什么没有在前面的代码片段中注入(和打开)?

非常感谢!

4 个答案:

答案 0 :(得分:17)

你可能已经想到这个或继续前进(我希望如此),但是为了别人的利益:

@Transactional注释告诉Spring使用动态代理包装原始ServiceImpl bean,该代理也实现'Service'(默认情况下,Spring代理接口,而不是实现)。当您在代理上调用hourly() 时,此代理将透明地处理交易的创建和提交/回滚。但是,如果直接在您的实现上调用hourly()(这是上面发生的事情),则会绕过代理,因此没有事务。

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

解决方案是

  1. 在您的“脏”解决方案中以编程方式划分事务(在这种情况下,您不需要注释)。
  2. 确保您的@Scheduled方法通过服务接口调用dao.update(xxx);,而不是直接调用您的实现(从而通过代理)。基本上,您需要将@Scheduled方法移动到另一个bean。
  3. 我希望这很清楚!

答案 1 :(得分:0)

使用注释驱动支持时,它仅适用于在该上下文中创建的类。我敢打赌,ServiceImpl不是在与事务管理器相同的上下文中创建的(直接或通过注释扫描)。

答案 2 :(得分:0)

我遇到了同样的问题,花了很多时间后,我意识到在一些没有检查空值的无关代码中调用dao.update()之后我得到了一个异常 - 所以它只是破坏了事务。 没有stackTrace打印,因为弹簧已经很好地处理了(一些捕获块)。 我花了一段时间。 所以 - 只需验证您的事务方法是否完成直到结束。 希望它会帮助别人。

Yosi Lev

答案 3 :(得分:0)

这里没有找到我经常使用的解决方案,所以在这里补充一下,我觉得更简洁,因为执行是放在同一个类中

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

这适用于您要调用的所有注释。请注意事务传播必须是 REQUIRED,这是默认的,如果是必需的,因为它在调用自己,这种运行事务的方式比运行其他注释方法更好......