nHibernate 急切加载 - 奇怪的更新行为

发布于 2024-10-07 09:10:04 字数 4061 浏览 0 评论 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:选择 airport0_.Id 作为 Id36_0_,airport0_.Name 作为 Name36_0_,airport0_.IsDeleted 作为 IsDeleted36_0_ FROM dbo.[机场] airport0_ WHERE airport0_.Id=@p0;@p0 = 1

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

NHibernate:选择 zone0_.Terminal_id 作为 Terminal4_1_、zones0_.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:使用(updlock,rowlock)从 dbo._uniqueKey 选择 next_hi
NHibernate:更新 dbo._uniqueKey 设置 next_hi = @p0 其中 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:更新 dbo.[终端] SET Airport_id = @p0 WHERE Id = @p1;@p0 = 1,@p1 = 16



我的问题是:
1.奇怪的更新行为会更新所有内容...
2. 事实上,firstAirport 和 secondaryAirport 不是同一个对象(也许我遗漏了有关二级缓存的信息?)

提前致谢,
强尼

my domain is an airport, which contains several terminals, and each terminal contains zones etc.
since the number of airport / terminal / zone entities is very small, i'd like to:
1. load all the heirarchy eagerly when retrieving an airport.
(using the following fluent configuration:

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

)
2. enable 2nd level caching, so that all retrievals of an airport object would not hit the DB.

the eager loading and caching works fine, but the following test produces some strange behaviour.
(the following code retrieves an airport entity twice (not hitting the DB the second time), and updates one of them.)

        [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")));
    }

see the resulting output:

first select
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 terminals0_.Airport_id as Airport4_1_, terminals0_.Id as Id1_, terminals0_.Id as Id50_0_, terminals0_.Name as Name50_0_, terminals0_.IsDeleted as IsDeleted50_0_, terminals0_.Airport_id as Airport4_50_0_ FROM dbo.[Terminal] terminals0_ WHERE terminals0_.Airport_id=@p0;@p0 = 1

NHibernate: SELECT zones0_.Terminal_id as Terminal4_1_, zones0_.Id as Id1_, zones0_.Id as Id51_0_, zones0_.Name as Name51_0_, zones0_.IsDeleted as IsDeleted51_0_, zones0_.Terminal_id as Terminal4_51_0_ FROM dbo.[Zone] zones0_ WHERE zones0_.Terminal_id=@p0;@p0 = 2

second select
Are those the same airport instance? False

now adding a terminal
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 = 'terminal added to second airport', @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 = 'test terminal', @p1 = False, @p2 = 1, @p3 = 2

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

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

my problems are:
1. the strange update behaviour which updates everything...
2. the fact that firstAirport and secondAirport are not the same object (maybe I'm missing something about 2nd level cache?)

thanks in advance,
Jhonny

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

戈亓 2024-10-14 09:10:04

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

由于您使用单独的 NHibernate ISession 加载第一个和第二个Airport,因此未使用缓存,因为 session1 不知道有关 session2 的任何信息,反之亦然。
NHibernate 的会话实现的“身份”模式的范围仅限于该会话之外。

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

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

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();
            }
        }

The fact that firstAirport and secondAirport are not the same object, is due to the fact that reference types are compared for reference-equality by default.

Since you're loading first and secondAirport using separate NHibernate ISession's, the cache is not being used, since session1 doesn't know anything about session2 and vice versa.
The 'Identity' pattern that is implemented by NHibernate's Session, is scoped to that session offcourse.

You can override this behaviour by overriding the Equals and GetHashcode methods properly.
You can override the Equals method, so that equality is determined based on the 'Id' of an airport for instance.

The strange update behaviour is due to the fact that you're updating the object in another session then the session from which it was retrieved.
You should see the ISEssion as a UnitOfWork. So, it is best to load the object and save the object in the same session, instead of doing these operations each in its own session.
(You can solve this as well by 'locking' the existing airport object into the session that you're using to perform the update as well).

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();
            }
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文