Сопоставление и запрос трехсторонних отношений «многие ко многим к одному» в NHibernate - PullRequest
2 голосов
/ 07 мая 2009

UPDATE: Я ошибочно вставил код запроса от неправильной перегрузки. Внизу фиксированный код

Привет, скажем, вот мой домен.

Я отслеживаю некоторые спортивные события, например, автомобильные гонки.

В каждой расе есть гонщики, которые принимают участие в гонке.

Racer - это гонщик, в частности гонщик (он имеет драйвер, стартовую полосу, время выполнения и т. Д.).

Драйвер имеет такие вещи, как имя и т. Д. Типичные личные данные.

Я хочу сделать следующий запрос: Получите мне все расы (давайте оставим постраничный) в этом событии, показывая информацию об их участниках, включая данные, полученные от гонщиков.

Моя проблема в том, что я не думаю, что мое сопоставление (или запрос критериев, или оба) является оптимальным для этого сценария, поэтому я хотел спросить вас, видите ли вы какие-либо шансы для оптимизации здесь. Мне особенно не нравится тот факт, что в настоящее время требуется два обхода базы данных, когда я думаю, что подзапрос может заставить его работать всего за один раз.

Вот мое отображение и запрос (БД генерируется из отображений)

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="Foo"
        namespace="Foo">
  <class name="Event" table="Events">
      <id name="Id">
        <generator class="guid"/>
      </id>
    <property name="EventId" not-null="true" unique="true" index="IDX_EventId" />
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="Foo"
        namespace="Foo">
  <class name="Race" table="Races">
    <id name="Id">
      <generator class="guid"/>
    </id>
    <property name="RaceId" not-null="true" unique="true" index="IDX_RaceId"/>
    <property name="Year" not-null="true" />
    <property name="IsValid" not-null="true" />
    <property name="Time" not-null="true" />

    <many-to-one name="Event" cascade="all" not-null="true" />

    <bag name="Contestants" cascade="save-update" inverse="true" lazy="false" batch-size="20" >
      <key column="Race"/>
      <one-to-many class="Racer"/>
    </bag>
  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="Foo"
        namespace="Foo">
  <class name="Racer" table="Racers">
    <id name="Id">
      <generator class="guid"/>      
    </id>
    <many-to-one name="Race" foreign-key="FK_Racer_has_race" not-null="true" cascade="save-update" />
    <property name="Lane" not-null="true" />
    <many-to-one name="Driver" foreign-key="FK_Racer_has_driver" cascade="save-update" lazy="false" fetch="join" />
    <property name="FinishPosition" />
    <property name="Finished" not-null="true" />
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Foo"
                   namespace="Foo">
  <class name="Driver" table="Drivers">
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Name" not-null="true" length="32" unique="true" index="IDX_Driver_Name" />
  </class>
</hibernate-mapping>

И код запроса:

public IList<Race> GetMostRecentRacesForEvent( int eventId, int firstRaceToFetch, int count ) {
    DetachedCriteria criteria = DetachedCriteria.For( typeof( Race ) ).
        CreateAlias( "Event", "event" ).
        Add( Restrictions.Eq( "event.EventId", eventId ) ).
        AddOrder<Race>( r => r.Time, Order.Desc ).
        SetResultTransformer( new DistinctRootEntityResultTransformer() ).
        SetFirstResult( firstRaceToFetch ).
        SetMaxResults( count );
    return this.ExecuteListQuery<Race>(criteria); }

Если для подкачки установлено значение 3 гонки / страница, вот SQL, который он генерирует:

SELECT TOP 3 Id2_1_, RaceId2_1_, Year2_1_, IsValid2_1_, Time2_1_, Event2_1_, Id1_0_, EventId1_0_ FROM (SELECT ROW_NUMBER() OVER(ORDER BY __hibernate_sort_expr_0__ DESC) as row, query.Id2_1_, query.RaceId2_1_, query.Year2_1_, query.IsValid2_1_, query.Time2_1_, query.Event2_1_, query.Id1_0_, query.EventId1_0_, query.__hibernate_sort_expr_0__ FROM (SELECT this_.Id as Id2_1_, this_.RaceId as RaceId2_1_, this_.Year as Year2_1_, this_.IsValid as IsValid2_1_, this_.Time as Time2_1_, this_.Event as Event2_1_, event1_.Id as Id1_0_, event1_.EventId as EventId1_0_, this_.Time as __hibernate_sort_expr_0__ FROM Races this_ inner join Events event1_ on this_.Event=event1_.Id WHERE event1_.EventId = @p0) query ) page WHERE page.row > 0 ORDER BY __hibernate_sort_expr_0__ DESC

2-й запрос:

SELECT contestant0_.Race           as Race2_,
       contestant0_.Id             as Id2_,
       contestant0_.Id             as Id0_1_,
       contestant0_.Race           as Race0_1_,
       contestant0_.Lane           as Lane0_1_,
       contestant0_.Driver         as Driver0_1_,
       contestant0_.FinishPosition as FinishPo5_0_1_,
       contestant0_.Finished       as Finished0_1_,
       driver1_.Id                 as Id3_0_,
       driver1_.Name               as Name3_0_
FROM   Racers contestant0_
       left outer join Drivers driver1_
         on contestant0_.Driver = driver1_.Id
WHERE  contestant0_.Race in ('4157280d-be8d-44be-8077-a770ef7cd394' /* @p0 */,'74e1bfaa-9926-43c7-8b17-e242634dc32f' /* @p1 */,'e1e86b67-2c37-4fbe-8793-21e84a6e4be4' /* @p2 */)

Ответы [ 3 ]

1 голос
/ 07 мая 2009

Стефан (отойти от комментариев, потому что это становится не очень полезным в данный момент).

Я переделал код в соответствии с вашим предложением (без изменения сопоставления), и теперь запрос возвращает только одну расу - первую из трех (для подкачки установлено значение 3 расы / страница), соответствующую запросу.

Когда я удаляю

SetResultTransformer( new DistinctRootEntityResultTransformer() )

Я получаю три предмета, но это одно и то же значение три раза, что ... странно. Есть идеи?

1 голос
/ 12 мая 2009

I думаю, создает несколько запросов, чтобы избежать появления дубликатов в поисковом запросе. (но я не уверен, я не видел внутренний исходный код nhibernate). Пейджинг по набору, который содержит дубликаты для выборки объекта, потому что соединения были использованы, на самом деле бесполезен, поэтому вы должны быть уверены, что возвращенный набор результатов не содержит дубликатов. Я думаю, что nhibernate выбирает 2 или более запросов здесь вместо 1 запроса.

0 голосов
/ 07 мая 2009

Попробуйте это:

DetachedCriteria criteria = DetachedCriteria.For( typeof( Race ), "race" )
    .CreateAlias("Event", "event")
    .CreateAlias("race.Contestants", "contestant")
    .SetFetchMode("race.Contestants", FetchMode.Join)
    .SetFetchMode("contestant.Driver", FetchMode.Join)
    .Add( Restrictions.Eq( "event.EventId", eventId ) )
    .AddOrder<Race>( r => r.Time, Order.Desc )
    .SetResultTransformer( new DistinctRootEntityResultTransformer() )
    .SetFirstResult( firstRaceToFetch )
    .SetMaxResults( count );
return this.ExecuteListQuery<Race>(criteria); }

Он должен сделать один запрос и присоединиться к гонкам, соревнованиям и соревнованиям.

Не следует слишком много использовать FetchMode.Join, возвращаемый набор результатов больше. (Он включает информацию о гонке и соревнованиях столько раз, сколько у вас есть участников.)

...