2016-06-22 51 views
1

我有Hibernate的性能問題。休眠 - 多次啓動相同的操作變得越來越慢

Hibernate : 3.2.6.ga 
JDK : jdk1.6.0_45 

我有一個函數witch被註釋@Transactionnal鏈接到一個EntityManager。

該功能被稱爲一個循環,所以我有:

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

它的確定,如果我啓動它5/10/20次,處理時間似乎並沒有增加。 但是,如果我啓動300/400次,「saveit」的時間越來越慢... 我監控了java內存,並沒有看到奇怪的東西。

所以我找到一些關於Flush/Clean魔術解決方案的文章。 我試過了,它是有效的。

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

但對我來說這是很奇怪的,因爲我認爲@Transactionnal註釋管理這一切的東西,特別是當我沒有做任何其他與此相關的循環外休眠...也許我有點bit lost ...

最後的問題:這個解決方法是否安全?

注意:實際上,saveIt函數在數據操作方面相當龐大,所以處理時間非常重要,不能增加。

編輯 - 附加信息:

我停在調試模式下在我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... 

所以,是的,我看到一些代理,但對我這個代理來自Spring注入,而不是來自Hibernate。

編輯N°2:

是的,我使用OpenSessionInView過濾

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

包含for循環的方法的名稱是什麼?你能否在堆棧中報告它(如果你過濾了它)? – Thierry

+0

這是MyAction類中的「doSave」。這是Struts控制器類。 – Johann

+0

好的,我猜測for循環是在'MyAction.doSave()'方法中。並且在$ Proxy40中您沒有其他的$ Proxy(堆棧中的上面,打印的堆棧跟蹤中的下面)?你使用openSessionInViewFilter嗎? – Thierry

回答

0

事實上,你把它(裏面的for循環,但外面的saveIt()方法表明,cleanMySession()工作方法在啓動for循環之前,您已經有一個事務處於打開狀態,因此,您在for循環中執行的所有操作僅在一個tx中完成,只有一個會話的大小將保持增長,從而減慢flush操作(髒檢查)

在撥打saveIt()之前,您需要重構服務調用以確保沒有事務存在。

要找出不需要的事務處於打開狀態,只需在for循環的開始處設置一個斷點,使用運行時設置斷點並查看堆棧跟蹤。當你開始看到$ Proxyxxx類時,下一個方法就是打開這個事務。

它看起來就像是在棧:

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一個配置,你可能會遇到的方法代理不@Transactional註解,只是不停地尋找在堆棧中的呼叫

編輯: 整個Http請求將有一個由過濾器打開的tx:查看它,就像您使用@Transactional註釋Controller(包括視圖生成)一樣。

這個過濾器被認爲是不好的(看看Why is Hibernate Open Session in View considered a bad practice?),但是如果你的應用程序很大,刪除它可能會很複雜和痛苦。

您的解決方法應該可行。

如果您需要提交多次,您可以查看@Transactional(propagation = Propagation.REQUIRES_NEW) ...但是有了這個,每個請求都會與數據庫形成多個連接(每個過濾器1個,每個嵌套級別爲REQUIRES_NEW),並可能失敗在中間,因爲在遇到'requires_new'方法時沒有更多連接池

+0

感謝您的回答。你有一個很好的觀點,那就是我之前可能有一筆交易已經開始(但是在哪裏?),這就是我的問題的目的。我所從事的這個項目非常混亂,我試圖弄清楚結構性問題在哪裏。我會深入探索。謝謝。 – Johann

相關問題