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

时间:2013-06-05 00:23:28

标签: 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()); 

最终断言将失败,因为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);
}

但是,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);
  }
}

这似乎有效,但是非常低效,因为它在驱逐它们之前将所有实例加载到hibernate会话缓存中,而当我真正想做的就是驱逐会话中的任何当前实例。

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

2 个答案:

答案 0 :(得分:12)

您可以使用session.refresh()方法。请参阅文档中的11.3. Loading an object

答案 1 :(得分: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());

此外,您必须确保您的处于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