Spring3的@Transactional @Scheduled未提交到数据库?

9

这是我第一次尝试使用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,但数据没有提交到数据库。

在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);
      }
    });
  }
}

这里代码运行良好,但是过于冗余,使得代码难以阅读。我想知道为什么之前的代码片段中TransactionManager没有被注入(和打开)?

非常感谢!


有点跑题,但是你在@Transactional中不需要使用rollbackFor。对于运行时异常,回滚总是隐式完成的。 - Piotr Findeisen
我尝试了你的不太优雅的解决方案,但它并没有起作用。你有什么想法为什么会这样吗? - Dejell
3个回答

20
你可能已经明白了或者放弃了(我希望如此),但是为了其他人的好处: @Transactional 注释告诉 Spring 包装原始的 ServiceImpl bean,使用一个动态代理,该代理还实现了'Service'接口 (默认情况下,Spring 代理的是接口,而不是实现)。当你在代理上调用 hourly() 时,这个代理将透明地处理事务的创建和提交/回滚。然而,如果你在你的实现上直接调用 hourly()(就像上面发生的那样),代理会被绕过,因此没有事务。

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

解决方案是要么:
  1. 像在您的“脏”解决方案中一样,在程序中标记交易(在这种情况下,您不需要注释)。
  2. 确保您的@Scheduled方法通过Service接口调用dao.update(xxx);,而不是直接调用您的实现(从而通过代理)。基本上您需要将@Scheduled方法移到另一个bean中。
希望这足够清楚!

关于你的第二个解决方案 - 你是什么意思? - Dejell
1
按照第二个解决方案所述进行操作。将@@Transactional方法移动到另一个被注入到@@Scheduled bean中的bean中,事务现在已经生效!否则,我一直收到“javax.persistence.TransactionRequiredException: No transactional EntityManager available”错误。 - agelbess

0

我曾经遇到过同样的问题,花了一些时间后发现,在某个不相关的代码中,dao.update()调用后出现了异常,而该代码没有检查null值,因此导致事务失败。 由于Spring已经很好地处理了这个异常(在某个catch块中),所以没有打印出stackTrace。 我花了一段时间来解决这个问题。 所以,请确保您的事务方法能够顺利完成。 希望这能帮助到某些人。

Yosi Lev


0

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


谢谢,但是如何“强制”ServiceImpl在与我的transactionManager相同的上下文中创建? - smallufo
要么显式地使用该类创建一个bean,要么确保您的component-scan标签与事务管理器位于同一配置或父配置中。 - Andrew White
你好,能否更具体一些?我不想在XML文件中手动创建serviceBean。但是我不清楚“component-scan标签是否在txManager的同一配置或父级中”?实际上,“context:component-scan”和<tx:annotation-driven transaction-manager="transactionManagerApp" />在同一个配置文件中!但为什么它不起作用呢? - smallufo
现在你正在做正确的事情;由于您在同一配置文件中定义了component-scan和annotation-driven,我感到困惑。我会给您一个“+1”,希望有人可以超过我的答案。 - Andrew White

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接