首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

NHibernate實戰(11):多對多關係映射與關聯查詢全攻略

     編輯:關於.NET

前言

承接一對多關係,本文以 Order-Product 為核心,完整演示 NHibernate 中多對多關係的雙向映射配置,並展示原生 SQL、HQL、Criteria API 三種關聯查詢方式的實戰代碼。

多對多關係建模

Order 與 Product 關係:一個訂單包含多個產品,一個產品屬於多個訂單。中間表 OrderProduct 維護關聯。

映射配置全解析

Order 端映射(一邊)

public class Order
{
    public virtual int OrderId { get; set; }
    public virtual DateTime OrderDate { get; set; }
    public virtual Customer Customer { get; set; }      // 多對一
    public virtual IList<Product> Products { get; set; } // 多對多
}
<class name="DomainModel.Entities.Order,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" />
    
    <!-- 多對一:訂單屬於客戶 -->
    <many-to-one name="Customer" column="Customer" not-null="true"
        class="DomainModel.Entities.Customer,DomainModel"
        foreign-key="FK_CustomerOrders" />
    
    <!-- 多對多:訂單包含產品 -->
    <bag name="Products" generic="true" table="OrderProduct">
        <key column="`Order`" foreign-key="FK_OrderProducts"/>
        <many-to-many column="Product"
            class="DomainModel.Entities.Product,DomainModel"
            foreign-key="FK_ProductOrders"/>
    </bag>
</class>

Product 端映射(另一邊)

public class Product
{
    public virtual int ProductId { get; set; }
    public virtual string Name { get; set; }
    public virtual float Cost { get; set; }
    public virtual IList<Order> Orders { get; set; } // 多對多
}
<class name="DomainModel.Entities.Product,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"/>
    
    <!-- 多對多:產品屬於訂單 -->
    <bag name="Orders" generic="true" table="OrderProduct">
        <key column="Product" foreign-key="FK_ProductOrders"/>
        <many-to-many column="`Order`"
            class="DomainModel.Entities.Order,DomainModel"
            foreign-key="FK_OrderProducts"/>
    </bag>
</class>

關鍵點解析

  • 雙向均使用 <bag> + <many-to-many>
  • table="OrderProduct" 指定中間表
  • key column 為本端外鍵欄位,many-to-many column 為對端外鍵
  • foreign-key 屬性供 SchemaExport 生成 DDL 使用

三種關聯查詢方式實戰

查詢目標:獲取擁有特定產品訂單的客戶列表

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

要點:{customer.*} 簡寫全屬性,AddEntity 指定返回實體類型。

2. HQL 查詢

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

優勢:面向對象語法,映射文件已定義關聯,無需重複指定 Join 條件。

3. Criteria API 查詢

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

機制:CreateCriteria("關聯屬性名") 導航至子實體,鏈式調用構建多層約束。圖示說明導航路徑:Customer → Orders → Products。

簡化版:按產品 ID 查詢客戶

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

結語

多對多映射核心在於中間表配置與雙向 bag 定義。三種查詢方式各有優勢:

  • 原生 SQL:完全控制,適合複雜報表、存儲過程調用
  • HQL:面向對象,開發效率高,關聯導航直觀
  • Criteria API:動態構建、類型安全、適合條件可變的搜索頁面

建議根據團隊技術棧與業務場景選擇主力查詢方式,下篇將探討延遲/立即加載機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved