>我有一个如下所示的交易,
@Transactional
public void changeJobStatus(Long jobId){
JobEntity jobEntity = jobRepository.findOneForUpdate(jobId);
...
}
而findOneForUpdate是用悲观锁查找数据库,
public interface JobRepository extends CrudRepository<JobEntity, Long>{
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select j from JobEntity j where j.id = :id")
JobEntity findOneForUpdate(@Param("id") Long id);
}
如果我正常调用 changeJobStatus ,这很好用。
但是当调用如下所示的计时器任务时,
TimerTask task = new TimerTask() {
@Override
public void run() {
changeJobStatus(jobId);
}
};
timer.schedule(task, waitTime);
会有一个例外:
javax.persistence.TransactionRequiredException: no transaction is in progress
为什么会这样?如果有办法在计时器任务中调用事务?
对 changeJobStatus()
的调用实际上是直接针对您的 bean 的(自我调用),因此在 bean 之间调用时不受通常的 Spring 代理的约束。因此,没有开始交易。
请参阅:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations 搜索"自调用"。
可能有几种潜在的方法可以解决这个问题:
- 您可以自动连接对您自己的 bean 的引用,这将通过代理实现,并通过它调用;
- 您可以使用 mode="aspectj",它执行字节码编织(增强)。
- 您可以通过平台交易管理器手动控制交易;
我的方法将取决于这是孤立的还是常见的情况。如果常见,我会研究"方面"模式;但我可能希望它是一个异常值,我可以坚持标准的 Spring "代理"模式。
这是由 Spring 的 AOP 限制引起的。正如Thomas所建议的那样,手动控制事务可以解决这个问题,而不是使用@Transactional。这是详细的实现,
我创建了一个简单的交易服务,如下所示,
@Service
public class SimpleTransactionService {
private final TransactionTemplate transactionTemplate;
@Autowired
public SimpleTransactionService(PlatformTransactionManager transactionManager){
transactionTemplate = new TransactionTemplate(transactionManager);
}
public void executeTransaction(ITransactionService task){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
task.transactionExecute();
}
});
}
}
ITransactionService只是一个具有一种方法的简单界面,
public interface ITransactionService {
void transactionExecute();
}
这是我在计时器任务中使用上述方法,
public void addTimerTask(Object param, Long waitTime){
TimerTask task = new TimerTask() {
@Override
public void run() {
simpleTransactionService.executeTransaction(() -> someOperation(param));
}
};
timer.schedule(task, waitTime);
}
someOperation是实际执行的事务。使用简单的事务服务和 lambda,可以在没有任何注释的情况下完成事务。