18

我正在使用 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);
}

但是休眠跟踪日志显示什么也没发生。我认为这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);
  }
}

这似乎可行,但效率极低,因为它在驱逐它们之前将所有实例加载到休眠会话缓存中,而我真正想做的只是驱逐会话中的任何当前实例。

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

4

2 回答 2

12

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

于 2013-06-05T00:33:08.613 回答
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 不支持自定义隔离级别”异常。在这种情况下,您可以应用以下解决方法: spring 3.3:http ://amitstechblog.wordpress.com/2011/05/31/supporting-custom-isolation-levels-with-jpa/ spring 3.2:http://shahzad- mughal.blogspot.com/2012/04/spring-jpa-hibernate-support-for-custom.html

于 2013-11-17T04:03:48.453 回答