2016-06-22 10 views
1

Hibernateでパフォーマンスに問題があります。Hibernate - 何度も同じ操作を実行すると遅くなり、遅くなります

Hibernate : 3.2.6.ga 
JDK : jdk1.6.0_45 

私はEntityManagerにリンクされている@Transactionnalというアノテーションが付けられています。

この関数は、ループ内で呼び出されたので、私が持っている:

for (Item i : itemList) 
{ 
saveIt(i); 
} 

それは私がそれを5/10/20回を起動した場合、処理時間が増加していないようです大丈夫です。 しかし、私が300/400回起動すると、 "saveit"する時間が遅くなり、遅くなります。 私はJavaメモリを監視しましたが、何か変わったことはありませんでした。

だから、Flush/Cleanの魔法の解決策について話している記事があります。 私はそれを試したと時間、それは動作します。

for (Item i : itemList) 
    { 
    saveit(i); 
    cleanMySession(); 
    } 

しかし、私は、私はこのループの外に休止状態に関連する他の何かをしないときに特別...たぶん私は少し午前、@Transactionnal注釈は、すべてこのようなものを管理することを考えたので、私には、それは、かなり奇妙ですビットが失われました...

最終的な質問:この回避策は安全ですか?

メモ:実際には、saveIt関数はデータ操作の面で非常に巨大なので、処理時間は非常に重要であり、増やしてはいけません。

EDIT - 追加情報:

私は私のcleanSession機能でデバッグモードで停止:ここ

public void cleanSession() { 
    Session session = (Session) em.getDelegate(); 
    session.flush(); 
    session.clear(); 
} 

はスタックです:

MyServiceImpl.cleanSession() line: 177 
GeneratedMethodAccessor216.invoke(Object, Object[]) line: not available 
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25 
Method.invoke(Object, Object...) line: 597 
AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 319 
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 196 
$Proxy40.cleanSession() line: not available 
MyAction.doSave() line: 814 
StrutsStack... 

はそうです、私はいくつかのプロキシを参照してくださいそれだけには私は、このプロキシはHibernateからではなく、Spring Injectionから来ています。

編集N°2:

はい、私はOpenSessionInViewフィルタ

<filter> 
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name> 
    <filter-class> 
     org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter 
    </filter-class> 
</filter> 
+0

forループを含むメソッドの名前は何ですか?あなたはそれをStackで報告することもできますか? – Thierry

+0

MyActionクラスの "doSave"です。これはStrutsコントローラクラスです。 – Johann

+0

だから、forループは 'MyAction.doSave()'メソッドにあると思います。そして、あなたは$ Proxy40よりもスタック内に他の$ Proxyがありません(スタックの上、印刷スタックトレースの下)。 openSessionInViewFilterを使用していますか? – Thierry

答えて

0

にメソッドを使用すると、forループ内で(それを置く場所に動作しますが、saveIt()方法の外にあることを示してcleanMySession()という事実を使用しますforループを開始する前にすでにトランザクションを開いているので、forループで行うすべてのアクションは1つのtxで実行され、サイズが大きくなるセッションは1つだけで、flush操作(ダーティーチェック)。

saveIt()に電話する前に、サービスコールをリファクタリングしてトランザクションが存在しないことを確認する必要があります。

不要なトランザクションがどこにあるのかを特定するには、forループの先頭にブレークポイントを設定し、実行時にそこに行き、スタックトレースを参照します。 $ Proxyxxxクラスが見え始めると、次のメソッドはトランザクションを開くメソッドになります。それはスタックにそのようになります

:あなたが設定さ@Transactional以外の側面を持っている場合

at ... <- the method with @Transactional annotation here -> 
at java.lang.reflect.Method.invoke(Method.java:498) 
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:310) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149) 
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 
at com.sun.proxy.$Proxy452.handle(Unknown Source) 
... 

、あなただけのスタックコールで探し続ける、@Transactionalと注釈を付けていない方法でプロキシが発生する場合があります

EDIT: HTTPリクエスト全体がフィルタによって開かれた1つのtxを持ちます。@Transactionalでコントローラに注釈を付けたように(ビュー生成も含めて)表示します。

このフィルタは悪いと見なされます(Why is Hibernate Open Session in View considered a bad practice?を参照してください)。しかし、アプリケーションが大きい場合は、削除するのが複雑で苦労する可能性があります。

回避策が必要です。

複数回コミットする必要がある場合は、@Transactional(propagation = Propagation.REQUIRES_NEW)を調べることができます。しかし、これにより、各リクエストはデータベースへの複数の接続を奪います(ネストされたレベルの1つにつき+1、REQUIRES_NEW)途中で、 ​​'requires_new'メソッドを検出したときにプールに接続がなくなるため、

+0

あなたの答えをありがとう。あなたは私がおそらく前に開かれたトランザクションを持っているという事実について良い点を持っています(しかしどこか?)それが私の質問の目的でした。私が取り組んでいるプロジェクトはかなり面倒です。私は大きな構造上の問題がどこにあるのか把握しようとしています。私は深く検索します。ありがとう。 – Johann

関連する問題