2009-01-05 47 views
2

我的數據庫中有一個存儲過程,用於計算兩個經緯度對之間的距離。這個存儲過程被稱爲「DistanceBetween」。我有一個SQL語句允許用戶搜索項目表中按距離提供的經緯度座標排序的所有項目。 SQL語句如下:NHibernate與距離計算存儲過程的查詢

SELECT Items.*, dbo.DistanceBetween(@lat1, @lat2, Latitude, Longitude) AS Distance 
FROM Items 
ORDER BY Distance 

如何在NHibernate中使用此查詢?我的域中的Item類沒有「Distance」屬性,因爲我的Items表中沒有「Distance」列。 「距離」屬性實際上只在用戶執行此搜索時才起作用。

+0

爲什麼不在Linq/Hql/Criteria中創建DistanceBetween方法? – Paco 2009-10-14 19:25:36

+0

Paco - 你能否詳細說明如何做到這一點? – 2009-10-14 20:01:00

+0

只要看看程序中的sql代碼? 我使用這個算法:http://en.wikipedia.org/wiki/Haversine_formula – Paco 2009-10-16 18:50:56

回答

3

基本上有可以使用的三種方法,其中一些已經被討論:

  1. 使用HQL查詢或CreateCriteria/ICriteria查詢; 缺點:它不是實體/ DAL的一部分; 上限:它是靈活的;
  2. 使用屬性映射與formula; 缺點:這並不總是可行或不可能的,如果不小心,性能會降低; 上限:它是您的實體不可分割的一部分;
  3. 創建單獨的XML HBM映射文件並映射到單獨的(將被創建的)實體; 缺點:它不是基礎實體的一部分; 上傳時間:您只需在需要時調用SP,完全控制映射/額外屬性/擴展,就可以使用部分或抽象類與現有實體結合。

我會在這裏簡單地展示一個選項2和3的例子,我相信選項1已經被其他人在本篇文章早些時候已經充分覆蓋了。

當查詢可以創建爲select語句的子查詢,並且映射表中所有需要的參數都可用時,選項二特別有用。如果表不可變,並且/或者以只讀方式緩存,這也會有所幫助,具體取決於您的存儲過程有多沉重。

<class name="..." table="..." lazy="true" mutable="false> 
    <cache usage="read-only" /> 

    <id name="Id" column="id" type="int"> 
     <generator class="native" /> 
    </id> 
    <property name="Latitude" column="Latitude" type="double" not-null="true" /> 
    <property name="Longitude" column="Longitude" type="double" not-null="true" /> 

    <property name="PrijsInstelling" 
     formula="(dbo.DistanceBetween(@lat1, @lat2, Latitude, Longitude))" 
     type="double" /> 

    ... etc 
</class> 

如果以上是不可能的,因爲在映射的限制,問題緩存或如果您當前的高速緩存設置檢索一個接一個,而不是被更大的金額,你不能改變這種狀況,你應該考慮一個替代方法例如整個查詢與參數的單獨映射。這是相當接近上述CreateSqlQuery方法,但迫使結果集進行某種類型的(你可以以聲明方式設置每個屬性):

<sql-query flush-mode="never" name="select_Distances"> 
    <return 
     class="ResultSetEntityClassHere,Your.Namespace" 
     alias="items" 
     lock-mode="read" > 

     <return-property name="Id" column="items_Id" /> 
     <return-property name="Latitude" column="items_Latitude" /> 
     <return-property name="Longitude" column="items_Longitude" /> 
     <return-property name="Distance" column="items_Distance" /> 
    </return> 

    SELECT 
     Items.*, 
     dbo.DistanceBetween(@lat1, @lat2, Latitude, Longitude) AS Distance 
    FROM Items 
    WHERE UserId = :userId 

</sql-query> 

您可以調用該查詢,如下所示:

List<ResultSetEntityClassHere> distanceList = 
    yourNHibernateSession.GetNamedQuery("select_Distances") 
     .SetInt32("userId", currentUserId) /* any params go this way */ 
     .SetCacheable(true)     /* it's usually good to cache */ 
     .List<ResultSetEntityClassHere>(); /* must match the class of sql-query HBM */ 

根據您的需求,您可以選擇一種方法。我個人使用下面的經驗法則來決定使用什麼方法:

  • 計算是輕還是可以緩存? 使用formula的方法;
  • 是否需要將參數發送到SP/SQL? 使用sql-query +映射方法;
  • 查詢的結構(非常)變量? 通過代碼使用ICriteria或HQL方法。

關於數據排序:當您選擇「公式」或「sql-query mapping」方法時,您必須在檢索數據時執行排序。這與通過當前映射檢索數據沒有區別。

更新:在sql-query XML中糾正了可怕的編輯錯誤。

1

你可以嘗試:

session.CreateSqlQuery(@"SELECT {item.*}, dbo.DistanceBetween(:lat1, :lat2, {item}.Latitude, {item}.Longitude) AS Distance 
    FROM Items {item} 
    ORDER BY Distance") 
     .AddEntity("item", typeof(Item)) 
     .SetDecimal("lat1", lat1) 
     .SetDecimal("lat2", lat2) 
     .List<Item>() 

NHibernate的是挑剔有關查詢表&列的別名,所以你需要讓它使用{}語法展開。另外,使用HQL命名參數語法(:lat1而不是@ lat1),並將SetDecimal()更改爲正確的數據類型。