Запрос NHibernate с расчетом расстояния хранится в процессе - PullRequest
2 голосов
/ 05 января 2009

У меня есть хранимая процедура в моей базе данных, которая вычисляет расстояние между двумя парами широта / долгота. Эта хранимая процедура называется «DistanceBetween». У меня есть оператор SQL, позволяющий пользователю искать все элементы в таблице «Предметы», упорядоченные по расстоянию до заданной координаты широта / долгота. Инструкция SQL выглядит следующим образом:

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

Как мне использовать этот запрос в NHibernate? Класс Item в моем домене не имеет свойства «Distance», поскольку в моей таблице «Items» нет столбца «Distance». Свойство «Расстояние» действительно вступает в игру только тогда, когда пользователь выполняет этот поиск.

Ответы [ 3 ]

3 голосов
/ 21 октября 2009

Существует три основных подхода, некоторые из которых уже обсуждались:

  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-запроса», вам придется упорядочивать данные при извлечении данных. Это не отличается от получения данных через ваши текущие сопоставления.

Обновление: Исправлена ​​ужасная ошибка редактирования в XML-файле sql-запроса.

1 голос
/ 08 января 2009

Вы можете попробовать:

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 () на правильный тип данных.

0 голосов
/ 21 октября 2009

Айенде объясняет, как сделать это в файлах сопоставления, не прибегая к передаче 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...