在持久化实体时需要事务来执行此操作使用事务或扩展持久化上下文

问题描述

我正在尝试开发一个以特定时间间隔运行并执行一些数据库修改的类。

我设法以特定时间间隔运行的代码,从数据库中检索记录,但是当我想提交对数据库的更改时,我收到以下错误

WFLYEE0110: Failed to run scheduled task: javax.persistence.TransactionrequiredException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)

是否允许@ApplicationScoped 创建交易?

谢谢!

@ApplicationScoped
@ActivateRequestContext
public class TaskRunner {

 @PersistenceContext(type = PersistenceContextType.EXTENDED)
  EntityManager em; 

  @Resource private Managedscheduledexecutorservice scheduler;

  private ScheduledFuture<?> TaskRunnerScheduler;

  private boolean initialized = false;

  private void init(@Observes @Initialized(ApplicationScoped.class) Object init) {

    if (initialized) return;

   
    initialized = true;
    try {
      // Execute at startup
      TaskRunner = scheduler.schedule(this::runSchedule,getSchedule());
    } catch (Throwable throwable) {

    }
  }

 @Transactional
  private void runSchedule() {
//retrieve db records
//make changes and commit
//sample
//em.persist(someEntity)
  }

  private Trigger getSchedule() {
    return new Trigger() {
      @Override
      public Date getNextRunTime(LastExecution lastExecutionInfo,Date taskScheduledTime) {
        return Date.from(
            zoneddatetime.Now().withSecond(0).withNano(0).plusHours("4").toInstant());
      }

      @Override
      public boolean skipRun(LastExecution lastExecutionInfo,Date scheduledRunTime) 
       {return false;}};
  }

}

解决方法

事务是通过拦截器开始的。 当你从 bean 内部调用 bean 的方法时,方法调用不会被拦截,也不会启动任何事务。

你需要另一个 Bean 并坚持在那里

@RequestScoped
@Transactional(value = TxType.REQUIRES_NEW)
public class SomeOtherBean{

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    EntityManager em;
    
    public void doSomething(){
         //retrieve db records
         //make changes and commit
         //sample
         //em.persist(someEntity)
    }
}

然后你可以在你的 TaskRunner 中注入那个 bean

@ApplicationScoped
@ActivateRequestContext
public class TaskRunner {

    @Inject
    SomeOtherBean someBean;

    ...

    private void runSchedule() {
        someBean.doSomething()
    }

}