NHibernate: критерии запроса для объекта и, возможно, подкласса в том же запросе - PullRequest
3 голосов
/ 17 февраля 2010

Это настройка для двух моих сущностей:

public class Person {
  public Guid Id {get;set;}
  public string Name {get;set;}
}

public class Immortal : Person {
  public string DarkName {get;set;}
}

Вот как выглядят их отображения:

<class name="Person">
  <id name="Id">
    <generator class="guid.comb"/>
  </id>
  <property name="Name" />

  <joined-subclass name="Immortal">
    <key column='PersonId' />
    <property name="DarkName" />
  </joined-subclass>
</class>

Итак, это установка, 2 сущности, одна является объединенным подклассом другой. У меня есть поисковая структура, которая берет любое количество критериев из формы, затем применяет соответствующие критерии к запросу, а затем возвращает результаты.

Теперь скажите, что у меня есть одно поле формы в этом случае, «Имя» - я хочу вернуть всех людей, будь то обычные Люди или этот особый класс Бессмертных существ, посмотрев, соответствует ли их имя свойству Имя Персона, но в случае Бессмертного я также хотел бы считать это совпадением, если их DarkName совпадает с тем, что было дано в форме.

Так что это моя дилемма, как мне это сделать? Лучшее, что я смог сделать с материалами NHritnate ICriteria, - это создать подзапрос в Immortal, чтобы проверить там имя, а затем посмотреть, есть ли в этом подзапросе корневой идентификатор человека. Однако при работе с таблицей из десятков тысяч человек этот метод получения результатов чрезвычайно неэффективен и может даже истечь время ожидания (более 30 с) в моей реальной ситуации.

Я был бы готов сделать это и в HQL, потому что я решил, что хочу, чтобы внешнее объединение в Immortal проверило это поле, но я не могу заставить HQL выполнить объединение двух разнородных сущностей с произвольным свойством насколько я знаю, должны основываться на прямых ассоциациях в ваших отображениях. Например, это то, что я хотел бы , чтобы увидеть:

select person from Person person
  outer join Immortal immortal on immortal.PersonId = person.Id
  where
    person.Name = :name or
    immortal.DarkName = :name

Что скажете, у вас стекопоток?

1 Ответ

3 голосов
/ 21 февраля 2010

Следующий запрос критерия выдает почти тот же SQL, который вы хотите.

Это использует некоторую слабость в механизме запроса критериев.

var list = session.CreateCriteria<Person>()
    .Add( Expression.Disjunction()
        .Add( Expression.Eq( "Name", name ) )
        .Add( Expression.Eq( "ImmortalName", name ) )
        )
    .List<Person>();

выдает следующий SQL:

SELECT this_.Id             as Id0_0_,
       this_.Name           as Name0_0_,
       this_1_.ImmortalName as Immortal2_1_0_,
       case 
         when this_1_.Id is not null then 1
         when this_.Id is not null then 0
       end as clazz_0_
FROM   person this_
       left outer join immortal this_1_
         on this_.Id = this_1_.Id
WHERE  (this_.Name = 'foo' /* @p0 */
         or this_1_.ImmortalName = 'foo' /* @p1 */)

Я использовал следующее сопоставление классов:

<class name="Person" table="person">
    <id name="Id">
        <generator class="identity" />
    </id>
    <property name="Name" />
    <joined-subclass name="Immortal" table="immortal">
        <key column="Id" />
        <property name="ImmortalName" />
    </joined-subclass>
</class>
...