代码之家  ›  专栏  ›  技术社区  ›  NAGASREE

Nhibernate:为什么会话缓存和查询结果不匹配?

  •  1
  • NAGASREE  · 技术社区  · 1 年前

    我们正在使用Nhibernate v4.0.30319,我们正在使用 Flushmode.Commit 让我们考虑一个例子 Employee 桌子与 IsActive DB中的列。

    在其中一笔交易中,我们正在获取 员工 具有 empId = 100 如下所示:

    var employee = session.Get<Employee>(100);
    
    employee.IsActive = false;
    

    它还没有被承诺,甚至还没有被刷新。

    我们立即运行查询以获取所有活动的员工。

    var activeEmployees = session.CreateCriteria<Employee>()
                                 .Add(Restrictions.Eq("IsActive", true))
                                 .List<Employee>();
    

    现在在 activeEmployees 结果我仍然得到empId=1的员工详细信息,但 IsActive 价值为 false .

    我无法理解为什么这种不一致的行为。如果查询在数据库上运行,那么它应该返回empId-100 IsActive 值“True”,否则如果它在一级缓存上运行,则不应在查询结果中包含empId-100。在这里,它将返回EmpId-1 IsActive 错误的

    它为什么会这样?我已经阅读了一些文章和配置细节,但我无法弄清楚这种行为。

    我创建了一个控制台应用程序:

    员工表:

    enter image description here

    Nhibernate配置:

     if (_sessionFactory == null)
     {
         var configuration = new Configuration();
    
         // Set connection properties for SQL Server
         configuration.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, typeof(SqlClientDriver).AssemblyQualifiedName);
         configuration.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "Data Source=INLT-D72FCSG3\\SQLEXPRESS;Initial Catalog=TestDB;Integrated Security=True;");
         configuration.SetProperty(NHibernate.Cfg.Environment.Dialect, typeof(MsSql2012Dialect).AssemblyQualifiedName);
         configuration.SetProperty(NHibernate.Cfg.Environment.ShowSql, "true");
         configuration.SetProperty(NHibernate.Cfg.Environment.FormatSql, "true");
         configuration.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlAuto, "update");
    
    
         configuration.AddFile("Employee.hbm.xml"); // Add your mapping files
         _sessionFactory = configuration.BuildSessionFactory();
     }
     return _sessionFactory;
    

    执行:

     using (var session = NHibernateHelper.OpenSession())
     {
         session.FlushMode = FlushMode.Commit;
         // Start a new transaction
         using (var transaction = session.BeginTransaction())
         {
             // Load employee with Id = 100
             var employee = session.Get<Employee>(1);
             Console.WriteLine($"Before Update: {employee.Name} - IsActive: {employee.IsActive}");
         // Modify the IsActive field to false (this marks the entity as dirty)
         employee.IsActive = false;
    
         // The entity is now in the session cache with IsActive = false
         Console.WriteLine($"After Update: {employee.Name} - IsActive: {employee.IsActive}");
    
         // Now, we fetch active employees (IsActive = true)
         var activeEmployees = session.CreateCriteria<Employee>()
                                      .Add(Restrictions.Eq("IsActive", true))
                                      .List<Employee>();
    
         // Print the active employees (it should still return the modified entity in the session cache)
         Console.WriteLine("\nActive Employees (before commit):");
         foreach (var emp in activeEmployees)
         {
             Console.WriteLine($"{emp.Name} - IsActive: {emp.IsActive}");
         }
    
         // Commit the transaction to persist changes to the database
         transaction.Commit();
     }
    

    }

      // Simulate a new session for the next query (to see changes after commit)
     using (var session = NHibernateHelper.OpenSession())
     {
         var activeEmployees = session.CreateCriteria<Employee>()
                                      .Add(Restrictions.Eq("IsActive", true))
                                      .List<Employee>();
    
         // Print the active employees after the session is committed
         Console.WriteLine("\nActive Employees (after commit):");
         foreach (var emp in activeEmployees)
         {
             Console.WriteLine($"{emp.Name} - IsActive: {emp.IsActive}");
         }
     }
    

    输出:

    NHibernate:
        SELECT
            employee0_.Id as id1_0_0_,
            employee0_.Name as name2_0_0_,
            employee0_.IsActive as isactive3_0_0_
        FROM
            Employee employee0_
        WHERE
            employee0_.Id=@p0;
        @p0 = 1 [Type: Int32 (0:0:0)]
    Before Update: John Doe - IsActive: True
    After Update: John Doe - IsActive: False
    NHibernate:
        SELECT
            this_.Id as id1_0_0_,
            this_.Name as name2_0_0_,
            this_.IsActive as isactive3_0_0_
        FROM
            Employee this_
        WHERE
            this_.IsActive = @p0;
        @p0 = True [Type: Boolean (0:0:0)]
    
    Active Employees (before commit):
    John Doe - IsActive: False
    Jane Smith - IsActive: True
    Bob Brown - IsActive: True
    Alice Green - IsActive: True
    NHibernate:
        SELECT
            this_.Id as id1_0_0_,
            this_.Name as name2_0_0_,
            this_.IsActive as isactive3_0_0_
        FROM
            Employee this_
        WHERE
            this_.IsActive = @p0;
        @p0 = True [Type: Boolean (0:0:0)]
    
    Active Employees (after commit):
    Jane Smith - IsActive: True
    Bob Brown - IsActive: True
    Alice Green - IsActive: True
    

    谢谢,

    纳盖斯雷。

    1 回复  |  直到 1 年前
        1
  •  1
  •   Amit Joshi    1 年前

    当你第一次得到ID为100的员工时,实体会被缓存在一级缓存中。然后你设置它 IsActive false 因此,一级缓存中的实体被更新;尚未反映到数据库中。数据库中的值仍然是 true .

    然后,您查询所有员工,其中 IsActive 真的 。查询必须构建如下内容:

    SELECT * FROM Employee WHERE IsActive = true
    

    如上所述 IsActive 员工id 100的值仍然是 真的 因此员工100也作为结果集的一部分返回。

    那么 NHibernate为返回的所有行构建Entity对象。其他行尚未位于一级缓存中,因此它将它们构建为新的行。但是,NHibernate发现员工100的Entity对象已经存在于一级缓存中,因此它不会构建它,它只是从一级缓存使用它。作为 IsActive 错误的 在一级缓存中,您会看到不匹配。

    这是正确和预期的行为;这就是NHibernate的工作原理。

    推荐文章