[英]Jersey, Guice and Hibernate - EntityManager thread safety
我已经在我的应用程序中以相同的方式使用了本教程: http : //www.benmccann.com/hibernate-with-jpa-annotations-and-guice/
我的应用程序是JAX-RS Web服务,它将接收许多并发请求并进行数据库更新。
GenericDAOImpl.java实现:
public class GenericDAOImpl<T> implements GenericDAO<T> {
@Inject
protected EntityManager entityManager;
private Class<T> type;
public GenericDAOImpl(){}
public GenericDAOImpl(Class<T> 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<EntityManager> entityManagerProvider;
要么
@Inject
protected EntityManagerFactory entityManagerProvider;
并针对每个请求:
EntityManager entityManager = entityManagerProvider.get()
但是然后我得到:
org.hibernate.PersistentObjectException: detached entity passed to persist
什么是实现Guice + Hibernate EntityManager注入/线程安全的通用DAO类的正确方法?
更新
来自http://www.benmccann.com/hibernate-with-jpa-annotations-and-guice/的 Andrew Rayner评论
“逻辑还不是真正可以投入生产的-至少在Web应用程序中使用了。
Hibernates连接池是非常基本的,并且尚未准备就绪,建议使用数据源池,例如c3p0。
EntityManager不应被重用-它应按事务/请求创建。 很可能污染后续请求。
如果出现问题,也不会进行事务回滚。
一种有趣的方法–但是对于Web应用来说,使用Guices自己的Persist扩展模块来管理EntityMananger实例和事务的生命周期会更加安全。”
首先,您使用哪种EntityManager? 查看您的代码,我认为这种类型是应用程序管理的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());
}
希望有帮助
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.