2

春/休止状態のアプリケーションでは、毎晩いくつかの cronjobs を実行しています。そのうちの 1 つは、次のように複数のトランザクションで作業を行う必要があります。

@Scheduled(cron = "...")
public void cron ( ) 
{
    batchJob();
}

@Transactional(propagation = Propagation.NEVER)
public void batchJob ( )
{
    List<Customer> customers = getCustomers();
    for (Customer customer : customers
    {
        doSomething(customer);
    }
}

@Transactional(readOnly = true)
protected List<Customer> getCustomers ( )
{
    return customerRepository.getCustomers();
}

@Transactional
protected void doSomething (Customer customer )
{
         // LazyInitializationException 
         customer.getAddress();
         // ...
}

私の春の設定の一部:

<!-- Transaction -->
<tx:annotation-driven mode="aspectj" />

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

ここでの重要な点は、長時間実行されるトランザクションを 1 つも作成したくないということです。そのため、最初にすべての顧客を取得し、顧客ごとにトランザクション メソッドを呼び出します。(投稿されたコードが問題を理解するのに十分であることを願っています)

もちろん、'getCustomer' に関するトランザクションがコミットされたときに Spring がセッションを閉じているため、LazyInitializationException が発生します。

私が考えることができる可能な解決策:

  • OpenSessionInViewInterceptor を使用できますが、これは Web コンポーネントです

  • 切り離されたオブジェクトを session.merge(customer) で再接続できます

  • これは Propagation.Nestedtransaction の場合ですか? ネストされたトランザクションのセマンティクスは何ですか? ほとんどのデータベースにはネストされたトランザクションがありません (postgresql にはありますが、使用したことはありません。2 フェーズ コミットと呼ばれます)。

  • メソッドを書き直して、customerId を消費し、2 番目のトランザクション内で顧客を再度読み込むことができます。

今、私の問題に関して2つの質問があります:

  1. 上記のバグを再現するテストをどのように作成できますか?

  2. これを中心に開いているセッションを簡単にスパンするにはどうすればよいですか、または複数のトランザクションで一連の作業を行うための最良の方法は何ですか?

4

1 に答える 1