【问题标题】:Hibernate reading function shows old data休眠读取功能显示旧数据
【发布时间】:2012-08-15 15:45:45
【问题描述】:

我在显示数据库中的数据时遇到问题。如果我更新一个对象,有时我会得到旧数据,有时会得到新数据。更新功能运行良好(我可以在数据库中看到正确的更新),而读取功能似乎获取缓存数据。我尝试禁用两个缓存,尝试在更新/保存期间打开和关闭会话,但它仍然无法正常工作。 User 和 Store bean 都有 Lazy fetches。 谢谢!

阅读功能:

    public static List<Store> getStoreByUser(User user)
        throws HibernateException {
    List<Store> result = null;
    Session session = sessionFactory.getCurrentSession();   
    Transaction transaction = null;
    try {
        transaction = session.getTransaction();
        Criteria criteria = session.createCriteria(Store.class);
        criteria.add(Restrictions.eq("userUserID", user));
        result = criteria.list();
    } catch (HibernateException he) {
        logger.error("No Store found for user: = " + user, he);
        throw he;
    }
    return result;
}

更新/保存功能:

    public static Integer insertOrUpdateStore(Store store)
        throws HibernateException {
    Integer id = null;
    Session session = sessionFactory.getCurrentSession();   
    Transaction transaction = null;
    try {
                    transaction = session.getTransaction();
        if (store.getStoreID() != null && store.getStoreID() != 0) {

            session.merge(store);
            transaction.commit();

        } else {
                id = (Integer) session.save(store);
            transaction.commit();               
        }
    } catch (HibernateException he) {
        if (transaction != null) {
            transaction.rollback();
        }
    } finally {
    }       
    return id;
}

【问题讨论】:

    标签: hibernate session caching


    【解决方案1】:

    您可以逐出旧数据,以确保从 db 中获取数据,而不是从 L1 和 L2 缓存中获取数据。

    您还必须确保您不在REPEATABLE_READ 隔离级别。在这种隔离模式下,它保证了同一事务中的两次读取将始终返回相同的结果。由于隔离级别优先于您的缓存逐出,因此缓存逐出不会有任何可见的效果。

    有一种方法可以解决此问题:将您的事务隔离级别声明为READ_UNCOMMITTEDREAD_COMMITTED

    【讨论】:

    • “同一事务中的两次读取将始终返回相同的结果”。这有点误导。 REPEATABLE READ显示更改的数据,只要它是在同一个事务中进行的。
    【解决方案2】:

    我有同样的问题,我的查询选择 * 返回旧数据。 我在 hibernate.cfg.xml 文件中像这样关闭了二级缓存

    <property name="hibernate.cache.use_second_level_cache">false</property>
    <property name="hibernate.cache.use_query_cache">false</property>
    <property name="hibernate.c3p0.max_statements">0</property>
    

    我会尝试在 transaction.commit() 之前/之后添加 session.flush() 或 session.clear() 但它没有给出积极的结果

    【讨论】:

      【解决方案3】:

      您可以打开一个新会话,以便从会话缓存中获取“新鲜”(从数据库更新)数据,而无需旧实体。在下面的示例中,您可以看到一个实体正在从数据库中查询。您也可以使用相同的机制来返回实体而不是布尔值,或者调用session.Refresh()(当然来自您当前的会话)来刷新数据库中的最新更改:

              /// <summary>
              ///  Gets an item exists on database.
              /// </summary>
              /// <param name="Id">Item ID to check if exists on database</param>
              /// <returns> True if the <paramref name="Id"/> to check exists on database, otherwise false. </returns>
              public bool Exists<T>(object Id)
              {
                  using (var session = NHibernateSessionHelper.OpenSession())
                  {
                      using (var transaction = session.BeginTransaction())
                      {
                          //get if the item is new. If is new, entity will be null and non exists
                          return session.Get<T>(Id) == null ? false : true;
                          //also you can return the entire table from database, or filter:
                          //session.CreateCriteria<T>().List<T>();
                      }
                  }
              }
      
              public void Refresh(object entity)
              {
                  //get latest changes from database (other users/sessions changes, manually, etc..) 
                  NHibernateSessionHelper.CurrentSession.Refresh(entity);
              }
      

      希望对你有帮助。

      【讨论】:

      • 在某些情况下,这个对我不起作用。我已经为每个操作创建了一个新会话,然后将其关闭,然后打印会话以确保它全部为空
      【解决方案4】:

      通常,您有一个isolation level“已提交读”。这使您的事务可以看到其他事务已提交的更改。隔离级别由底层 dbms 实现,而不是 hibernate。

      您不能禁用一级缓存(可能是通过使用不应该用于一般目的的无状态会话)。执行查询时,NH 总是从缓存中找到值时返回,以确保您不会在内存中获得相同的数据库记录。

      如果这对您来说是个问题,您应该切换到更高的隔离级别。例如可重复读取(这意味着它所说的:多次读取相同的数据时,您总是得到相同的结果)。仍然有机会看到其他交易的变化。使用可序列化的隔离级别,您应该不会再遇到这种问题了。

      注意:切换到另一个隔离级别是对大多数系统的重大更改,应仔细计划。

      【讨论】:

      • 我不知道哪个是我的默认隔离值,但是我已将其切换为“已提交读”并添加:2在 hibernate.cfg.xml 中,现在它可以工作了!!我花了很多时间寻找解决方案;你的解释很清楚!非常感谢斯特凡!
      • 经过几次尝试,我注意到脏数据仍然存在,有时它们会弹出......我可能需要一个新的工作示例,因为我不知道我做错了什么。
      • 仔细阅读我的答案:使用“已提交读”时,它仍然可能发生。您需要提高隔离级别。
      • 如果我把级别 4 或 8(可序列化)我得到一个错误:org.hibernate.exception.GenericJDBCException: Lock wait timeout exceeded;尝试重新启动事务。这可能是一个僵局。我觉得我遇到这种问题很奇怪,可能是我读取或更新数据时出了点问题。会话和事务呢?我可以在每次更新/阅读时打开/关闭它们吗?
      • 您应该保留整个业务流程的会话和事务(应尽可能短)。必须在相同的数据上并行运行很多。超时和死锁取决于底层 dbms 中可序列化的实现。例如。 SQL Server 有很多锁定。甲骨文要好得多。我想知道你在这些并行交易中做了什么......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      • 2017-08-14
      • 1970-01-01
      • 2018-05-31
      • 1970-01-01
      相关资源
      最近更新 更多