【问题标题】:Hibernate query returns stale dataHibernate 查询返回过时的数据
【发布时间】:2015-10-08 22:28:14
【问题描述】:

我有一个只从数据库读取数据的休眠查询(休眠 3)。数据库由单独的应用程序更新,查询结果不反映数据库中的更改。

通过一些研究,我认为这可能与 Hibernate L2 缓存有关(我不认为它是 L1 缓存,因为我总是打开一个新会话并在它完成后关闭它)。

Session session = sessionFactoryWrapper.getSession();
List<FlowCount> result = session.createSQLQuery(flowCountSQL).list();
session.close();

我尝试在休眠配置文件中禁用第二层缓存,但它不起作用:

<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.use_query_cache">false</property>
<propertyname="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

我还在Session session = sessionFactoryWrapper.getSession(); 之后添加了session.setCacheMode(CacheMode.Refresh); 以强制刷新一级缓存,但仍然无法正常工作...

还有其他方法可以获取数据库中的更改吗?我在如何禁用缓存方面做错了吗?谢谢。

更新: 我通过监控数据库查询日志做了另一个实验:

  1. 第一次运行代码。检查日志。查询出现。
  2. 等待几分钟。数据已被另一个应用程序更改。我通过 MySql Workbench 验证了它。为了区别于前面的查询,我添加了一个虚拟条件。
  3. 第二次运行代码。检查日志并显示查询。

两次我都使用相同的查询,但由于数据已更改,结果应该不同,但不知何故不是......

【问题讨论】:

  • 我有一个非常相似的问题。您是否找到了忘记在此处发布的解决方案?
  • @AndrewBourgeois 抱歉,我没有找到解决方案,所以我删除了 Hibernate 并使用 C3P0 让项目顺利进行。

标签: java hibernate caching


【解决方案1】:

为了强制 L1 缓存刷新,您可以使用 Session 的 refresh(Object) 方法。

来自 Hibernate 文档,

从底层数据库重新读取给定实例的状态。 不建议使用它来实现长时间运行的会话 跨越许多业务任务。然而,这种方法在某些情况下是有用的 特殊情况。例如

  • 数据库触发器在插入或更新时更改对象状态
  • 在同一会话中执行直接 SQL(例如大规模更新)后
  • 插入 Blob 或 Clob 后

此外,您提到您添加了session.setCacheMode(CacheMode.Refresh) 以强制刷新一级缓存。这是行不通的,因为 CacheMode 不需要对 L1 缓存做任何事情。再次来自 Hibernate Docs,

CacheMode 控制会话如何与二级交互 缓存和查询缓存。

【讨论】:

    【解决方案2】:

    没有二级缓存和查询缓存,hibernate 总是会在一个新会话中从数据库中获取所有数据。

    您可以通过启用 org.hibernate 包的 DEBUG 日志级别(如果您想查看绑定变量,则启用 org.hibernate.type 的 TRACE 级别)来检查 Hibernate 究竟执行了哪个查询。

    【讨论】:

    • 嗨,我已经更新了我的帖子。我确实检查了数据库查询日志,查询命中了源,但不知何故它没有返回正确的结果。
    • 手动(通过 SQL 客户端)执行查询时会发生什么?
    • 如果我手动执行它,我会得到最新的数据。但是,在休眠日志中,它仍然显示错误。会不会是连接?其他一些帖子建议使用 C3P0 作为连接提供者...
    【解决方案3】:

    查询反映了多长时间的更改?如果它在一段时间后显示更改,则可能与您获取会话的方式有关。

    我对 SessionFactoryWrapper 类不熟悉,这是你写的自定义类吗?您是否以某种方式缓存会话对象的时间超过了必要的时间?如果是这样,如果它已经在会话中加载,查询将重用对象。这就是 Hibernate 保证的可重复读取语义背后的理念。

    您可以在运行查询之前清除会话,然后它将返回最新数据。

    【讨论】:

      【解决方案4】:

      Hibernate 的内置连接池机制存在漏洞。

      将其替换为像 c3p0 这样的生产质量替代品。

      在我开始使用 c3p0 之前,我遇到了同样的问题,即返回过时的数据。

      【讨论】:

      • 这个答案不是很有用,所以我认为应该是评论。
      【解决方案5】:

      以防万一它是一级缓存

      你能显示你的查询吗?

      查看以下错误:

      https://hibernate.atlassian.net/browse/HHH-9367

      https://jira.grails.org/browse/GRAILS-11645

      补充:

      http://howtodoinjava.com/2013/07/01/understanding-hibernate-first-level-cache-with-example/

      http://www.dineshonjava.com/p/cacheing-in-hibernate-first-level-and.html#.VhZ7o3VElhE

      Hibernates 1st Level Cache 导致的可重复查找器问题

      需要明确的是,两个测试都成功了 - 完全不符合逻辑:

      userByEmail('foo@bar.com').email != 'foo@bar.com'
      

      完成测试

      @Issue('https://jira.grails.org/browse/GRAILS-11645')
      class FirstLevelCacheSpec extends IntegrationSpec {
          def sessionFactory
      
          def setup() {
              User.withNewSession {
                  User user = new User(email: 'test@test.org', password: 'test-password')
                  user.save(flush: true, failOnError: true)
              }
      
          }
      
          private void updateObjectInNewSession(){
              User.withNewSession {
                  def u = User.findByEmail('test@test.org', [cache: false])
                  u.email = 'foo@bar.com'
                  u.save(flush: true, failOnError: true)
              }
          }
      
          private User userByEmail(String email){
              User.findByEmail(email, [cache: false])
          }
      
          def "test first update"() {
              when: 'changing the object in another session'
              updateObjectInNewSession()
      
              then: 'retrieving the object by changed identifier (no 2nd level cache)'
              userByEmail('foo@bar.com').email == 'foo@bar.com'
          }
      
          def "test stale object in 1st level"() {
              when: 'changing the object after pulling objects to cache by finder'
              userByEmail('test@test.org')
              updateObjectInNewSession()
      
              then: 'retrieving the object by changed identifier (no 2nd level cache)'
              userByEmail('foo@bar.com').email != 'foo@bar.com'
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-08-29
        • 2010-11-03
        • 2013-02-28
        • 2011-09-14
        • 1970-01-01
        • 1970-01-01
        • 2013-07-17
        • 1970-01-01
        • 2023-03-28
        相关资源
        最近更新 更多