zoukankan      html  css  js  c++  java
  • NHibernate之(11):探索多对多关系及其关联查询

    本节内容

    • 多对多关系引入
    • 多对多映射关系
    • 多对多关联查询
      • 1.原生SQL关联查询
      • 2.HQL关联查询
      • 3.Criteria API关联查询
    • 结语

      返回文章列表

    多对多关系引入

    让我们再次回顾在第二篇中建立的数据模型:

    数据关系模型

    在图上,我已经清晰的标注了表之间的关系,上两篇分析Customer和Order之间的“外键关系”或者称作“父子关系”、“一对多关系”和关联查询,这一篇以Order为中心,分析Order和Product之间的关系,直接看下面一幅图的两张表:

    多对多关系

    上面两张表关系表达的意思是:Order有多个Products,Product属于多个Orders。我们称Order和Product是多对多关系,这一篇我们来深入讨论在NHibernate如何映射多对多关系及其多对多关联查询。

    多对多映射关系

    1.Order有多个Products

    有了上两篇的基础,现在直奔主题,建立关系。大家可以把这篇的代码作为模板,以后在工作中参考。

    修改Order.cs类代码如下:

    namespace NHibernateOper.DomainModel
    {
    
        public class Order
        {
            public virtual int OrderId { get; set; }
            public virtual DateTime OrderDate { get; set; }
            //多对一关系:Orders属于一个Customer
            public virtual Customer Customer { get; set; }
            //多对多关系:Order有多个Products
            public virtual IList<Product> Products { get; set; }
        }
    }

    修改Order.hbm.xml映射文件如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                       assembly="NHibernateOper.DomainModel" namespace="NHibernateOper.DomainModel">
    
        <class name="NHibernateOper.DomainModel.Order,NHibernateOper.DomainModel" 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="true" />
            <!--多对一关系:Orders属于一个Customer-->
            <!--Customer必须与Order类中的 Customer字段保持一致 -->
            <!--column="CustomerId" 用于定义数据库Order表中的字段名称-->
            <many-to-one name="Customer" column="CustomerId" not-null="true"
                         class="NHibernateOper.DomainModel.Customer,NHibernateOper.DomainModel"
                         foreign-key="FK_CustomerOrders"/>
            <bag name="Products" generic="true" table="OrderProduct">
                <key column="OrderId" foreign-key="FK_OrderProducts"/>
                <many-to-many column="ProductId" class="NHibernateOper.DomainModel.Product,NHibernateOper.DomainModel"
                               foreign-key="FK_ProductOrders" />
    
            </bag>
        </class>
    </hibernate-mapping>

    在多对多关系中,其两方都使用Bag集合和many-to-many元素。看看上面各个属性和one-to-many,many-to-one属性差不多。

    2.Product属于多个Orders

    在项目NHibernateOper.DomainModel 新建Product.cs类,编写代码如下:

    namespace NHibernateOper.DomainModel
    {
        public class Product
        {
            public virtual int ProductId { get; set; }
            public virtual string Name { get; set; }
            public virtual float Cost { get; set; }
            //多对多关系:Product属于多个Orders
            public virtual IList<Order> Orders { get; set; }
        }
    }

    在项目DomainModel层的Mappings文件夹中新建Product.hbm.xml映射文件,编写代码如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                       assembly="NHibernateOper.DomainModel" namespace="NHibernateOper.DomainModel">
        <class name="NHibernateOper.DomainModel.Product,NHibernateOper.DomainModel" table="Product">
    
            <id name="ProductId" column ="ProductId" type="Int32" unsaved-value="0">
                <generator class="native"/>
            </id>
            <property name="Name" column="Name" type="string" not-null="true" length="50"/>
            <property name="Cost" column="Cost" type="float" not-null="true"/>
            <!--多对多关系:Product属于多个Orders-->
            <bag name="Orders" generic="true" table="OrderProduct">
                <key column="ProductId" foreign-key="FK_ProductOrders"/>
                <many-to-many column="OrderId"
                            class="NHibernateOper.DomainModel.Order,NHibernateOper.DomainModel"
                            foreign-key="FK_OrderProducts"/>
            </bag>
        </class>
    </hibernate-mapping>

    多对多关联查询

    使用NHibernate中提供的三种查询方法实现多对多关联查询,查询返回所有订单和产品的顾客列表。

    1.原生SQL关联查询

    public IList<Customer> UseSQL_GetCustomersWithOrdersHavingProduct(DateTime orderDate)
    {
        return _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>();
    }

    这里需要使用Join告诉查询如何在表之间关联。无论多么复杂的关系,我们必须在查询语句中指定返回值。这里使用AddEntity设置返回的实体。

    2.HQL关联查询

            public IList<Customer> UseHQL_GetCustomersWithOrdersHavingProduct(DateTime orderDate)
            {
                return _session.CreateQuery("select distinct c from Customer c "
                    + "inner join c.Orders o where o.OrderDate > :orderDate")
                    .SetDateTime("orderDate", orderDate)
                    .List<Customer>();
            }

    因为在映射文件已经定义实体之间一对多、多对多关系,NHibernate通过映射文件知道如何去关联这些实体,我们不需要在查询语句中重复定义。这里使用查询和上一篇使用HQL关联查询语句一样,NHibernate完全可以去关联对象,实现查询订单和产品。

    3.Criteria API关联查询

    因为实体之间的关联我们在映射文件中已经定义好了。所以我们在查询子对象使用子CreateCriteria语句关联对象之间导航,可以很容易地在实体之间指定约束。这里第二个CreateCriteria()返回ICriteria的新实例,并指向Orders实体的元素。第三个指向Products实体的元素。

            public IList<Customer> UseCriteriaAPI_GetCustomerswithOrdersHavingProduct()
            {
                return _session.CreateCriteria(typeof(Customer))
                    .Add(Restrictions.Eq("FirstName", "YJing"))
                    .CreateCriteria("Orders")
                    .Add(Restrictions.Gt("OrderDate", new DateTime(2008, 10, 1)))
                    .CreateCriteria("Products")
                    .Add(Restrictions.Eq("Name", "Cnblogs"))
                    .List<Customer>();
            }

    下面我用一幅图简单明了的说明这段代码神秘之处,也显示了一些约束条件。

    条件查询

    编写一个测试用例测试UseCriteriaAPI_GetCustomerswithOrdersHavingProduct()方法,遍历列表,看看产品名称是否为“Cnblogs”,OK!测试通过。

    [Test]
    public void UseCriteriaAPI_GetCustomerswithOrdersHavingProductTest()
    {
        IList<Customer> customers = _relation.UseCriteriaAPI_GetCustomerswithOrdersHavingProduct();
        bool found = false;
        foreach (Customer c in customers)
        {
            foreach (Order o in c.Orders)
            {
                foreach (Product p in o.Products)
                {
                    if (p.Name == "Cnblogs")
                    {
                        found = true;
                        break;
                    }
                }
            }
        }
        Assert.IsTrue(found);
    }

    下面再写个简单例子查询产品Id所关联的一些顾客,测试用例同上面差不多,自己修改下就可以啦。

    public IList<Customer> UseCriteriaAPI_GetCustomerswithOrdersHavingProduct(int productId)
    {
        return _session.CreateCriteria(typeof(Customer))
            .CreateCriteria("Orders")
            .CreateCriteria("Products")
            .Add(Restrictions.Eq("ProductId", productId))
            .List<Customer>();
    }

    结语

    这一篇通过全盘代码的形式完成NHibernate中的多对多关系映射,使用NHibernate中提供的三种查询方法实现了多对多关联查询。希望对你有所帮助,多多练习。我们下次继续讨论NHibernate话题,像延迟加载、立即加载、对象状态等话题,关于朋友回复说讨论更多话题,我只能说,再等等吧,慢慢来,这才第十一篇,先把基础的问题弄清楚。

    NHibernate Q&A

    下次继续分享NHibernate!

  • 相关阅读:
    POJ
    hdu 5652
    CodeForces
    #6285. 数列分块入门 9
    #6284. 数列分块入门 8
    #6283. 数列分块入门 7
    #6282. 数列分块入门 6
    #6280. 数列分块入门 4 #6281. 数列分块入门 5
    #6278. 数列分块入门 2和#6278. 数列分块入门 3
    spark-sklearn TypeError: 'JavaPackage' object is not callable
  • 原文地址:https://www.cnblogs.com/a282421083/p/13444648.html
Copyright © 2011-2022 走看看