【问题标题】:Trying to manually commit during interceptor managed transaction在拦截器托管事务期间尝试手动提交
【发布时间】:2012-05-10 01:01:15
【问题描述】:

这是一种奇怪的情况,我通常不会这样做,但不幸的是我们的系统现在需要这种情况。

系统
我们正在运行一个 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();

开始事务如下所示:

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,因为我的 buyClient 对象不是由 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;
    }

但是,当我们开始新事务时,我们会收到以下错误:

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)

我的目标 我的目标是能够在另一端完全初始化该实体,或者是否有人对我如何将数据提交到数据库有任何想法,以便线程可以拥有一个完全填充的对象。或者,有办法查询一个完整的对象? 谢谢,我知道这真的很重要。如果我不够清楚,我深表歉意。

我试过了
休眠.initialize() saveWithFlush() 更新(引导)

【问题讨论】:

    标签: spring hibernate transactions interceptor


    【解决方案1】:

    我没有遵循所有内容 - 您可以尝试其中一种方法来解决您遇到的关于同一个对象与两个会话相关联的问题。

    // 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)
    

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

    【讨论】:

    • 感谢您的提示。当我尝试驱逐时,我得到了这个错误:org.hibernate.HibernateException:非法尝试将一个集合与 org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:435) 的两个打开的会话在 org.hibernate.event 关联。 def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:66) at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:122) at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:83)在 org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(
    • 或者这取决于我是使用更新(上面的错误)还是合并:org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已经与会话相关联:[com.qe. model.lead.LifeLead#9943840] 在 org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:305) 在 org.hibernate.event 的 org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:638) .def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOr
    • 我认为新的错误是因为对象引用了其他实体。您还需要逐出这些或将关联上的级联属性设置为包括逐出。我在回答中提到的其他选项是进行刷新和清除 - 因此所有对象都与第一个会话分离,然后在第二个会话中进行合并。
    • 我现在知道了:org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联:[com.qe.model.lead.HealthLead#9956568] 哇!我看到了一个错误。让我再试一次。
    • 仍然得到 org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联:[com.qe.model.lead.AutoLead#9957286] 我正在运行:getCurrentSession( ).save(铅);驱逐(领导); getCurrentSession().flush(); getCurrentSession().clear();在我的线程中,我正在调用: Lead incomingLead = leadDao.merge(lead);
    【解决方案2】:

    感谢@gkamal 的帮助。 对于生活在后代的每个人。我的困境的答案是对 hibernateTemplate 而不是 getCurrentSession() 的剩余调用。大约一年半前我采取了行动,由于某种原因错过了一些关键的地方。这正在产生第二笔交易。之后,我能够使用@gkamal 建议并驱逐对象并再次抓取它。

    这篇文章帮我弄清楚了:
    http://forum.springsource.org/showthread.php?26782-Illegal-attempt-to-associate-a-collection-with-two-open-sessions

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2019-06-04
      • 2015-09-26
      • 2015-06-28
      • 2017-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多