在NHibernate中对三对多对多的映射和查询

时间:2009-05-07 09:54:10

标签: sql nhibernate orm mapping

更新: 我错误地从错误的重载中粘贴了查询代码。底部是固定代码

嗨,我们说这是我的域名。

我正在跟踪一些体育赛事,比如赛车比赛。

每场比赛都有参加比赛的赛车手。

Racer特别是Race的驱动程序(它有Driver,start lane,run-time等)。

驱动程序包含名称等等......典型的个人数据。

我想做以下查询: 在本次活动中,让我参加所有比赛(让我们分页),向参赛者展示信息,包括从车手那里获取的数据。

我的问题是,我不认为我的映射(或标准查询,或两者)对于这种情况是最佳的,所以我想问你,如果你在这里看到任何优化机会。 我特别不喜欢这样一个事实,即当我认为子查询可以让它一次性工作时,它需要两次往返数据库。

这是我的映射和查询(DB是从映射生成的)

<?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

第二次查询:

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 个答案:

答案 0 :(得分:1)

Stefan(退出评论,因为此时此功能已变得不太可用)。

我根据你的建议重新编写代码(不改变任何映射)现在,查询只返回一个Race - 三个中的第一个(分页设置为3个比赛/页)与查询匹配。

当我删除

SetResultTransformer( new DistinctRootEntityResultTransformer() )

我得到三个项目,但是它的值是三次相同的值,这很奇怪。有什么想法吗?

答案 1 :(得分:1)

认为它会创建多个查询,以避免重复进入分页查询。 (但我不确定,我还没有看到nhibernate内部源代码)。对包含用于获取实体的重复项的集合进行分页,因为使用了连接实际上是无用的,因此您需要确保返回的结果集没有重复。我认为nhibernate选择2个或更多查询而不是1个查询。

答案 2 :(得分:0)

试试这个:

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太多,返回的结果集更大。 (它包括种族和活动信息,与参赛者一样多次。)