存储过程更新数据库后,从数据库重新加载对象,而不是缓存?

Joh*_*zen 17 hibernate

我正在使用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会话缓存中,而当我真正想要做的就是驱逐会话中的任何当前实例.

我看不到任何清除指定类型的所有对象的第一级缓存的方法,那么还有哪些其他解决方案可用?

lun*_*unr 12

您可以使用session.refresh()方法.见11.3.在文档中加载对象.


Sha*_*Muh 5

为什么不在从数据库中再次读取帐户对象之前将其逐出?我会这样做:

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

  • 感谢您的评论.我不再在该项目上工作(或在该公司),所以我无法验证您提出的解决方案.我确实尝试在原始帖子中删除所有帐户类,但驱逐该实例可能是要走的路. (2认同)