【问题标题】:nHibernate eager loading- strange update behaviournHibernate 急切加载 - 奇怪的更新行为
【发布时间】:2010-12-15 09:27:41
【问题描述】:

我的域是一个机场,其中包含多个航站楼,每个航站楼都包含区域等。
由于机场/航站楼/区域实体的数量非常少,我想:
1.检索机场时急切加载所有层次结构。
(使用以下流畅的配置:

//eagerly load terminals
mapping.HasMany(x => x.Terminals).Not.LazyLoad()
            .Cache.ReadWrite();

)
2. 启用二级缓存,这样机场对象的所有检索都不会命中数据库。

急切加载和缓存工作正常,但以下测试会产生一些奇怪的行为。
(以下代码两次检索机场实体(第二次未访问数据库),并更新其中一个。)

        [TestMethod]
    public void TestSecondLevelCache()
    {
        Airport firstAirport = null, secondAirport = null;

        Console.WriteLine("first select");
        using (ISession session = this.SessionFactory.OpenSession())
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                //the idea here is to see whether there are two calls to DB here. check the sql output
                AirportDAO dao = new AirportDAO(session);
                firstAirport = dao.GetAirport();
                transaction.Commit();
            }
        }

        Console.WriteLine("second select");
        using (ISession session = this.SessionFactory.OpenSession())
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                //the idea here is to see whether there are two calls to DB here. check the sql output
                AirportDAO dao = new AirportDAO(session);
                secondAirport = dao.GetAirport();
                transaction.Commit();
            }
        }

        Console.WriteLine("Are those the same airport instance? " + firstAirport.Equals(secondAirport));

        Console.WriteLine("now adding a terminal");
        using (ISession session = this.SessionFactory.OpenSession())
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                secondAirport.Terminals.Add(new Terminal() { Name = "terminal added to second airport", Zones = new List<Zone>() });
                session.Update(secondAirport);
                transaction.Commit();
            }
        }
        //this Assert fails, since firstAirport != secondAirport
        Assert.IsNotNull(firstAirport.Terminals.FirstOrDefault(t => t.Name.Contains("second airport")));
    }

查看结果输出:

先选择
NHibernate:选择 airport0_.Id 作为 Id36_0_,airport0_.Name 作为 Name36_0_,airport0_.IsDeleted 作为 IsDeleted36_0_ FROM dbo.[Airport] airport0_ WHERE airport0_.Id=@p0;@p0 = 1

NHibernate:选择 terminal0_.Airport_id 作为 Airport4_1_,terminal0_.Id 作为 Id1_,terminal0_.Id 作为 Id50_0_,terminal0_.Name 作为 Name50_0_,terminal0_.IsDeleted 作为 IsDeleted50_0_,terminal0_.Airport_id 作为 Airport4_50_0_ FROM dbo.[Terminal] terminal0_ WHERE terminal0_.Airport_id= @p0;@p0 = 1

NHibernate:选择 zone0_.Terminal_id 作为 Terminal4_1_,zone0_.Id 作为 Id1_,zones0_.Id 作为 Id51_0_,zones0_.Name 作为 Name51_0_,zones0_.IsDeleted 作为 IsDeleted51_0_,zones0_.Terminal_id 作为 Terminal4_51_0_ FROM dbo.[Zone] zone0_ WHERE zone0_.Terminal_id= @p0;@p0 = 2


第二次选择
这些是同一个机场实例吗?假的

现在添加一个终端
NHibernate:从 dbo._uniqueKey 中选择 next_hi,使用 (updlock, rowlock)
NHibernate:更新 dbo._uniqueKey 设置 next_hi = @p0 where next_hi = @p1;@p0 = 17, @p1 = 16

NHibernate: INSERT INTO dbo.[Terminal] (Name, IsDeleted, Airport_id, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = '终端添加到第二个机场', @p1 = False, @ p2 = NULL,@p3 = 16
NHibernate: UPDATE dbo.[Airport] SET Name = @p0, IsDeleted = @p1 WHERE Id = @p2;@p0 = '测试机场', @p1 = False, @p2 = 1

NHibernate: UPDATE dbo.[Terminal] SET Name = @p0, IsDeleted = @p1, Airport_id = @p2 WHERE Id = @p3;@p0 = '测试终端', @p1 = False, @p2 = 1, @p3 = 2

NHibernate: UPDATE dbo.[Zone] SET Name = @p0, IsDeleted = @p1, Terminal_id = @p2 WHERE Id = @p3;@p0 = '测试区', @p1 = False, @p2 = 2, @p3 = 3

NHibernate: UPDATE dbo.[Terminal] SET Airport_id = @p0 WHERE Id = @p1;@p0 = 1, @p1 = 16



我的问题是:
1. 更新所有内容的奇怪更新行为...
2. firstAirport 和 secondAirport 不是同一个对象的事实(也许我遗漏了一些关于二级缓存的东西?)

在此先感谢,
强尼

【问题讨论】:

    标签: nhibernate second-level-cache


    【解决方案1】:

    firstAirport 和 secondAirport 不是同一个对象,这是因为默认情况下比较引用类型的引用相等性。

    由于您使用单独的 NHibernate ISession 加载 firstAirport 和 secondAirport,因此没有使用缓存,因为 session1 不知道 session2 的任何信息,反之亦然。
    由 NHibernate 的 Session 实现的 'Identity' 模式的范围仅限于该 session offcourse。

    您可以通过正确覆盖 Equals 和 GetHashcode 方法来覆盖此行为。 您可以重写 Equals 方法,以便根据例如机场的“Id”来确定相等性。

    奇怪的更新行为是由于您在另一个会话中更新对象,然后是从中检索它的会话。 您应该将 ISEssion 视为一个 UnitOfWork。因此,最好在同一会话中加载对象并保存对象,而不是在各自的会话中执行这些操作。 (您也可以通过将现有机场对象“锁定”到您用于执行更新的会话中来解决此问题。

    using (ISession session = this.SessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
    
                    session.Lock (secondAirport, LockMode.None);
    
                    secondAirport.Terminals.Add(new Terminal() { Name = "terminal added to second airport", Zones = new List<Zone>() });
                    session.Update(secondAirport);
                    transaction.Commit();
                }
            }
    

    【讨论】:

    • 谢谢!这似乎有效。另外,关于 firstAirport != secondAirport,我的目标是查看一个会话是否更新机场,另一个会话是否立即看到它(不调用 Get())。我现在看到这是一个非常愚蠢的假设。显然,如果我想查看不同会话对对象所做的更改,我必须再次加载该对象。
    • 另外,查看 Ayende 的解决方案,使用 Merge()-ayende.com/Blog/archive/2009/11/08/…
    猜你喜欢
    • 2017-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    相关资源
    最近更新 更多