Hibernate を使用して Oracle データベースにアクセスしています。
Hibernate の第 1 レベル (またはセッション) キャッシュに問題があると思います。アカウントを表すテーブルがあります: ACCOUNT テーブル、INVOICE テーブル、および PAYMENTS テーブルです。PAYMENT を追加すると、関連する INVOICE および ACCOUNT テーブルの列が自動的に更新されるように、Oracle データベースで定義されたプロシージャがあります。
私が抱えている問題は、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
2 番目の呼び出しaccountDao.get(...)
でデータベースにアクセスし、新しい ACCOUNT オブジェクトを取得します。しかし、Hibernate は既にキャッシュにあるアカウント オブジェクトを返すように見えます (lookup 呼び出しのデバッグ出力を調べると、 が表示されますnumber of objects hydrated: 0
)。
Hibernate は、ストアド プロシージャの呼び出しのためにデータベースが変更されたことを認識していないと思います。これが、オブジェクトをキャッシュ内で使用する理由です。
そこで、解決策を考え始めました。1 つは、PAYMENT が保存されるたびに、休止状態のセッション キャッシュから ACCOUNT および PAYMENT オブジェクトを削除することです。これにより、ACCOUNT または INVOICE 操作のデータベース フェッチが (新しく更新された値で) 強制されます。
私は次のことを試しました:
public void save(Payment payment) {
getSession().persist(payment);
getSessionFactory().evict(Invoice.class);
getSessionFactory().evict(Account.class);
}
しかし、休止状態のトレース ログは何も起こらなかったことを示していました。sessionFactory.evict(...)
これは有効になっていない第 2 レベルのキャッシュで動作するため、削除するものは何もないと思います。
次に、見つかった各インスタンスを削除して、セッション キャッシュからすべての ACCOUNT オブジェクトと INVOICE オブジェクトを削除しようとしました。
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);
}
}
これは機能しているように見えますが、すべてのインスタンスを削除する前に休止状態のセッション キャッシュにロードするため、非常に非効率的です。
指定されたタイプのすべてのオブジェクトの第 1 レベルのキャッシュをクリアする方法がわかりません。他にどのような解決策がありますか?