Spring 启动事务无法在计时器中工作



>我有一个如下所示的交易,

@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 搜索"自调用"。

可能有几种潜在的方法可以解决这个问题:

  1. 您可以自动连接对您自己的 bean 的引用,这将通过代理实现,并通过它调用;
  2. 您可以使用 mode="aspectj",它执行字节码编织(增强)。
  3. 您可以通过平台交易管理器手动控制交易;

我的方法将取决于这是孤立的还是常见的情况。如果常见,我会研究"方面"模式;但我可能希望它是一个异常值,我可以坚持标准的 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,可以在没有任何注释的情况下完成事务。

相关内容

  • 没有找到相关文章

最新更新