我正在使用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());
Run Code Online (Sandbox Code Playgroud)
最后的断言会因为account.getBalance()回报0.00而不是失败而失败20.00.
我希望第二次调用来accountDao.get(...)访问数据库,并获取新的ACCOUNT对象.但是Hibernate似乎返回已经在其缓存中的帐户对象(当我检查查找调用的调试输出时,我看到number of objects hydrated: 0).
我假设Hibernate不知道数据库由于存储过程调用而发生了变化,这就是它在缓存中使用对象的原因.
所以我开始考虑解决方案.一种是在保存PAYMENT时从休眠会话缓存中删除任何ACCOUNT和PAYMENT对象.这将强制对任何ACCOUNT或INVOICE操作进行数据库提取(使用新更新的值).
我尝试了以下方法:
public void save(Payment payment) {
getSession().persist(payment);
getSessionFactory().evict(Invoice.class);
getSessionFactory().evict(Account.class);
}
Run Code Online (Sandbox Code Playgroud)
但是,hibernate跟踪日志显示没有任何反应.我认为这sessionFactory.evict(...)是在二级缓存上运行的,它没有启用,所以没有什么可以驱逐的.
接下来,我尝试通过逐出我可以找到的每个实例来从会话缓存中驱逐所有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);
}
}
Run Code Online (Sandbox Code Playgroud)
这似乎有效,但效率非常低,因为它在驱逐它们之前将所有实例加载到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));
accountDao.getSession().evict(account)
account = accountDao.get(accountId);
assertEquals(20.00, account.getBalance());
Run Code Online (Sandbox Code Playgroud)
此外,您必须确保您不在REPEATABLE_READ隔离级别.在这种隔离模式下,Spring保证同一事务中的两个读取始终返回相同的结果.由于隔离级别优先于缓存逐出,因此缓存逐出不会产生任何可见效果.
有一种方法可以解决此问题:将事务隔离级别声明为READ_UNCOMMITTED或READ_COMMITTED.如果您使用标准方言,您可能会遇到着名的"标准JPA不支持自定义隔离级别"例外.在这种情况下,您可以应用以下解决方法:春季3.3:http://amitstechblog.wordpress.com/2011/05/31/supporting-custom-isolation-levels-with-jpa/ 春季3.2:HTTP:// shahzad- mughal.blogspot.com/2012/04/spring-jpa-hibernate-support-for-custom.html
| 归档时间: |
|
| 查看次数: |
35810 次 |
| 最近记录: |