NHibernate为什么要进行额外的数据库调用?

时间:2016-10-18 10:42:24

标签: c# nhibernate fluent-nhibernate nhibernate-criteria

这是我的班级:

电影

List<Person> Actors
List<Genre> Genres

MovieMap

HasManyToMany(x => x.Genres)
    .Table("movies_genres")
    .ParentKeyColumn("movie_id")
    .ChildKeyColumn("genre_id")
    .Cascade.SaveUpdate().LazyLoad();

HasManyToMany(x => x.Actors)
    Table("movies_actors")
    .ParentKeyColumn("movie_id")
    .ChildKeyColumn("person_id")
    .Cascade.SaveUpdate().LazyLoad();

存储库方法

public Movie get_movie_by_id(int id)
{
    var movie = _session.CreateCriteria(typeof(Movie))
        .CreateAlias("Genres", "mg")
        .SetFetchMode("Genres", FetchMode.Eager)
        .CreateAlias("Actors", "ma")
        .SetFetchMode("Actors", FetchMode.Eager)
        .Add(Restrictions.Eq("Id", id))
        .UniqueResult<Movie>();
    return movie;
}

当我呼叫get_movie_by_id() NHibernate向SQL服务器发出 3次调用时:

  1. 关于加入movies_genres,流派和movie_actors,演员的桌上电影

    SELECT
    (...removed it all to make it shoerter...) 
    FROM movies this_ inner join movies_genres genres4_ on this_.id=genres4_.movie_id inner join genre mg1_ on genres4_.genre_id=mg1_.id inner join movies_actors actors6_ on this_.id=actors6_.movie_id inner join person ma2_ on actors6_.person_id=ma2_.id left outer join person person8_ on this_.director_id=person8_.id left outer join countries country9_ on this_.country_id=country9_.id WHERE this_.id = @p0;
    
  2. 关于加入流派的桌子movie_genres

    SELECT genres0_.movie_id as movie1_1_, genres0_.genre_id as genre2_1_, genre1_.id as id3_0_, genre1_.name as name3_0_ FROM movies_genres genres0_ left outer join genre genre1_ on genres0_.genre_id=genre1_.id WHERE genres0_.movie_id=@p0;
    
  3. 关于加入演员的桌子movies_actors

    SELECT actors0_.movie_id as movie1_1_, actors0_.person_id as person2_1_, person1_.id as id4_0_, person1_.name as name4_0_ FROM movies_actors actors0_ left outer join person person1_ on actors0_.person_id=person1_.id WHERE actors0_.movie_id=@p0;
    
  4. 在第一次调用中,NHibernate已经拥有了所有数据。为什么要进行这两个额外的通话?

1 个答案:

答案 0 :(得分:0)

这是因为NHibernate如何自动生成查询。查看您的HasManyToManyLazyLoad配置。

NH首先生成查询以从任何相关表中获取没有数据的所有movies(只有连接存在)。然后它在依赖表上为WHERE生成Primary Key子句的新查询。

然后,它会使用您的POCOsEntities来映射多个查询的输出。

如果您想避免这种情况并要求NH一次性检索数据,请尝试删除LazyLoad。使用.hbm文件,您可以通过<set name = "PocoName" table = "TableName" fetch = "join">

实现相同的目标

或者,您可以使用预先加载。我不确定如何使用代码配置执行此操作,但您可以尝试删除对LazyLoad的调用。

使用.hbm文件,您可以使用以下内容强制执行急切加载: -

<class name="A" table="A" lazy="false">

更新1:

这反对NHibernate文档。请参阅this链接。

从上面的链接复制以下内容: -

  

相反,我们保留默认行为,并为a覆盖它   特定交易,使用left join fetch中的HQL。这说明   NHibernate在第一个选择中急切地获取关联,使用   外连接。在ICriteria查询API中,您可以使用   SetFetchMode(FetchMode.Join)

     

如果您觉得自己希望改变抓取策略   由Get()Load()使用,只需使用ICriteria查询即可   例如:

User user = (User) session.CreateCriteria<User>()
                .SetFetchMode("Permissions", FetchMode.Join)
                .Add( Expression.Eq("Id", userId) )
                .UniqueResult();
     

避免N + 1选择问题的一种完全不同的方法是   使用二级缓存。