Отображение NHibernate и выполнение запросов там, где таблицы связаны, но без ограничения внешнего ключа

#nhibernate #nhibernate-mapping

#nhibernate #nhibernate-сопоставление

Вопрос:

Я довольно новичок в NHibernate, и мне нужно задать пару вопросов, относящихся к очень частому сценарию. Следующий упрощенный пример иллюстрирует проблему.

У меня есть две таблицы с именами Equipment и Users. Пользователи — это набор системных администраторов. Оборудование — это набор механизмов.

Таблицы:

  • Таблица Users содержит идентификатор пользователя int и имя входа nvarchar(64).
  • Таблица оборудования имеет EquipId int, EquipType nvarchar(64), обновленный с помощью int.

Поведение:

  • Системные администраторы могут вносить изменения в оборудование, и когда они это делают, в поле UpdatedBy оборудования «обычно» устанавливается их идентификатор пользователя.
  • Пользователи могут быть удалены в любое время.
  • Новые элементы оборудования имеют значение UpdatedBy, равное null.

На оборудовании нет ограничений по внешнему ключу.Обновлено, что означает:

  • Оборудование.UpdatedBy может иметь значение null.
  • Оборудование.Значение UpdatedBy может быть = существующий пользователь.Значение идентификатора пользователя
  • Оборудование.Значение UpdatedBy может быть = несуществующий пользователь.Значение идентификатора пользователя

Чтобы найти оборудование и кто последним обновлял оборудование, я мог бы запросить следующим образом:

выберите E.EquipId, E.EquipName, U.userId, U.LoginName из оборудования, которое E оставило внешним пользователям соединения U включенным. E.UpdatedBy = U.userId

Достаточно просто.

Итак, как это сделать в NHibernate?

Мои сопоставления могут быть следующими:

 <?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
  namespace="Data"
  assembly="Data">

  <class name="User" table="Users">
    <id name="Id" column="UserId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="LoginName" unique="true" not-null="true" />
  </class>

  <class name="Equipment" table="Equipment">
    <id name="Id" column="EquipId" type="int" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="EquipType" />
    <many-to-one name="UpdatedBy" class="User" column="UpdatedBy" />
  </class>

</hibernate-mapping>
  

Итак, как мне получить все элементы оборудования и кто их обновил?

    using (ISession session = sessionManager.OpenSession())
   {
      List<Data.Equipment> equipList =
         session
            .CreateCriteria<Data.Equipment>()
            // Do I need to SetFetchmode or specify that I
            // want to join onto User here? If so how?
            .List<Data.Equipment>();

      foreach (Data.Equipment item in equipList)
      {
         Debug.WriteLine("nEquip Id: "   item.Id);
         Debug.WriteLine("Equip Type: "   item.EquipType);

         if (item.UpdatedBy != null)
            Debug.WriteLine("Updated By: "   item.UpdatedBy.LoginName);
         else
            Debug.WriteLine("Updated by: Nobody");
      }
   }
  

При оборудовании.UpdatedBy = 3 и пользователей нет.userId = 3, вышеуказанный сбой

У меня также есть ощущение, что сгенерированный SQL — это select all from Equipment, за которым следует множество столбцов select от Users, где userId = n, тогда как я ожидал, что NHibernate присоединится влево в соответствии с моим обычным SQL и выполнит одно нажатие. Если я могу указать NHibernate выполнить запрос одним нажатием, как мне это сделать?

В моем проекте важно время, поэтому я с благодарностью принимаю любую помощь, которую вы могли бы предоставить. Если вы размышляете о том, как NHibernate может работать в этом сценарии, пожалуйста, скажите, что вы не совсем уверены. Большое спасибо.

Ответ №1:

В вашем отображении добавьте not-null= false, например:

 <many-to-one name="UpdatedBy" class="User" column="UpdatedBy" not-null="false" />
  

В вашем коде не проверяйте, установлена ли у пользователя страна, чтобы узнать, была ли она кем-либо обновлена. Если пользователь имеет значение null, это вызовет исключение NullPointerException. Вместо этого проверьте, имеет ли пользователь значение null:

 User user = item.UpdatedBy;
if (user  != null)
    Debug.WriteLine("Updated By: "   user.LoginName);
else
    Debug.WriteLine("Updated by: Nobody");
  

Комментарии:

1. Спасибо. Возможно ли заставить NHibernate получать все данные за один заход?

2. Вы могли бы просто настроить критерии для получения всех пользователей, подключенных к оборудованию. Или получите все оборудование, к которому подключен пользователь, и выполните итерацию по оборудованию так же, как и раньше.

3. Есть ли у вас какие-либо приблизительные примеры такого запроса criteria api. Я нахожу Criteria api самой сложной частью hibernate для понимания. Я постоянно просматриваю свои существующие sql-запросы и думаю, как, черт возьми, выразить все это с помощью Criterions api.