nhibernate 多表关联查询之多对多查询之经验分享

学习了一段时间nhibernate,始终有一个问题困扰,就是多表关联查询的hql语句问题,今日终于解决了,先分享经验

 

 

NHibernate关系之——多对多
多对多关系

废话不多说,看图:


这里我们主要是要解决Order表和Product表之间的多对多的关系,相信有过数据库设计经验的人是很容易理解的,OrderProduct是一个关系表,而Order和Product表都是实体表,在数据库中多对多的的实体关系都通过在两个实体间增加一个关系表来解决,如下:

 

 

通过在NHibernate中配置Order和Product之间的关系,我们可以直接还原这两个表之间本来的关系(比如:我们可以这样访问order.products.count来获取某个订单下有多少个产品,或者product.orders.count来查看某个产品有多少个订单是定了的,这样是不是很符合我们的思维,更有助于我们对业务的理解),而如果使用SQL的话,我们必须通过它们之间的关系表来获取他们之间的关系,配置后这些都可以交由NHibernate来自动完成了。

 

一、编写Product表对应的实体类和hbm映射文件

Product.cs代码如下:

namespace Model.Entities

{

    public class Product

    {

        public virtual Int32 ProductId { get; set; }

        public virtual string Name { get; set; }

        public virtual decimal Cost { get; set; }

    }

}

Product.hbm.xml代码如下:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">

  <class name="Model.Entities.Product,Model" table="Product">

    <id name="ProductId" column="ProductId" type="Int32"  unsaved-value="0">

      <generator class="native"/>

    </id>

    <property name="Name" column="Name" type="string" length="50" not-null="true"/>

    <property name="Cost" column="Cost" type="decimal" not-null="true"/>

  </class>

</hibernate-mapping>

 

二、修改Order表对应的实体类和hbm映射文件

Order.cs修改如下:

namespace Model.Entities

{

    public class Order

    {

        public virtual Int32 OrderId { get; set; }

        public virtual DateTime OrderDate { get; set; }

        //public virtual Int32 Customer { get; set; }

        //一个order是属于一个customer的

        public virtual Customer Customer { get; set; }

        //多对多一个Order可以拥有多个Products

        public virtual IList<Product> Products { get; set; }

    }

}

Order.hbm.xml修改如下:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">

  <!--注意:下面的table="[Order]"属性,这是因为Order是sql中的关键字,如果不用[]括起来,将会在执行时报错,大家在到时可以试试-->

  <class name="Model.Entities.Order,Model" table="[Order]">

    <id name="OrderId" column="OrderId" type="Int32" unsaved-value="0">

      <generator class="native"/>

    </id>

    <property name="OrderDate" column ="OrderDate" type="DateTime" not-null="false"></property>

    <!--同样请注释掉Customer属性-->

    <!--<property name="Customer" column="Customer" type="Int32" not-null="false"></property>-->

   

    <!--一个order属于一个Customer-->

<many-to-one name="Customer" column="Customer" not-null="true" class="Model.Entities.Customer,Model" foreign-key="FK_CustomerOrders"/>

 

    <!--多对多,一个Order有多个Products,这里配置的意思是,OrderProduct表中一个key字段即[Order]字段对应多个Product-->

    <bag name="Products" generic="true" table="OrderProduct"><!—name_对应Order.cs中的Products属性、table_关系表-->

      <key column="[Order]" foreign-key="FK_OrderProducts"/><!—column_OrderProduct表的字段_外键、foreign-key_关系名称,FK_是必须的-->

      <many-to-many column="Product" class="Model.Entities.Product,Model" foreign-key="FK_ProductOrders"/><!—column_OrderProduct表的字段_外键-->

    </bag>

  </class>

</hibernate-mapping>

 

三、修改Product表对应的实体类和hbm映射文件

Product.cs修改如下:

namespace Model.Entities

{

    public class Product

    {

        public virtual Int32 ProductId { get; set; }

        public virtual string Name { get; set; }

        public virtual decimal Cost { get; set; }

        //多对多一个Product可以属于多个Orders

        public virtual IList<Order> Orders { get; set; }

    }

}

Product.hbm.xml修改如下:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">

  <class name="Model.Entities.Product,Model" table="Product">

    <id name="ProductId" column="ProductId" type="Int32"  unsaved-value="0">

      <generator class="native"/>

    </id>

    <property name="Name" column="Name" type="string" length="50" not-null="true"/>

    <property name="Cost" column="Cost" type="decimal" not-null="true"/>

 

    <!--多对多,解释可以参考上面Order.hbm.xml的注释-->

    <bag name="Orders" generic="true" table="OrderProduct">

      <key column="Product" foreign-key="FK_ProductOrders"/>

      <many-to-many column="[Order]" class="Model.Entities.Order,Model" foreign-key="FK_OrderProducts"/>

    </bag>

  </class>

</hibernate-mapping>

 

四、为相关表添加关联数据

数据可以由自己添加,这里提供一个参考:

 

 


五、编写DAL(数据访问层、持久层)方法来关联查询Customer、Order、OrderProduct和Product表

方法可以放在旧有的文件里面,也可以放在新建的文件里面,具体参考前面的DAL层的相关文件,代码如下:

        //关联查询——多对多

        public IList<Customer> GetCustomersWithOrdersAndProducts(DateTime orderDate)

        {

            IList<Customer> customers = null;

 

            //用SQL查询

            //customers = session

            //    .CreateSQLQuery("select distinct customer.* from Customer customer inner join [Order] o on o.Customer=customer.CustomerId inner join OrderProduct op on o.OrderId=op.[Order] inner join Product p on op.Product=p.ProductId where o.OrderDate< :orderDate")

            //    .AddEntity("customer", typeof(Customer))

            //    .SetDateTime("orderDate", orderDate)

            //    .List<Customer>();

 

            /*因为在映射文件已经定义实体之间一对多、多对多关系,NHibernate通过映射文件知道如何去关联这些实体,我们不需要在查询

            语句中重复定义。这里使用查询和上一篇使用HQL关联查询语句一样,NHibernate完全可以去关联对象,实现查询订单和产品。

这句话来自:http://www.cnblogs.com/lyj/archive/2008/10/27/1320764.html,不过我没看明白,先继续吧,以后再看看*/

            //用HQL查询

            customers = session

                .CreateQuery("select distinct c from Customer c inner join c.Orders o where o.OrderDate<=:orderDate")

                .SetDateTime("orderDate", orderDate)

                .List<Customer>();

 

            return customers;

        }

注意:上面的方法中提供了两种查询方式,一种是SQL查询、一种是HQL查询,当你使用其中一种时,请注释掉另外一种,以确保测试的准确性;

 

六、编写DAL.Test项目中的测试方法

方法可以放在旧有的文件里面,也可以放在新建的文件里面,具体参考前面的DAL.Test层的相关文件,代码如下:

        [Test]

        public void GetCustomers()

        {

            IList<Customer> customers = sample.GetCustomersWithOrdersAndProducts(DateTime.Now);

        }

在函数中设置一个断点,然后在“即时窗口”输入代码如下:

 

 

要访问Customer的属性就OK但是要访问Order的熟悉就只能访问Count,其它的属性不能访问。

 

七、小宇宙爆发——访问关联对象及其属性

上面的截图我们可以看到,这里无法通过customer关联访问其order对象,这是因为我不会用ISet集合,好吧!既然我不会用就改用IList吧,不过要做小小的修改,如下:

Customer.cs修改如下:

    //注意,实体类必须为public,否则无法访问

    public class Customer

    {

        //在NHibernate的实体类中,所有的公共方法、属性和事件都必须使用virtual修饰

        public virtual int CustomerId { get; set; }

        public virtual string Firstname { get; set; }

        public virtual string Lastname { get; set; }

        //一个Customer有多个Order

        //public virtual ISet<Order> Orders { get; set; }  //ISet在映射文件中对应Set配置节

        public virtual IList<Order> Orders { get; set; }  //IList在映射文件中对应bag配置节

    }

Customer.hbm.xml修改如下:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">

  <class name="Model.Entities.Customer,Model" table="Customer">

    <id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0">

      <generator class="native"></generator>

    </id>

   

    <property name="Firstname" column="Firstname" type="string" length="50" not-null="false"></property>

    <property name="Lastname" column="Lastname" type="string" length="50" not-null="false"></property>

   

    <!--一个Customer对应多个order-->

    <!--<set name="Orders" table="[Order]" generic="true" inverse="true">

      <key column="Customer" foreign-key="FK_CustomerOrders"/>

      <one-to-many class="Model.Entities.Order,Model"/>

    </set>-->

    <bag name="Orders" table="[Order]" generic="true" inverse="true">

      <key column="Customer" foreign-key="FK_CustomerOrders"/>

      <one-to-many class="Model.Entities.Order,Model"/>

    </bag>

  </class>

</hibernate-mapping>

 

在函数GetCustomers()中设置一个断点,然后在“即时窗口”输入代码如下:

 

 

从上面的代码我们可以看到,我们可以随意的访问关联对象及其属性,hoho~~还是IList好用。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/soldierluo/archive/2009/10/20/4701872.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微恒软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值