• NHibernate 备忘(一) 级联问题


     在用NHibernate碰到一个级联操作问题,在各位大侠的帮助下总算是给搞清楚了,好了废话不说,看问题。

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

     1     public class Customer
     2     {
     3         ISet<Order> _orders = new HashedSet<Order>();
     4 
     5         public virtual int CustomerID { getset; }
     6         public virtual string CustomerName { getset; }
     7         public virtual ISet<Order> Orders
     8         {
     9             get
    10             {
    11                 return _orders;
    12             }
    13             set
    14             {
    15                 _orders = value;
    16             }
    17         }
    18 
    19         /// <summary>
    20         /// 添加一个订单
    21         /// </summary>
    22         /// <param name="o"></param>
    23         /// <returns></returns>
    24         public virtual int AddOrder(Order o)
    25         {
    26             Orders.Add(o);
    27             o.Customer = this;
    28             return Orders.Count;
    29         }
    30 
    31         /// <summary>
    32         /// 移除一个订单
    33         /// </summary>
    34         /// <param name="o"></param>
    35         /// <returns></returns>
    36         public virtual int RemoveOrder(Order o)
    37         {
    38             Orders.Remove(o);
    39             return Orders.Count;
    40         }
    41     }
    42 
    43     /// <summary>
    44     /// 订单
    45     /// </summary>
    46     public class Order
    47     {
    48         public virtual int OrderID { getset; }
    49         public virtual int ProductNum { getset; }
    50         public virtual Customer Customer { getset; }
    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        }

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

     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生成的语句:

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

     看看NH生成的代码:

     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语句
    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?

    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来做,别跟我一样闷着头浪费很长时间。

     结论:inverse 表明的是关联的方向,设置为 true 的被认为是关系的反向端, 设置为 false的被认为是关系的正向,从这个比较中可以看出,对映射关系做修 改(新增、删除)必须在主映射端完成,否则无法保存到数据库中。因此,在双向的one-to-many和many-to-many映射中,必须清楚哪个主映射端、哪个是反向端。

     没什么内容,就是一点学习积累。第一次发文章,请拍砖! 

     源码下载 ,欢迎转载,但请注明出处 NHibernate 备忘(一) 级联问题


    作者:燕之威 
    出处:http://www.cnblogs.com/smartcoder
    本文采用知识共享Attribution-NonCommercial-ShareAlike 2.5 China Mainland许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    80岁也要写程序
  • 相关阅读:
    快速排序
    冒泡排序
    mysql 拷贝表插入新的表
    http协议
    nginx错误日志error_log日志级别
    MySQL数据库远程访问的权限
    mysql create database 指定utf-8编码
    MYSQL日志
    linux常用命令
    java学习--基础知识进阶第六天--集合&迭代器、增强for & 泛型、常见数据结构、List子体系
  • 原文地址:https://www.cnblogs.com/smartcoder/p/1739363.html
Copyright © 2020-2023  润新知