NHibernate查询与距离计算存储过程

时间:2009-01-05 00:59:23

标签: sql nhibernate

我的数据库中有一个存储过程,用于计算两个纬度/长度对之间的距离。此存储过程称为“DistanceBetween”。我有一个SQL语句允许用户搜索Items表中按所提供的纬度/经度坐标的距离排序的所有项目。 SQL语句如下:

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

如何在NHibernate中使用此查询?我的域中的Item类没有“距离”属性,因为我的Items表中没有“距离”列。 “距离”属性实际上仅在用户执行此搜索时才会发挥作用。

3 个答案:

答案 0 :(得分:3)

基本上可以使用三种方法,其中一些方法已经讨论过了:

  1. 使用HQL查询或CreateCriteria / ICriteria查询; 缺点:它不是实体/ DAL的一部分; 好处:它很灵活;
  2. 使用formula的属性映射; 缺点:它并不总是可行或可能,如果不小心,性能会降低; 好处:计算是您实体不可分割的一部分;
  3. 创建单独的XML HBM映射文件并映射到单独的(待创建的)实体; 缺点:它不是基础实体的一部分; 优势:您只需在需要时调用SP,完全控制映射/额外属性/扩展,可以使用部分或抽象类与现有实体组合。
  4. 我将在这里简要展示一个选项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>
    

    如果由于映射限制,缓存问题或者当前缓存设置逐个检索而不是更大量而无法更改,则应该考虑上述情况,您应该考虑另一种方法,例如a整个查询与参数的单独映射。这与上面的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 :(得分: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()更改为正确的数据类型。

答案 2 :(得分:0)

Ayende解释了如何在映射文件中执行此操作,而不必诉诸于将SQL传递给您的会话:

http://ayende.com/Blog/archive/2006/09/18/UsingNHibernateWithStoredProcedures.aspx

另请参阅有关此问题的Hibernate文档(也与NHibernate相关):

http://docs.jboss.org/hibernate/stable/core/reference/en/html/querysql.html