2012-05-10 33 views
0

这是一个奇怪的情况,我通常不会这样做,但不幸的是我们的系统现在需要这种情况。尝试在拦截器管理的事务中手动提交

的系统
我们正在运行使用OpenSessionInView和TransactionInterceptor并将管理我们的交易春/ Hibernate应用程序。大多数情况下,它的效果很好。但是,最近我们需要产生一些线程来向提供者发出一些并发的HTTP请求。

的问题
我们需要传递到线程有所有我们在我们目前的交易已经更新了数据的实体。问题在于我们在服务层的内部产生了深度的线程,并且很难做出更小的事务来完成这项工作。我们试图原本只是路过实体的线程,只是呼吁:

leadDao.update(lead); 

的问题是,我们不是让生活在两会实体错误。接下来,我们尝试提交原始事务,并在线程完成后立即重新打开。 这就是我在这里列出的。

try { 
     logger.info("------- BEGIN MULTITHREAD PING for leadId:" + lead.getId()); 
     start = new Date(); 
     leadDao.commitTransaction(); 
     List<Future<T>> futures = pool.invokeAll(buyerClientThreads, lead.getAffiliate().getPingTimeout(), TimeUnit.SECONDS); 
     for (int i = 0; i < futures.size(); i++) { 
      Future<T> future = futures.get(i); 
      T leadStatus = null; 
      try { 
       leadStatus = future.get(); 
       if (logger.isDebugEnabled()) 
        logger.debug("Retrieved results from thread buyer" + leadStatus.getLeadBuyer().getName() + " leadId:" + leadStatus.getLead().getId() + " time:" + DateUtils.formatDate(start, "HH:mm:ss")); 
      } catch (CancellationException e) { 
       leadStatus = extractErrorPingLeadStatus(lead, "Timeout - CancellationException", buyerClientThreads.get(i).getBuyerClient().getLeadBuyer(), buyerClientThreads.get(i).getBuyerClient().constructPingLeadStatusInstance()); 
       leadStatus.setTimeout(true); 
       leadStatus.setResponseTime(new Date().getTime() - start.getTime()); 
       logger.debug("We had a ping that didn't make it in time"); 
      } 
      if (leadStatus != null) { 
       completed.add(leadStatus); 
      } 
     } 
    } catch (InterruptedException e) { 
     logger.debug("There was a problem calling the pool of pings", e); 
    } catch (ExecutionException e) { 
     logger.error("There was a problem calling the pool of pings", e); 
    } 
    leadDao.beginNewTransaction(); 

的开始交易看起来是这样的:

public void beginNewTransaction() { 
    if (getCurrentSession().isConnected()) { 
     logger.info("Session is not connected"); 
     getCurrentSession().reconnect(); 
     if (getCurrentSession().isConnected()) { 
      logger.info("Now connected!"); 
     } else { 
      logger.info("STill not connected---------------"); 
     } 
    } else if (getCurrentSession().isOpen()) { 
     logger.info("Session is not open"); 
    } 
    getCurrentSession().beginTransaction(); 
    logger.info("BEGINNING TRANSAACTION - " + getCurrentSession().getTransaction().isActive()); 

} 

的线程使用TransactionTemplates因为我的buyerClient对象不被弹簧(长参与要求)进行管理。 这里是代码:

@SuppressWarnings("unchecked") 
    private T processPing(Lead lead) { 
     Date now = new Date(); 
     if (logger.isDebugEnabled()) { 
      logger.debug("BEGIN PINGING BUYER " + getLeadBuyer().getName() + " for leadId:" + lead.getId() + " time:" + DateUtils.formatDate(now, "HH:mm:ss:Z")); 
     } 
     Object leadStatus = transaction(lead); 
     if (logger.isDebugEnabled()) { 
      logger.debug("PING COMPLETE FOR BUYER " + getLeadBuyer().getName() + " for leadId:" + lead.getId() + " time:" + DateUtils.formatDate(now, "HH:mm:ss:Z")); 
     } 
     return (T) leadStatus; 
    } 

    public T transaction(final Lead incomingLead) { 
     final T pingLeadStatus = this.constructPingLeadStatusInstance(); 
     Lead lead = leadDao.fetchLeadById(incomingLead.getId());  
     T object = transactionTemplate.execute(new TransactionCallback<T>() { 

      @Override 
      public T doInTransaction(TransactionStatus status) { 
       Date startTime = null, endTime = null; 

       logger.info("incomingLead obfid:" + incomingLead.getObfuscatedAffiliateId() + " affiliateId:" + incomingLead.getAffiliate().getId()); 

       T leadStatus = null; 
       if (leadStatus == null) { 
        leadStatus = filterLead(incomingLead); 
       } 
       if (leadStatus == null) { 
        leadStatus = pingLeadStatus; 
        leadStatus.setLead(incomingLead); 
...LOTS OF CODE 
} 
       if (logger.isDebugEnabled()) 
        logger.debug("RETURNING LEADSTATUS FOR BUYER " + getLeadBuyer().getName() + " for leadId:" + incomingLead.getId() + " time:" + DateUtils.formatDate(new Date(), "HH:mm:ss:Z")); 
       return leadStatus; 
      } 
     }); 
     if (logger.isDebugEnabled()) { 
      logger.debug("Transaction complete for buyer:" + getLeadBuyer().getName() + " leadId:" + incomingLead.getId() + " time:" + DateUtils.formatDate(new Date(), "HH:mm:ss:Z")); 
     } 

     return object; 
    } 

然而,当我们开始新的事务,我们得到这个错误:

org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started 
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:660) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 

我的目标 我的目标是能够有充分initalized该实体另一方面还是有人有任何想法,我怎么可以将数据提交到数据库,以便线程可以有一个完全填充的对象。或者,有办法查询完整的对象? 谢谢,我知道这确实涉及。如果我不够清楚,我很抱歉。

我已经试过
同样,Hibernate.initialize() saveWithFlush() 更新(铅)

回答

0

感谢@gkamal的帮助。 适合所有居住在后人的人。我困境的答案是剩下的调用hibernateTemplate而不是getCurrentSession()。我在一年半前做出这个举动,并由于某种原因错过了一些关键的地方。这是产生第二笔交易。之后,我能够使用@gkamal建议并驱逐对象并再次抓取它。

这个帖子让我看着办吧:
http://forum.springsource.org/showthread.php?26782-Illegal-attempt-to-associate-a-collection-with-two-open-sessions

0

我没有按照一切 - 你可以尝试这样一个要解决的是你对这个问题同一个对象与两个会话相关联。

// do this in the main thread to detach the object 
// from the current session 
// if it has associations that also need to be handled the cascade=evict should 
// be specified. Other option is to do flush & clear on the session. 
session.evict(object); 


// pass the object to the other thread 

// in the other thread - use merge 
session.merge(object) 

第二种方法 - 创建对象的深层副本并传递副本。如果你的实体类是可序列化的,那么这很容易实现 - 只需序列化对象并反序列化即可。

+0

感谢您的提示。当我试着逐出我得到这个错误: org.hibernate.HibernateException:非法尝试集合有两个打开的会话 \t在org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:435) \t在组织关联.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:66) \t在org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:122) \t在org.hibernate.event.def.AbstractVisitor .processValue(AbstractVisitor.java:83) \t at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues( – matsientst

+0

或这取决于如果我使用更新(上述错误)或mer ge: org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联:[com.qe.model.lead.LifeLead#9943840] \t at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness( StatefulPersistenceContext.java:638) \t在org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:305) \t在org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOr – matsientst

+0

我认为新的错误因为对象是指其他实体。您也需要将这些驱逐出去,或者将关联中的cascade属性设置为包含evict。我在回答中提到的其他选项是做一个清空和清除 - 所有对象都从第一个会话中分离出来,然后在第二个会话中进行合并。 – gkamal