【发布时间】:2017-04-03 20:12:55
【问题描述】:
我在 Spring web @Controller 中有一个 @transactional 方法,它调用了一个 @async 方法。 @transactional 方法将实体持久化到 Spring-wired javax.persistence.entitymanager 并调用 @async 函数将一些衍生数据保存到分析服务器。 @async 函数依赖于来自 NativeQuery 的结果,该结果来自它自己的 @PersistenceContext。
问题是@async 函数内部的本机查询在第一个事务刷新之前运行,这意味着实体尚未刷新到数据库。
确保@async 函数仅在完成当前打开的事务之后运行的最佳方法是什么?我尝试将@async 函数包装在它自己的@transactional(propagation = Propagation.REQUIRES_NEW) 注释中,并在调用异步方法之前在第一个事务中手动调用em.flush(),但都没有改变结果。
这是我的代码的简化版本(编辑-添加了一些额外的细节):
网络控制器
@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;
}
}
异步服务
@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?)告诉异步函数只在当前事务刷新后运行?
【问题讨论】:
-
如果不提交数据,另一个事务无法看到数据。如果您希望它在另一个方法之后运行,那么在另一个方法之后运行它。创建一个(非事务性)服务,该服务首先调用该方法来持久化内容,然后调用另一个方法。
-
你是对的——这可能是我提供的简化示例的最佳解决方案。不幸的是,异步方法是从许多不同的事务性@RequestMapping 方法中调用的,这需要一个很大的重构来包装它们。传递给异步方法的数据取决于调用的方法,因此将其包装在 servlet 过滤器中会很棘手。
-
呃? Servlet 过滤器?那是从哪里来的?另外,为什么异步调用必须更改...您想结合此处的内容更改调用它的位置。显然,由于过于简化,您提出的问题不会给您想要/需要的答案。
标签: java spring hibernate transactions