尝试在拦截器管理的事务期间手动提交

时间:2012-05-10 01:01:15

标签: spring hibernate transactions interceptor

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

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

begin transaction看起来像这样:

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对象不是由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() 更新(铅)

2 个答案:

答案 0 :(得分: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)

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

答案 1 :(得分:0)

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

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