[英]Safely clearing Hibernate session in the middle of large transaction
我使用Spring + Hibernate進行操作,需要創建和更新數十萬個項目。 像這樣的東西:
{
...
Foo foo = fooDAO.get(...);
for (int i=0; i<500000; i++) {
Bar bar = barDAO.load(i);
if (bar.needsModification() && foo.foo()) {
bar.setWhatever("new whatever");
barDAO.update(bar);
// commit here
Baz baz = new Baz();
bazDAO.create(baz);
// if (i % 100 == 0), clear
}
}
}
為了防止丟失中間的更改,我在barDAO.update(bar)
之后立即提交更改:
HibernateTransactionManager transactionManager = ...; // injected by Spring
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
transactionManager.commit(transactionStatus);
此時我不得不說整個進程在包含在org.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter
中的事務中運行(是的,這是一個webapp)。
這一切都運行良好,但有一個例外:在幾千次更新/提交之后,整個過程變得非常慢,很可能是由於內存因Spring / Hibernate保留的不斷增加的對象而膨脹。
在僅Hibernate環境中,通過調用org.hibernate.Session#clear()
可以很容易地解決這個問題。
現在,問題:
clear()
的好時機? 它的性能成本是否很高? bar
或baz
等對象不會自動釋放/ GCd? 在提交之后將它們保留在會話中有什么意義(在下一個迭代循環中它們無論如何都無法訪問)? 我沒有做過記憶轉儲來證明這一點,但我的好感覺是它們一直存在直到完全退出。 如果答案是“Hibernate cache”,那么為什么緩存在可用內存不足時刷新? org.hibernate.Session#clear()
(考慮到整個Spring上下文,延遲加載等等)? 是否有任何可用的Spring包裝器/對應物來實現相同的目的? foo
會發生什么,假設在循環內調用clear()
? 如果foo.foo()
是一個延遲加載方法怎么辦? 謝謝你的答案。
何時是清除()的好時機? 它的性能成本是否很高?
在刷新更改后,定期(理想情況下與JDBC批處理大小相同)。 該文檔描述了有關批處理的章節中的常用習語:
13.1。 批量插入
在使新對象持久化flush()然后清除()會話時,為了控制第一級緩存的大小。
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for ( int i=0; i<100000; i++ ) { Customer customer = new Customer(.....); session.save(customer); if ( i % 20 == 0 ) { //20, same as the JDBC batch size //flush a batch of inserts and release memory: session.flush(); session.clear(); } } tx.commit(); session.close();
這不應該有性能成本 ,反對:
為什么bar或baz等對象不會自動釋放/ GCd? 在提交之后將它們保留在會話中有什么意義(在下一個迭代循環中它們無論如何都無法訪問)?
如果您不想跟蹤實體,那么您需要clear()
地clear()
會話,這就是它的工作方式(人們可能希望在不“丟失”實體的情況下提交事務)。
但是從我所看到的情況來看,bar和baz實例應該在明確之后成為GC的候選者。 分析內存轉儲以查看正在發生的事情將會很有趣。
安全/建議直接調用org.hibernate.Session #clear()
只要你flush()
掛起的更改就不會松開它們(除非這是你想要的),我沒有看到任何問題(你的當前代碼會松動每100個循環創建一個但是它可能只是一些偽代碼)。
如果對上述問題的回答是正確的,那么對象foo會發生什么,假設在循環內調用clear()? 如果foo.foo()是一個延遲加載方法怎么辦?
調用clear()
從Session
驅逐所有已加載的實例,從而使它們成為分離的實體。 如果后續調用要求實體“附加”,則它將失敗。
我只想指出,在清除會話后,如果要繼續使用會話中的某些對象,則必須使用Session.refresh(obj)
才能繼續。
否則您將收到以下錯誤:
org.hibernate.NonUniqueObjectException
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.