在 servlet
中有以下代码片段@PeristenceContext
private EntityManager entityManager;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,
IOException {
runTransaction();
}
@Transactional
public void runTransaction() {
entityManager.merge(entityA);
entityManager.remove(entityC);
}
entityManager
方法是在容器管理的事务边界内部调用的=>问题:在runTransaction()
方法内部调用doPost
有什么问题吗?尤其是runTransaction()
的调用是线程安全的吗?如果这种方法有问题,如何解决?
在您的情况下,它可能是线程安全的(因为它依赖于请求),但它肯定不是事务性的。仅当存在 CDI 规范
第 2.6.2 项定义的“业务方法调用”时,
@Transactional
注释才会激活事务边界。在你的例子中,servlet的doPost正在调用它自己的类中的方法,所以它不会被拦截。
为了能够启动事务,您应该将数据库代码(entitymanager 注入和 runTransaction 方法)移动到另一个 CDI bean(您可以选择 @RequestScoped
来保证线程安全)并从 servlet 调用该 bean。所以:
@RequestScoped
public class DataManager {
@PersistenceContext
private EntityManager em;
@Transactional
public void runTransaction(...) {
// Do your thing here
}
}
@WebServlet("/save")
public class YourServlet extends HttpServlet {
@Inject
private DataManager dataManager;
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// Gather data to be saved
dataManager.runTransaction(/*arguments*/); // transaction will be started just before this method is actually called and will end before returning the result to this method
// return response using resp
}
}
由于 dataManager
是一个
@RequestScoped
CDI 代理,每次调用它时,都会调用正确的请求上下文 bean,使其具有请求相关性和线程安全性。请注意,bean 之间的数据共享可能会干扰执行的线程安全性,因此请明智地选择传递给
runTransaction
方法的内容。