Java 休眠多线程防止多个save(),需要JTA吗?
我正在为我的web应用程序使用hibernate会话/请求模型。我的jdbc事务从每个web请求的开头开始,并在最后提交 //非托管环境习惯用法Java 休眠多线程防止多个save(),需要JTA吗?,java,hibernate,transactions,synchronization,jta,Java,Hibernate,Transactions,Synchronization,Jta,我正在为我的web应用程序使用hibernate会话/请求模型。我的jdbc事务从每个web请求的开头开始,并在最后提交 //非托管环境习惯用法 Session sess = factory.openSession(); Transaction tx = null; try { tx = sess.beginTransaction(); // do some work ... tx.commit(); } catch (RuntimeException e)
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
我面临的问题是,我正在基于几个参数测试实体A的存在性,并且仅当它不存在时才执行插入
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
MyEntityADAO.save(entity);
}
}
问题是同步没有帮助,因为当当前运行的线程退出该方法并释放锁时,对MyEntityADAO.save的调用实际上不会写入数据库,对数据库的写入发生在提交事务之后,这通常是我的应用程序所需要的,除了少数情况。上面的代码导致在多线程环境中使用相同的参数保存多个记录
我已尝试在其自己的新会话和事务中执行保存代码:
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
Session session = HibernateUtil.createSession();
MyEntityADAO.save(entity);
Transaction t = session.beginTransaction();
}
}
在某些情况下,上述情况会导致2个打开的会话使用hibernate加载同一集合时出现问题
我是否应该将每个DAO调用都包含在自己的事务中,并使用JTA进行事务传播?有没有办法避免JTA?在调用MyEntityADAO.save和call beginTransaction之后立即在主会话上提交与主会话关联的事务,并像现在一样在请求结束时提交该事务,这样可以吗?数据库中数据的一致性不应该因为只执行原子会话的某些部分而受到损害其自身交易的变化。尽管某些同步可能在您的环境中起作用,但如果您需要对应用程序进行集群,或者如果多个应用程序访问数据库,则无法解决问题 您应该做的是在[param1-param2]上的数据库中放置一个唯一约束。如果存在竞争条件,这将导致两个事务中的一个回滚 如果您仍然选择在自己的事务中隔离check/insert代码,因为如果这样做成功而外部事务失败,这不是问题,那么我不认为JTA会成为问题。假设您使用的是EJB或Spring,只需将此方法放在它自己的EJB/bean中,并使用REQUIRES\u新的传播将该方法标记为事务性的 因此,代码如下所示:
// some code
Long id = myBean.checkIfExistOrCreate(param1, param2); // this methos call starts a new transaction
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
如果无法同步checkIfExistOrCreate,请尝试调用它,捕获它可能引发的任何异常,然后重试调用它:
Long id = null;
try {
id = myBean.checkIfExistOrCreate(param1, param2);
}
catch (Exception e) { // a well-defined exception would be better
// the transaction roled back: retry
id = myBean.checkIfExistOrCreate(param1, param2);
}
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
该解决方案适用于我和我的特定应用程序要求,试图避免JTA和嵌套事务: 使用ManagedSessionContext,因为org.hibernate.context.ThreadLocalSessionContext将关闭并为每个事务创建新会话。如果在为一个请求创建多个事务时在多个打开的会话中加载具有关联集合的实体,则会遇到与这些实体相关联的问题 我打开一个hibernate会话,并将其绑定到web请求开头的上下文 任何在insert之前需要测试其存在性的服务层方法都被标记为已同步,全局事务与insert语句一起提交,并启动一个新事务 在请求结束时,提交绑定到会话的事务 公共同步myMethodparam1,param2{
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
MyEntityADAO.save(entity);
HibernateUtil.getCurrentSession().getTransaction().commit();
HibernateUtil.getCurrentSession().getTransaction().begin();
}
}
我知道这很难看,而且不会对每个场景中的每个人都有效,但在对事务管理、隔离级别、锁定和版本控制进行了非常深入的搜索之后,我发现这是唯一对我有效的解决方案。我没有使用Spring,也没有使用JavaEE容器,使用Tomcat6。谢谢您的输入。我确实设置了约束,但在我的dao/服务层中没有捕获任何hibernate/jdbc异常。看起来是个好主意。谢谢。我正在捕获ConstraintViolationException并尝试获取catch块中的现有实体,并获取空值。是否有一些中间阶段,当事务在底层db mysql中标记为已完成,并且表约束已更新,但数据库尚未从表中返回新行时?我已经完成了尝试在保存后在同步方法/块内刷新会话..不太频繁地获取异常,但仍然没有解决问题刷新没有帮助。事务通常彼此隔离运行,并且使用传统的默认隔离级别READ_COMMITTED,在A提交之前,事务B将不会看到事务A在数据库中插入的任何内容。如果要使解决方案正常工作,您仍然需要在退出同步块之前提交正在执行插入的事务,则catch块内是否存在的测试将导致空值。