这是我第一次尝试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);
}
});
}
}
这里工作正常,但它太多了,使代码更难阅读。 我想知道为什么没有在前面的代码片段中注入(和打开)?
非常感谢!
答案 0 :(得分:17)
你可能已经想到这个或继续前进(我希望如此),但是为了别人的利益:
@Transactional
注释告诉Spring使用动态代理包装原始ServiceImpl
bean,该代理也实现'Service
'(默认情况下,Spring代理接口,而不是实现)。当您在代理上调用hourly()
时,此代理将透明地处理交易的创建和提交/回滚。但是,如果直接在您的实现上调用hourly()
(这是上面发生的事情),则会绕过代理,因此没有事务。
http://blog.springsource.org/2012/05/23/understanding-proxy-usage-in-spring/
解决方案是
dao.update(xxx);
,而不是直接调用您的实现(从而通过代理)。基本上,您需要将@Scheduled
方法移动到另一个bean。我希望这很清楚!
答案 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,这是默认的,如果是必需的,因为它在调用自己,这种运行事务的方式比运行其他注释方法更好......