EntityManager真的是线程安全的吗?

时间:2014-07-09 00:58:08

标签: java multithreading jpa glassfish-4

我在这里谈论基本用法:

@Stateless
public class BookServiceBean implements BookService {
  @PersistenceContext EntityManager em;
  public void create(Book book) { this.em.persist(book);}
} 

通过Google搜索上述问题,StackOverflow说yes, but no - 接受的答案是肯定的,但是后续行动是否定的; Spring.io说both yes and no,而似乎是Java EE专家的Adam Bien给出了unqualified yes

我对一个简单的预定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;之类的代码并自行管理EntityManager,但我想利用@PersistenceContext给我的所有自动交易内容。

3 个答案:

答案 0 :(得分:10)

不,EntityManager不是线程安全的。 Adam Bien虽然也是正确的。你只是没有正确地看待这个问题。他回答的问题不是,如果EntityManager是线程安全的,他说明在无状态会话bean中使用容器管理的EntityManger是安全的。然后他解释了允许容器发挥其魔力的规范的推理和措辞 - "每个实例只能看到序列化的方法调用序列"。这允许容器注入每个方法调用具有不同的EntityManager上下文,类似于每个调用如何绑定到它们自己的事务和隔离的资源。

注入实际上只是注入一个EntityManager代理,它使容器控制下面的JPA EntityManagers的生命周期,允许它与线程和事务绑定。

因此,EntityManager不是线程安全的,但注入EntityManager代理的容器必须在无状态会话bean中安全使用。

答案 1 :(得分:3)

对于应用程序管理的实体管理器

EntityManager实例不是线程安全的。

EntityManagerFactory实例是线程安全的。

  

当应用程序需要访问持久性上下文时,使用应用程序管理的实体管理器,该持久性上下文不会随特定持久性单元中的EntityManager实例跨JTA事务传播。在这种情况下,每个EntityManager都会创建一个新的隔离的持久性上下文。 EntityManager及其关联的持久性上下文由应用程序显式创建和销毁。当EntityManager实例不是线程安全的,因此无法直接注入EntityManager实例时,也会使用它们。 EntityManagerFactory实例是线程安全的。

有关更多详细信息,请访问here

对于容器管理的实体管理器

要求EntityManager在无状态会话Bean中安全使用。

答案 2 :(得分:1)

尽管@Chris' answer帮助我了解了更多关于EntityManager的信息(因此是勾号),但我找出了真正的问题 - ManagedScheduledExecutorService不一定是auto-kill its threads on application termination - 这似乎是真的Glassfish 4.0也。所以,虽然我的底层应用程序已经终止,但线程仍然有来自死应用程序EntityManager的{​​{1}}(我认为),所以当计时器打勾时,它会突出我上面描述的错误

我通过获取EntityManagerFactory并在ScheduledFuture中调用future.cancel(false)来解决了问题。


此外,似乎Timer Tick bean和contextDestroyed()没有混合,所以我不得不这样做:

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; } } 返回的值所做的更改的自动持久性,据我所知,但我无法解释它!