0

我的域是一个机场,其中包含多个航站楼,每个航站楼都包含区域等。
由于机场/航站楼/区域实体的数量非常少,我想:
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: SELECT airport0_.Id as Id36_0_, airport0_.Name as Name36_0_, airport0_.IsDeleted as IsDeleted36_0_ FROM dbo.[Airport] airport0_ WHERE airport0_.Id=@p0;@p0 = 1

NHibernate: SELECT terminal0_.Airport_id as 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


秒选择
这些是同一个机场实例吗?False

现在添加一个终端
NHibernate: select next_hi from dbo._uniqueKey with (updlock, rowlock)
NHibernate: update dbo._uniqueKey set 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 = 'test airport', @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 不是同一个对象的事实(也许我错过了关于二级缓存的一些东西?)

提前谢谢,
Jhonny

4

1 回答 1

1

firstAirport 和 secondAirport 不是同一个对象的事实是由于默认情况下比较引用类型的引用相等性这一事实。

由于您使用单独的 NHibernate ISession 加载 firstAirport 和 secondAirport,因此未使用缓存,因为 session1 对 session2 一无所知,反之亦然。
NHibernate 的 Session 实现的“身份”模式仅限于该会话的课程。

您可以通过正确覆盖 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();
            }
        }
于 2010-12-15T09:35:08.847 回答