EntityManager真的是线程安全的吗?
我在说这里的基本用法:
@Stateless public class BookServiceBean implements BookService { @PersistenceContext EntityManager em; public void create(Book book) { this.em.persist(book);} }
谷歌上面的问题,StackOverflow说是,但没有 – 接受的答案是肯定的,但后续是否是; Spring.io说是和否 ,而且似乎是Java EE专家的Adam Bien给出了不合格的肯定 。
我对一个简单的预定bean的经验表明答案是否定的:
@Stateless public class TimerTick implements TimerTickAbs, Runnable { @PersistenceContext private EntityManager entityManager; @Override public void run() { Query q = entityManager.createQuery("SELECT blah..."); } @Override public Runnable runner() { return this; } }
抽象界面:
@Local public interface TimerTickAbs { public Runnable runner(); }
开始于:
@Resource ManagedScheduledExecutorService managedExecutorService; @EJB TimerTick myRunner; public void startup() { managedExecutorService.scheduleAtFixedRate(myRunner.runner(), 3, 40, TimeUnit.SECONDS); }
如果我打印出Thread.currentThread().getId()
,即使我仍然在调用之间的同一个线程,我得到:
SEVERE:java.lang.IllegalStateException:尝试在已关闭的EntityManager上执行操作
我知道我可以像@PersistenceUnit private EntityManagerFactory emf;
那样做代码@PersistenceUnit private EntityManagerFactory emf;
并自己管理EntityManager
,但我想利用@PersistenceContext
给我的所有自动交易内容。
不,EntityManager不是线程安全的。 Adam Bien虽然也是正确的。 你只是没有正确地看待这个问题。 他回答的问题不在于EntityManager是否是线程安全的,他说在无状态会话bean中使用容器管理的EntityManger是安全的。 然后他解释了允许容器发挥其魔力的规范的推理和措辞 – “每个实例只能看到序列化的方法调用序列”。 这允许容器注入每个方法调用具有不同的EntityManager上下文,类似于每个调用如何绑定到它们自己的事务和隔离的资源。
注入实际上只是注入一个EntityManager代理,它允许容器控制下面的JPA EntityManagers的生命周期,允许它绑定到线程和事务。
因此,EntityManager不是线程安全的,但注入EntityManager代理的容器必须在无状态会话bean中安全使用。
尽管@Chris的回答帮助我理解了更多关于EntityManager的信息(因此嘀嗒),但我解决了真正的问题 – ManagedScheduledExecutorService
并不一定会在应用程序终止时自动终止其线程 – 这对于Glassfish 4.0来说也是如此。 所以,虽然我的底层应用程序已经终止,但是线程仍然有来自死应用程序的EntityManagerFactory
的EntityManagerFactory
(我认为),所以当计时器打勾时它会突出我上面描述的错误。
我通过获取ScheduledFuture
并在我的contextDestroyed()
调用future.cancel(false)
来解决问题。
此外,似乎Timer Tick bean和EntityManager
不混合,所以我必须这样做:
@Stateless public class TimerTick implements TimerTickAbs, Runnable { @EJB private RealTimerTick realTick; @Override public void run() { realTick.run(); } @Override public Runnable runner() { return this; } }
和:
@Stateless public class RealTimerTick implements TimerTickAbs, Runnable { @PersistenceContext private EntityManager entityManager; @Override public void run() { Query q = entityManager.createQuery("SELECT blah..."); } @Override public Runnable runner() { return this; } }
这个现在可以很好地工作,包括对Query
返回的值所做的更改的自动持久性,据我所知,但我无法解释它!