Spring 4:在调用@transactional事务之前运行的@async nativeQuery提交

时间:2017-04-03 20:12:56

标签: java spring hibernate transactions

我在Spring网络@transactional中使用@Controller方法调用@async方法。 @transactional方法将实体持久保存到Spring-wired javax.persistence.entitymanager并调用@async函数将某些衍生数据保存到分析服务器。 @async函数取决于NativeQuery来自其@PersistenceContext的结果。

问题是@async函数内的本机查询在第一个事务刷新之前正在运行,这意味着该实体尚未刷新到数据库。

在完成当前打开的交易后,确保@async功能仅运行的最佳方法是什么?我尝试将@async函数包装在其自己的@transactional(propagation = Propagation.REQUIRES_NEW)注释中,并在调用异步方法之前在第一个事务中手动调用em.flush(),但都没有改变结果。 / p>

这是我的代码的简化版本(编辑 - 添加了一些额外的细节):

WebController

@Controller
class WebController {
    @PersistenceContext
    EntityManager em;

    @Autowired
    AsyncService service;

    @RequestMapping("/createObject")
    @Transactional
    @ResponseBody
    public Obj createObj(Obj obj){
        em.persist(obj);

        //Run the time consuming async task for the newly-persisted entity
        service.runAsyncMethod(obj.getId());

        return obj;
    }

}

AsyncService

@Service
@EnableAsync
class AsyncService {

     @PersistenceContext
     EntityManager em;

     @Async
     public void runMethod(Long itemId) {
         //Run native query
         Query query = em.createNativeQuery("select name from obj where id = :objId")
         query.setParameter("objId", itemId);


         List results = query.getResultsList()
         //`results` is empty
     }
}

此外:如果我在持久性配置中打开hibernate.show_sql,则打印的SQL语句看起来像,就像它们的顺序正确一样:

    Hibernate: insert into content (id, name) values (?, ?)
    Hibernate: select name from obj where id = ?

最后,如果我将service.runAsyncMethod()电话打包在java.util.TimerTask并安排延迟1秒,那么 会按预期工作。

从我的应用程序中的多个不同的Web端点调用asyncMethod。

有没有办法(可能通过AOP?)告诉异步函数只在当前事务刷新后运行?

1 个答案:

答案 0 :(得分:1)

如果在事务提交(flush complete)之后调用异步方法,我们可以实现这一点。这可以通过在TransactionSynchronizationAdapter(Spring提供的抽象类)的overCommit()中调用async方法并在TransactionSynchronizationManager中注册此TransactionSynchronizationAdapter对象(这有助于事务同步)来实现。

前:

@Controller
class WebController {
   @PersistenceContext
   EntityManager em;

   @Autowired
   AsyncService service;

   @Transactional
   public Obj createObj(Obj obj){
        em.persist(obj);

        // Call the Async method only after transaction commit.
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {

           // Override the afterCommit which need to be executed after transaction commit
           public void afterCommit() { 
              //Run the time consuming async task for the newly-persisted entity
              service.runAsyncMethod(obj.getId());
           }
         }

        return obj;
      }

    }