0

これは奇妙な状況であり、私は通常それを行うことはありませんが、残念ながら、私たちのシステムではこの種のシナリオが必要になりました。

システム
OpenSessionInViewとTransactionInterceptorを使用してトランザクションを管理するSpring/Hibernateアプリケーションを実行しています。ほとんどの場合、それはうまく機能します。ただし、最近、プロバイダーに対していくつかの同時HTTP要求を行うために、多数のスレッドを生成する必要があります。

問題
現在のトランザクションで更新したすべてのデータを保持するには、スレッドに渡されるエンティティが必要です。問題は、サービスレイヤーの内臓の奥深くにスレッドを生成することであり、この作業を可能にするために小さなトランザクションを作成することは非常に困難です。最初は、エンティティをスレッドに渡して、次のように呼び出してみました。

leadDao.update(lead);

問題は、2つのセッションに存在するエンティティに関するエラーが発生することです。次に、元のトランザクションをコミットし、スレッドが完了するとすぐに再開しようとします。これは私がここにリストしたものです。

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());

}

私のbuyerClientオブジェクトはSpringによって管理されていないため、スレッドはTransactionTemplatesを使用しています(長い間関係する要件)。そのコードは次のとおりです。

@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

私はすべてをフォローしていませんでした-これの1つを試して、2つのセッションに関連付けられている同じオブジェクトについて発生する問題を回避できます。

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

2番目のアプローチ-オブジェクトのディープコピーを作成し、そのコピーを渡します。これは、エンティティクラスがシリアル化可能である場合に簡単に実現できます。オブジェクトをシリアル化して逆シリアル化するだけです。

于 2012-05-10T05:24:53.390 に答える
0

助けてくれてありがとう@gkamal。後世に住むすべての人のために。私のジレンマに対する答えは、getCurrentSession()の代わりにhibernateTemplateへの残りの呼び出しでした。私は約1年半前に引っ越しをしましたが、何らかの理由でいくつかの重要な場所を逃しました。これにより、2番目のトランザクションが生成されていました。その後、@ gkamalの提案を使用してオブジェクトを削除し、再度取得することができました。

この投稿は私がそれを理解するのに役立ちました: http:
//forum.springsource.org/showthread.php?26782-Illegal-attempt-to-associate-a-collection-with-two-open-sessions

于 2012-05-10T22:15:48.557 に答える