ITPub博客

首页 > Linux操作系统 > Linux操作系统 > NHibernate 备忘-- 级联问题 (转)

NHibernate 备忘-- 级联问题 (转)

原创 Linux操作系统 作者:iDotNetSpace 时间:2010-05-20 16:31:51 0 删除 编辑
在用NHibernate碰到一个级联操作问题,在各位大侠的帮助下总算是给搞清楚了,好了废话不说,看问题。

 Customer对多个Order,典型的一对多关系,代码如下,不多讲。

<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1     public class Customer
 2     {
 3         ISet<Order> _orders = new HashedSet<Order>();
 4 
 5         public virtual int CustomerID { get; set; }
 6         public virtual string CustomerName { get; set; }
 7         public virtual ISet<Order> Orders
 8         {
 9             get
10             {
11                 return _orders;
12             }
13             set
14             {
15                 _orders = value;
16             }
17         }
18 
19         /// 
20         /// 添加一个订单
21         /// 

22         /// 
23         /// 
24         public virtual int AddOrder(Order o)
25         {
26             Orders.Add(o);
27             o.Customer = this;
28             return Orders.Count;
29         }
30 
31         /// 
32         /// 移除一个订单
33         /// 

34         /// 
35         /// 
36         public virtual int RemoveOrder(Order o)
37         {
38             Orders.Remove(o);
39             return Orders.Count;
40         }
41     }
42 
43     /// 
44     /// 订单
45     /// 

46     public class Order
47     {
48         public virtual int OrderID { get; set; }
49         public virtual int ProductNum { get; set; }
50         public virtual Customer Customer { get; set; }
51     }

      刚开始的时候,我设置 inverse="false",在保存Customer的时候,NH自动为我保存了Order,感觉很方便,后来删除Customer的时候,引发了一个异常,说是外键不允许为空,很奇怪,我删除数据怎么会让外键为空?后来发现了问题:没看到NH输出delete语句,只看到有两条将外键更新为空的update语句,但是奇怪的是数据却被删掉了,为了搞清楚这个问题,于是有了下文。后来在李永京的建议下,使用NHProfiler监视,发现了NH生成的delete语句,看来是testDriver.Net的问题,漏掉了某些输出的sql语句。

      关于级联删除,nh在级联删除的时候总是先Update,然后在delete,这是因为inverse属性设置的原因,我们着重看看这个属性。

      我们先让inverse默认为false,使用如下代码创建Customer。

 1        [Test] 

 2        public  void CreateCustomer()
 3        {
 4            Customer cus =new Customer();
 5            cus.CustomerName ="test";
 6            cus.Orders.Add(new Order { ProductNum =1 });
 7            cus.Orders.Add(new Order { ProductNum =2 });

 8            NHibernateHelper.EncloseInTransaction(s =>

 9                {
10                    s.Save(cus);
11                });
12        }

 第四行是一个自定义的执行事务的方法,直接粘贴李永京写的。

<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1          public static void EncloseInTransaction(Action<ISession> work)
 2          {
 3             if (sessionFactory == null)
 4             {
 5                 Configuration cfg = new Configuration();
 6                 cfg = cfg.Configure();
 7                 sessionFactory = cfg.BuildSessionFactory();
 8             }
 9             using (var s = sessionFactory.OpenSession())
10             {
11                 using (var tx = s.BeginTransaction())
12                 {
13                     try
14                     {
15                         work(s);
16                         tx.Commit();
17                     }
18                     catch
19                     {
20                         tx.Rollback();
21                         throw;
22                     }
23                 }
24             }
25         }

      注意,这里我们直接使用ISet的Add方法加入订单,我们看看NH生成的语句:

<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->1 NHibernate: INSERT INTO [Customer] (CustomerName) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'test'
2 NHibernate: INSERT INTO [Order] (ProductNum, CustomerID) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 1, @p1 = NULL
3 NHibernate: INSERT INTO [Order] (ProductNum, CustomerID) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 2, @p1 = NULL
4 NHibernate: Batch commands:
5 command 0:UPDATE [Order] SET CustomerID = @p0 WHERE OrderID = @p1;@p0 = 6, @p1 = 11
6 command 1:UPDATE [Order] SET CustomerID = @p0 WHERE OrderID = @p1;@p0 = 6, @p1 = 12

      有什么感觉?先插入为两条外键为空的记录,然后在用update更新外键,很别扭吧,如果你的数据库外键不允许为空,还会引发异常。先别急,接着我们看看删除。用以下代码删除一个客户 : 

1[Test]     

2public void DeleteCustomer()
3{
4    NHibernateHelper.EncloseInTransaction(s =>

5    {
6     Customer cus = s.Load<Customer>(1);
7     s.Delete(cus);
8    });
9}

 看看NH生成的代码:

<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 NHibernate: SELECT customer0_.CustomerID as CustomerID1_0_, customer0_.CustomerName as Customer2_1_0_ FROM [Customer] customer0_ WHERE customer0_.CustomerID=@p0;@p0 = 2
 2 NHibernate: SELECT orders0_.CustomerID as CustomerID1_, orders0_.OrderID as OrderID1_, orders0_.OrderID as OrderID0_0_, orders0_.ProductNum as ProductNum0_0_, orders0_.CustomerID as CustomerID0_0_ FROM [Order] orders0_ WHERE orders0_.CustomerID=@p0;@p0 = 2
 3 NHibernate: UPDATE [Order] SET CustomerID = null WHERE CustomerID = @p0;@p0 = 2
 4 NHibernate: Batch commands:
 5 command 0:DELETE FROM [Order] WHERE OrderID = @p0;@p0 = 3
 6 command 1:DELETE FROM [Order] WHERE OrderID = @p0;@p0 = 4
 7 
 8 NHibernate: Batch commands:
 9 command 0:DELETE FROM [Customer] WHERE CustomerID = @p0;@p0 = 2
10 

      我们注意第三行的那个语句,发现什么了?原来NH先把子对象的外键置空,然后才去删除子对象,最后才去删除父对象。 这显然不是我们想要的,在插入的时候,我们希望是三条insert,删除的时候能不能不要那个update,有的时候外键不允许为空还会引发异常!

     当设置inverse="true时,我们应该使用 Customer的AddOrder方法(代码见Customer实体类)来添加订单,如果你还是直接调用Orders属性的话,那么订单对象的Customer属性将会为空,那么插入到数据库中后外键也是空(可以把后面的代码下载下来改一下试试)! 我们修改一下创建客户的代码:

 1        [Test]   

 2        public void CreateCustomer()
 3        {
 4            Customer cus =new Customer();
 5            cus.CustomerName ="test";
 6            cus.AddOrder(new Order { ProductNum =1 });
 7            cus.AddOrder(new Order { ProductNum =2 });

 8            NHibernateHelper.EncloseInTransaction(s =>10                {
 9                    s.Save(cus);
10                });
11        }

   注意第六第七行,运行测试,我们看看输出的sql语句,正式我们想要的形式了。

当 inverse="true"时输出的sql语句
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->NHibernate: INSERT INTO [Customer] (CustomerName) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'test'
NHibernate: INSERT INTO [Order] (ProductNum, CustomerID) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 1, @p1 = 4
NHibernate: INSERT INTO [Order] (ProductNum, CustomerID) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 2, @p1 = 4

我们在看看删除输出的sql语句,是不是没有那个update?

<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1 NHibernate: SELECT customer0_.CustomerID as CustomerID1_0_, customer0_.CustomerName as Customer2_1_0_ FROM [Customer] customer0_ WHERE customer0_.CustomerID=@p0;@p0 = 3
2 NHibernate: SELECT orders0_.CustomerID as CustomerID1_, orders0_.OrderID as OrderID1_, orders0_.OrderID as OrderID0_0_, orders0_.ProductNum as ProductNum0_0_, orders0_.CustomerID as CustomerID0_0_ FROM [Order] orders0_ WHERE orders0_.CustomerID=@p0;@p0 = 3
3 NHibernate: Batch commands:
4 command 0:DELETE FROM [Order] WHERE OrderID = @p0;@p0 = 5
5 command 1:DELETE FROM [Order] WHERE OrderID = @p0;@p0 = 6
6 
7 NHibernate: Batch commands:
8 command 0:DELETE FROM [Customer] WHERE CustomerID = @p0;@p0 = 3

 讲到这里,这个inverse="true"算是清楚了,另外就是提醒下大家在分析NH性能的时候,最好使用NHProfiler来做,别跟我一样闷着头浪费很长时间。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/12639172/viewspace-663222/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2008-01-04

  • 博文量
    2376
  • 访问量
    5766206