Spring 试图在侦听器管理的事务期间手动提交
这是一个奇怪的情况,我通常不会这样做,但不幸的是,我们的系统现在需要这种情况 系统Spring 试图在侦听器管理的事务期间手动提交,spring,hibernate,transactions,interceptor,Spring,Hibernate,Transactions,Interceptor,这是一个奇怪的情况,我通常不会这样做,但不幸的是,我们的系统现在需要这种情况 系统 我们正在运行一个Spring/Hibernate应用程序,它使用OpenSessionInView和TransactionInterceptor来管理我们的事务。在大多数情况下,它工作得很好。但是,我们最近需要生成大量线程,以便向提供者发出一些并发HTTP请求 问题 我们需要传递到线程中的实体拥有我们在当前事务中更新的所有数据。问题是,我们在服务层的内部深处产生了线程,很难进行较小的事务来实现这一点。我们最初尝试
我们正在运行一个Spring/Hibernate应用程序,它使用OpenSessionInView和TransactionInterceptor来管理我们的事务。在大多数情况下,它工作得很好。但是,我们最近需要生成大量线程,以便向提供者发出一些并发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();
线程正在使用TransactionTemplates,因为我的buyerClient对象不是由spring管理的(长期涉及的需求)。
这是代码:
@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;
}
我的目标
我的目标是能够在另一端完全初始化该实体,或者是否有人知道如何将数据提交到数据库,以便线程可以拥有一个完全填充的对象。或者,有办法查询完整对象吗?
谢谢,我知道这很重要。如果我说得不够清楚,我向你道歉
我试过了Hibernate.initialize() saveWithFlush()
更新(lead)我并不是什么都听懂的-您可以尝试使用其中一种方法来解决与两个会话关联的同一对象的问题
// 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)
第二种方法-创建对象的深度副本并传递副本。如果您的实体类是可序列化的,那么这很容易实现——只需序列化对象并反序列化即可 谢谢@gkamal的帮助。 为了每个生活在子孙后代的人。我的困境的答案是对hibernateTemplate而不是getCurrentSession()的剩余调用。我大约在一年半前搬家,由于某种原因错过了几个重要的地方。这正在生成第二个事务。在那之后,我能够使用@gkamal建议,驱逐该对象并再次抓取它 这篇文章帮我弄明白了:
谢谢你的提示。当我尝试逐出时,我遇到了以下错误:org.hibernate.HibernateException:非法尝试将集合与org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:435)org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:66)上的两个打开会话相关联在org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:122)在org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:83)在org.hibernate.event.def.AbstractVisitor.ProcessEntityValues(或者这取决于我是否使用更新(上面的错误)或merge:org.hibernate.unUniqueObjectException:org.hibernate.engine.StatefulPersistenceContext.CheckUniversity(StatefulPersistenceContext.java:638)上的会话:[com.qe.model.lead.LifeLead#9943840]已与具有相同标识符值的不同对象关联在org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:305)在org.hibernate.event.def.DefaultSaveOrUpdateEventListener.EntityIsDetailed(DefaultSaveOrI认为新错误是因为对象引用了其他实体。您也需要逐出这些实体,或者将关联上的cascade属性设置为包含逐出。我在回答中提到的其他选项是执行刷新和清除-因此所有对象都从第一个会话中分离,然后在第二个会话中执行合并session.I现在得到了:org.hibernate.ununiqueObjectException:具有相同标识符值的不同对象已与会话关联:[com.qe.model.lead.HealthLead#9956568]Doh!我看到一个错误。让我再试一次。仍然获取org.hibernate.unUniqueObjectException:具有相同标识符值的另一个对象已与会话关联:[com.qe.model.lead.AutoLead#9957286]我正在运行:getCurrentSession().save(lead);execute(lead);getCurrentSession().flush();getCurrentSession().clear();在我的线程中我调用:Lead incomingLead=leadDao.merge(Lead);
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)
// 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)