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

时间:2017-01-17 03:39:17

标签: java spring spring-boot

我有如下的交易,

@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时,

    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            changeJobStatus(jobId);
        }
    };
    timer.schedule(task, waitTime);

会有例外:

javax.persistence.TransactionRequiredException: no transaction is in progress

为什么会这样?如果有办法在TimerTask中调用事务?

2 个答案:

答案 0 :(得分:1)

changeJobStatus()的调用实际上直接指向你的bean(自调用),因此在bean之间调用时不受通常的Spring代理约束。因此,没有任何交易开始。

请参阅:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations搜索&#34;自我调用&#34;。

可能有几种可能的方法来解决这个问题:

  1. 你可以自动连接你自己的bean的引用,这将通过代理实现,并通过它调用;
  2. 你可以使用mode =&#34; aspectj&#34;,它执行字节码编织(增强)。
  3. 您可以通过PlatformTransactionManager手动控制交易;
  4. 我的方法取决于这是孤立的还是常见的情况。如果常见,我会调查&#34; aspectj&#34;模式;但我可能希望它是一个异常值,我可以坚持标准的Spring&#34;代理&#34;模式。

答案 1 :(得分:0)

这是由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();
}

以下是我在TimerTask中使用的方法,

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,可以在没有任何注释的情况下完成事务。