0

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

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

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

我试过
Hibernate.initialize() saveWithFlush() update(lead)

4

2 回答 2

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)

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

于 2012-05-10T05:24:53.390 回答
0

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

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

于 2012-05-10T22:15:48.557 回答