Jersey,Guice和Hibernate – EntityManager线程安全

我在我的应用程序中以相同的方式使用了本教程: http : //www.benmccann.com/hibernate-with-jpa-annotations-and-guice/

我的应用程序是JAX-RS Web服务,它将接收许多并发请求并对数据库进行更新。

GenericDAOImpl.java实现:

public class GenericDAOImpl implements GenericDAO { @Inject protected EntityManager entityManager; private Class type; public GenericDAOImpl(){} public GenericDAOImpl(Class type) { this.type = type; } @Override public void save(T entity) { entityManager.getTransaction().begin(); entityManager.persist(entity); entityManager.getTransaction().commit(); } 

}

如果2个并发线程试图保存实体,我得到

 java.lang.IllegalStateException: Transaction already active 

如果我评论交易,保存效果很好。

我试过用

 @Inject protected Provider entityManagerProvider; 

要么

 @Inject protected EntityManagerFactory entityManagerProvider; 

并为每个请求:

 EntityManager entityManager = entityManagerProvider.get() 

但后来我得到:

 org.hibernate.PersistentObjectException: detached entity passed to persist 

实现Guice + Hibernate EntityManager注入/线程安全通用DAO类的正确方法是什么?

UPDATE

Andrew Rayner的评论来自http://www.benmccann.com/hibernate-with-jpa-annotations-and-guice/

“逻辑并不是真正的生产准备 – 至少如果在网络应用程序中使用。

Hibernates连接池非常基础,并且没有生产就绪 – 建议使用数据源池,例如c3p0。

不应重用EntityManager – 它是为每个事务/请求创建的。 很有可能污染后续请求。

如果出现问题,也没有事务回滚。

一个有趣的方法 – 但是webapps使用Guices自己的Persist扩展模块来管理EntityMananger实例和事务的生命周期会更安全。“

首先,您使用的是哪种EntityManager? 看看你的代码,我认为这是Application-Managed EntityManager。 了解不同类型的EntityManager非常重要。

请参阅: http : //docs.oracle.com/javaee/6/tutorial/doc/bnbqw.html

基于此,您需要创建一个EntityManagerFactory对象,然后创建一个EntityManager对象。

基本示例:

 private static EntityManagerFactory emf; EntityManager em = null; public static EntityManagerFactory getEmf(){ if(emf == null){ emf = Persistence.createEntityManagerFactory("nameOfYourPersistenceUnit"); } return emf; } em = getEmf().createEntityManager(); em.getTransaction().begin(); em.persist(entity); em.getTransaction().commit(); em.close(); 

问题是我的端点是用@Singleton注释的,所以它在并发调用期间重用了相同的EntityManager。 删除@Singleton后,在并发调用期间,将使用不同的EntityManager对象。 如果端点调用是后续的,则可能使用先前/旧的EntityManager。

高度简化的例子:

 @Path("/v1/items") public class ItemsService { @Inject private EntityManager entityManager; @POST @Path("/{id}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public void saveItem(){ entityManager.getTransaction().begin(); entityManager.persist(new Item()); entityManager.getTransaction().commit(); } } 

如果它说交易已经打开,那意味着它被另一个进程打开而没有关闭……

我建议使用@Transactionl而不是Writing:

 em.getTransaction().begin(); 

 em.getTransaction().commit(); em.close(); 

那将为你管理的事情……

所以对你来说就是这样:

 @Transactionl @POST @Path("/{id}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public void saveItem(){ entityManager.persist(new Item()); } 

希望有所帮助