目录
写在前面
上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题。这篇文章对多对多关系的查询处理也采用上篇文章的描述方式进行说明。
文档与系列文章
[NHibernate]持久化类(Persistent Classes)
[NHibernate]集合类(Collections)映射
[NHibernate]缓存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]条件查询Criteria Query
多对多关系关联查询
多对多关系非常常见,比如系统中常用的权限管理问题,一个用户有多个权限,当然一个权限可以属于多个用户(这样描述只是为了对多对多关系有个感性的认识)。那么在咱们的客户/订单/产品,这三张表中有没有多对多的关系呢?
订单可以有多个产品,一种产品可以属于多个订单(这个地方有点绕,如果再有一个数量的字段,比较好理解,下一个订单,只是库存减少了,但id还是那个id)。关系图如下:
其中表TB_OrderProduct为order和product的关系表,字段OrderID和ProductID为联合主键。
添加联合主键的sql语句如下:
1 ALTER TABLE tb_orderproduct WITH NOCHECK ADD 2 CONSTRAINT [PK_orderproduct] PRIMARY KEY NONCLUSTERED 3 ( 4 orderid, 5 productid 6 )
首先修改Order类
1 /// <summary> 2 /// 描述:订单实体,数据库持久化类 3 /// 创建人:wolfy 4 /// 创建时间:2014-10-16 5 /// </summary> 6 public class Order 7 { 8 /// <summary> 9 /// 订单id 10 /// </summary> 11 public virtual Guid OrderID { set; get; } 12 /// <summary> 13 /// 下订单时间 14 /// </summary> 15 public virtual DateTime OrderDate { set; get; } 16 /// <summary> 17 /// 下订单的客户,多对一的关系:orders对应一个客户 18 /// </summary> 19 public virtual Customer Customer { set; get; } 20 /// <summary> 21 /// 多对多关系,一个订单下可以有多个产品 22 /// </summary> 23 public virtual IList<Product> Products { set; get; } 24 }
修改Order.hbm.xml映射文件如下
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities"> 3 <class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order"> 4 <id name="OrderID" column="OrderID" type="Guid" unsaved-value="null"> 5 <generator class="assigned" /> 6 </id> 7 <property name="OrderDate" column="OrderDate" type="DateTime" 8 not-null="true" /> 9 <!--多对一关系:Orders属于一个Customer--> 10 <many-to-one name="Customer" column="CustomerID" not-null="true" 11 class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" 12 foreign-key="FK_TB_Order_TB_Customer" /> 13 <!--多对多关系:order下有多个product--> 14 <bag name="Products" generic="true" table="TB_OrderProduct"> 15 <key column="OrderID" foreign-key="FK_TB_OrderProduct_TB_Order"/> 16 <many-to-many column="ProductID" class="Wolfy.Shop.Domain.Entities.Product,Wolfy.Shop.Domain" 17 foreign-key="FK_TB_OrderProduct_TB_Product"/> 18 19 </bag> 20 </class> 21 </hibernate-mapping>
修改Product类
1 /// <summary> 2 /// 描述:商品实体,数据库持久化类 3 /// 创建人:wolfy 4 /// 创建时间:2014-10-16 5 /// </summary> 6 public class Product 7 { 8 /// <summary> 9 /// 商品id 10 /// </summary> 11 public virtual Guid ProductID { set; get; } 12 /// <summary> 13 /// 商品名称 14 /// </summary> 15 public virtual string Name { set; get; } 16 /// <summary> 17 /// 商品单价 18 /// </summary> 19 public virtual decimal Price { set; get; } 20 /// <summary> 21 /// 多对多关系:Product属于多个Orders 22 /// </summary> 23 public virtual IList<Order> Orders { get; set; } 24 25 }
Mappings文件夹中新建Product.hbm.xml映射文件
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities"> 3 <class name="Wolfy.Shop.Domain.Entities.Product,Wolfy.Shop.Domain" table="TB_Product"> 4 <id name="ProductID" column="ProductID" type="Guid" unsaved-value="null"> 5 <generator class="assigned" /> 6 </id> 7 <property name="Name" column="Name" type="String" 8 not-null="true" /> 9 <property name="Price" column="Price" type="float" 10 not-null="true" /> 11 <!--多对多关系:product属于多个orders--> 12 <bag name="Orders" generic="true" table="TB_OrderProduct"> 13 <key column="ProductID" foreign-key="FK_TB_OrderProduct_TB_Product"/> 14 <many-to-many column="OrderID" class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" 15 foreign-key="FK_TB_OrderProduct_TB_Order"/> 16 </bag> 17 </class> 18 </hibernate-mapping>
修改映射文件的属性,这个动作应该养成一个习惯,不管你写的配置文件对不对,只要添加了映射文件,就修改它的属性,如图:
通过上面对比,你会发现多对多关系many-to-many节点的配置相似,只是方向有点区别。
原生SQL查询
查询客户的所有的订单和订单下所有的产品。
CustomerData.cs
1 /// <summary> 2 /// 通过sql查询客户下的订单和产品信息 3 /// </summary> 4 /// <param name="cutomerID"></param> 5 /// <returns></returns> 6 public IList<Customer> GetCustomerOrderProductsBySQL(Guid cutomerID) 7 { 8 //获得ISession实例 9 ISession session = NHibernateHelper.GetSession(); 10 //使用inner join关联多个表进行查询 11 return session.CreateSQLQuery("select distinct c.* from TB_Customer c" + 12 " inner join TB_Order o on o.CustomerID=c.CustomerID" + 13 " inner join TB_OrderProduct op on o.OrderID=op.OrderID" + 14 " inner join TB_Product p on op.ProductID=p.ProductID where c.CustomerID=:CustomerID") 15 //使用AddEntity设置返回的实体。 16 .AddEntity("Customer", typeof(Customer)) 17 .SetGuid("CustomerID", cutomerID) 18 .List<Customer>(); 19 }
生成的sql语句
HQL查询
查询客户的所有的订单和订单下所有的产品。
CustomerData.cs
1 /// <summary> 2 /// 通过HQL查询客户下的订单和产品信息 3 /// </summary> 4 /// <param name="cutomerID"></param> 5 /// <returns></returns> 6 public IList<Customer> GetCustomerOrderProductsByHQL(Guid cutomerID) 7 { 8 //获得ISession实例 9 ISession session = NHibernateHelper.GetSession(); 10 //使用HQL基于面向对象的查询方式 11 return session.CreateQuery("select distinct c from Customer c inner join c.Orders o inner join o.Products where c.CustomerID=:CustomerID") 12 .SetGuid("CustomerID", cutomerID) 13 .List<Customer>(); 14 }
生成的sql语句
通过查看sql及HQL语句可以知道,c.Orders o inner join o.Products 内部为我们查询了所有Order下的所有产品,你会发现我们的查询中并没有出现表TB_OrderProduct,而在sql语句中出现了该表,因为这些信息在映射文件已经描述了。nhibernate通过映射文件,知道如何关联,该关联那张数据表。
public IList<Customer> UseHQL_GetCustomersWithOrdersHavingProduct(DateTime orderDate)
{
return _session.CreateQuery("select distinct c from Customer c ," + " c.Orders.elements o where o.OrderDate > :orderDate") .SetDateTime("orderDate", orderDate) .List<Customer>(); }
上面这段代码是@李永京文章中的,这种方式也尝试了c.Orders.elements,总是报以下异常:
NHibernate.Hql.Ast.ANTLR.QuerySyntaxException : c.Orders.elements is not mapped [select distinct c from Customer c , c.Orders.elements o where o.OrderDate > :orderDate]
也许,大概elements在nhibernate 4.0版本中废除了吧,最后没办法,就通过我上面写的那种方式实现了HQL查询,毕竟条条大路通罗马,不能在一条路上吊死吧。
Criteria API关联查询
通过HQL方式,我们已经知道了实体间的关系已经在映射文件中定义好了,所以我们在查询子对象使用子CreateCriteria语句关联对象之间导航,可以很容易地在实体之间指定约束。这里第二个CreateCriteria()返回ICriteria的新实例,并指向Orders实体的元素。第三个指向Products实体的元素。
查询客户的所有的订单和订单下所有的产品。
CustomerData.cs
1 /// <summary> 2 /// 通过HQL查询客户下的订单和产品信息 3 /// </summary> 4 /// <param name="cutomerID"></param> 5 /// <returns></returns> 6 public IList<Customer> GetCustomerOrderProductsByCriteriaAPI(Guid cutomerID) 7 { 8 //获得ISession实例 9 ISession session = NHibernateHelper.GetSession(); 10 return session.CreateCriteria(typeof(Customer)) 11 .Add(Restrictions.Eq("CustomerID", cutomerID)) 12 .CreateCriteria("Orders") 13 .CreateCriteria("Products") 14 .List<Customer>(); 15 }
生成的sql语句
此时查询出来的数据有两条,因为有两个产品,没有对其进行去重。
测试数据如下:
TB_Customer表
TB_Order表
TB_OrderProduct表
TB_Product表
总结
在学习多对多关联查询时,在c.Orders.elements的地方卡在那个地方了,这块还需要在查一查nhibernate版本之间是不是有差异。被一个问题,折腾到现在,本来打算十点半睡觉的(每天最晚十点半上床睡觉),我基本是不熬夜的,有点强迫症,不想带着问题睡觉,很晚了,就写到这儿吧。
参考地址:http://www.cnblogs.com/lyj/archive/2008/10/27/1320764.html#!comments