我正在使用Hibernate來訪問Oracle數據庫。在存儲過程更新數據庫之後,從數據庫重新加載對象,而不是緩存?
我想我在Hibernate的第一級(或會話)緩存中遇到了一些麻煩。我有代表賬戶的表格:ACCOUNT表,INVOICE表和PAYMENTS表。 在Oracle數據庫中定義了一些過程,因此添加PAYMENT會自動更新關聯的INVOICE和ACCOUNT表中的列。
我的問題是,當我使用Hibernate做一些這樣的:
Account account = accountDao.get(accountId);
assertEquals(0.00, account.getBalance());
// Saving a payment will trigger stored procedures that
// will update the account balance.
paymentDao.save(createPaymentForAccount(accountId, 20.00));
account = accountDao.get(accountId);
assertEquals(20.00, account.getBalance());
最後斷言會失敗,因爲account.getBalance()
回報0.00
而非20.00
。
我希望第二次調用accountDao.get(...)
來擊中數據庫,並獲取新的ACCOUNT對象。但是Hibernate似乎已經將緩存中的帳戶對象返回了(當我檢查查找調用的調試輸出時,我看到了number of objects hydrated: 0
)。
我假設Hibernate沒有意識到數據庫因存儲過程調用而改變,這就是爲什麼它在緩存中使用對象。
於是我開始思考解決方案。一種是每當保存付款時從hibernate會話緩存中刪除任何ACCOUNT和PAYMENT對象。這將強制數據庫獲取(使用新更新的值)以進行任何ACCOUNT或INVOICE操作。
我試過如下:
public void save(Payment payment) {
getSession().persist(payment);
getSessionFactory().evict(Invoice.class);
getSessionFactory().evict(Account.class);
}
但休眠跟蹤日誌表明,什麼都沒有發生。我認爲sessionFactory.evict(...)
在二級緩存上運行,這是不啓用的,所以沒有任何驅逐。
下一個我試圖驅逐每個實例驅逐從會話緩存中的所有賬戶和發票的對象我能找到的:
public void save(Payment payment) {
getSession().persist(payment);
for (Invoice invoice: lookupInvoices()) { // e.g. "from Invoice" query
getSession().evict(invoice);
}
for (Account account: lookupAccounts()) { // e.g. "from Account" query
getSession().evict(account);
}
}
這似乎是工作,但效率極其低下的,因爲它加載的所有實例進入休眠會話緩存在驅逐它們之前,當我真正想要做的就是驅逐會話中的任何當前實例。
我看不到任何清除指定類型所有對象的第一級緩存的方法,那麼還有哪些其他解決方案可用?
感謝。然而,我不確定'refresh()'是否是解決方案。我的理解是'refresh()'對於重新加載由數據庫更新的對象很有用,例如如果我保存了「付款」,那麼如果觸發器修改其狀態,則刷新它。但在我的情況下,當更新付款時,我需要在緩存中的每個現有ACCOUNT和INVOICE對象上調用'refresh'。如何從緩存中獲取每個帳戶和付款對象? –
你可以在這些關聯上設置'cascade =「refresh」'。在這種情況下,每當一個實體刷新時,所有關聯的實體也將被刷新。 – lunr
我可以像你所建議的那樣設置'cascade =「refresh」',並且由於ACCOUNT和INVOICE之間的關聯,這意味着我只需刷新ACCOUNT對象。但是我需要解決的問題是:如何從緩存中獲取ACCOUNT對象,這樣我就可以對每個對象調用'refresh',而不執行'from Account'查詢?例如類似於'getSession()。refresh(Account.class)'(它在API AFAIK中不存在,但這是我所尋求的功能)。 –