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

Hibernate 存储过程更新数据库后,是否从数据库而不是缓存重新加载对象?,hibernate,Hibernate,我正在使用Hibernate访问Oracle数据库 我想我在使用Hibernate的第一级(或会话)缓存时遇到了一些问题。我有代表账户的表:账户表、发票表和付款表。 Oracle数据库中定义了一些过程,以便添加付款将自动更新关联发票和帐户表中的列 我遇到的问题是,当我使用Hibernate执行以下操作时: Account account = accountDao.get(accountId); assertEquals(0.00, account.getBalance()); // Savin

我正在使用Hibernate访问Oracle数据库

我想我在使用Hibernate的第一级(或会话)缓存时遇到了一些问题。我有代表账户的表:账户表、发票表和付款表。 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()); 
public void save(Payment payment) {
  getSession().persist(payment);
  getSessionFactory().evict(Invoice.class);
  getSessionFactory().evict(Account.class);
}
最后的断言将失败,因为
account.getBalance()
返回
0.00
,而不是
20.00

我想第二次调用
accountDao.get(…)
来访问数据库,并取回新的ACCOUNT对象。但是Hibernate似乎返回已经在其缓存中的account对象(当我检查查找调用的调试输出时,我看到
对象数量:0

我假设Hibernate不知道数据库因为存储过程调用而发生了更改,这就是它在缓存中使用对象的原因

所以我开始考虑解决办法。一种是在保存付款时从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()); 
public void save(Payment payment) {
  getSession().persist(payment);
  getSessionFactory().evict(Invoice.class);
  getSessionFactory().evict(Account.class);
}
但是hibernate跟踪日志显示什么也没有发生。我认为,
sessionFactory.execute(…)
在第二级缓存上运行,该缓存未启用,因此没有任何可退出的内容

接下来,我尝试通过逐出我能找到的每个实例,从会话缓存中逐出所有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会话缓存中,而我真正想做的就是逐出会话中的任何当前实例


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

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

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

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());
此外,您还必须确保您的系统不处于可重复读取隔离级别。在这种隔离模式下,Spring保证同一事务中的两次读取总是返回相同的结果。由于隔离级别优先于缓存逐出,因此缓存逐出将不会产生任何可见的效果

有一种方法可以解决此行为: 将事务隔离级别声明为READ_UNCOMMITTED或READ_COMMITTED。如果使用标准方言,可能会遇到著名的“标准JPA不支持自定义隔离级别”异常。在这种情况下,您可以应用以下解决方法: 春季3.3:
春季3.2:

谢谢。但是,我不确定
refresh()
是否是这里的解决方案。我的理解是,
refresh()
对于重新加载由数据库更新的对象非常有用,例如,如果我保存了
付款
,则
如果触发器修改其状态,则刷新它。但在我的例子中,当更新付款时,我需要对缓存中的每个现有帐户和发票对象调用
refresh
。如何从缓存中提取每个帐户和支付对象?您可以对这些关联设置
cascade=“refresh”
。在这种情况下,每当刷新实体时,所有关联的实体都将被刷新。我可以按照您的建议设置
cascade=“refresh”
,因为帐户和发票之间存在关联,这意味着我只需要刷新帐户对象。但我需要解决的问题是:如何从缓存中获取ACCOUNT对象,这样我就可以对每个对象调用
refresh
,而无需执行
from ACCOUNT
查询?e、 类似于
getSession().refresh(Account.class)
(这在API AFAIK中不存在,但这正是我所寻求的功能)。我认为逐个刷新实体不是一个好主意。毕竟,刷新将为每个实体发出一个单独的查询。在一个查询中重新加载这些对象对我来说似乎更好。也许,在您的应用程序中,您可以限制重新加载帐户的数量?我也找不到访问会话中实体的方法。我想原因很清楚。您需要持有您的实体。谢谢您的评论。我不再从事那个项目(或在那个公司),所以我没有办法验证你提出的解决方案。在我最初的帖子中,我确实尝试过逐出所有帐户类,但是逐出实例可能是一种方法。