一对一关联问题,NHProf显示选择N + 1警报,更改标准使用FetchMode.Join不解决N + 1问题有什么想法吗?

时间:2011-03-31 04:33:58

标签: nhibernate nhibernate-mapping linq-to-nhibernate

我们面临一对一的关联问题,NHProf显示选择N + 1 警报,更改标准以使用FetchMode.Join不解析N + 1 问题。以下是详细信息。

软件版本详细信息: NH版本:1.2 .net版本:3.5 DB:Oracle 11g 二级缓存:已启用

Hbm文件和类实体。

描述:'Sample'实体可以包含0或1'关联' 实体。 “关联”实体具有来自的外键约束 '样本'实体。

Sample.hbm.xml

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
assembly="NHSample" namespace="NHSample"> 
<class name="Sample" table="SAMPLE" proxy="Sample" 
polymorphism="explicit" > 
        <id name="Id" type="Decimal" unsaved-value="-1"> 
                        <column name="SA_ID" sql-type="NUMBER" not-null="true" 
unique="true"/> 
                        <generator class="sequence"> 
                                <param name="sequence">SA_ID_SEQ</param> 
                        </generator> 
    </id> 
        <property name="SampleName" type="String"> 
                <column name="SAMPLE_NAME" length="100" sql-type="VARCHAR2" not- 
null="false"/> 
        </property> 
        <one-to-one name="Association" class="Association" property- 
ref="SampleAssociated" cascade="all-delete-orphan"/> 
  </class> 
</hibernate-mapping> 

Sample.cs

namespace NHSample 
{ 
    public class Sample 
    { 
        public virtual decimal Id 
        { 
            get ; 
            set ; 
        } 

        public virtual string SampleName 
        { 
            get ; 
            set ; 
        } 

        public virtual Association Association 
        { 
            get ; 
            set ; 
        }
    } 
} 

Association.hbm.xml

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
assembly="NHSample" namespace="NHSample"> 
  <class name="Association" table="ASSOCIATION"> 
    <id name="Id" type="Decimal" unsaved-value="-1"> 
      <column name="AS_ID" sql-type="NUMBER" not-null="true" 
unique="true"/> 
      <generator class="sequence"> 
        <param name="sequence">AS_ID_SEQ</param> 
      </generator> 
    </id> 
        <property name="AssociationName" type="String"> 
                <column name="ASSOCIATION_NAME" length="100" sql-type="VARCHAR2" not- 
null="false"/> 
        </property> 
    <many-to-one name="SampleAssociated" class="Sample"> 
      <column name="SA_ID" sql-type="NUMBER" not-null="true"/> 
    </many-to-one> 
  </class> 
</hibernate-mapping> 

Association.cs

namespace NHSample 
{ 
    public class Association 
    { 
        public virtual decimal Id 
        { 
            get ; 
            set ; 
        } 

        public virtual string AssociationName 
        { 
            get ; 
            set ; 
        } 

        public virtual Sample SampleAssociated 
        { 
            get ; 
            set ; 
        } 
    } 
} 

没有FetchMode的条件=用于访问示例实体的连接:

class Program 
    { 
        static void Main(string[] args) 
        { 
            using (ISession session = SessionFactory.OpenSession()) 
            { 
                var electedIds = new List<decimal>() { 1, 2, 3 }; 

                ICriteria criteria = 
session.CreateCriteria(typeof(Sample)); 
                criteria.Add(Expression.In("Id", electedIds)); 

                var list = criteria.List(); 
            } 
            Console.ReadKey(); 
        } 
    } 

当执行Criteria.List()时,NHProf显示N + 1警报。 以下是NHProf显示的SQL语句。

- 声明#1

SELECT this_.SA_ID                   as SA1_0_1_, 
       this_.SAMPLE_NAME             as SAMPLE2_0_1_, 
       associatio2_.AS_ID            as AS1_1_0_, 
       associatio2_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio2_.SA_ID            as SA3_1_0_ 
FROM   SAMPLE this_ 
       left outer join ASSOCIATION associatio2_ 
         on this_.SA_ID = associatio2_.SA_ID 
WHERE  this_.SA_ID in (1 /* :p0 */,2 /* :p1 */,3 /* :p2 */) 

- 陈述#2

SELECT associatio0_.AS_ID            as AS1_1_0_, 
       associatio0_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio0_.SA_ID            as SA3_1_0_ 
FROM   ASSOCIATION associatio0_ 
WHERE  associatio0_.SA_ID = 1 /* :p0 */ 

- 陈述#3

SELECT associatio0_.AS_ID            as AS1_1_0_, 
       associatio0_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio0_.SA_ID            as SA3_1_0_ 
FROM   ASSOCIATION associatio0_ 
WHERE  associatio0_.SA_ID = 2 /* :p0 */ 

- 陈述#4

SELECT associatio0_.AS_ID            as AS1_1_0_, 
       associatio0_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio0_.SA_ID            as SA3_1_0_ 
FROM   ASSOCIATION associatio0_ 
WHERE  associatio0_.SA_ID = 3 /* :p0 */ 

使用NHProf网站建议修改cirteria使用 FETCHMODE.Join:

class Program 
    { 
        static void Main(string[] args) 
        { 
            using (ISession session = SessionFactory.OpenSession()) 
            { 
                var electedIds = new List<decimal>() { 1, 2, 3 }; 

                ICriteria criteria = 
session.CreateCriteria(typeof(Sample)); 
                criteria.SetFetchMode("Association", FetchMode.Join); 
                criteria.Add(Expression.In("Id", electedIds)); 

                var listByIds = criteria.List(); 
            } 
            Console.ReadKey(); 
        } 
    } 

NHProf仍显示N + 1警报,以下是SQL查询。

- 声明#1

SELECT this_.SA_ID                   as SA1_0_1_, 
       this_.SAMPLE_NAME             as SAMPLE2_0_1_, 
       associatio2_.AS_ID            as AS1_1_0_, 
       associatio2_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio2_.SA_ID            as SA3_1_0_ 
FROM   SAMPLE this_ 
       left outer join ASSOCIATION associatio2_ 
         on this_.SA_ID = associatio2_.SA_ID 
WHERE  this_.SA_ID in (1 /* :p0 */,2 /* :p1 */,3 /* :p2 */) 

- 陈述#2

SELECT associatio0_.AS_ID            as AS1_1_0_, 
       associatio0_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio0_.SA_ID            as SA3_1_0_ 
FROM   ASSOCIATION associatio0_ 
WHERE  associatio0_.SA_ID = 1 /* :p0 */ 

- 陈述#3

SELECT associatio0_.AS_ID            as AS1_1_0_, 
       associatio0_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio0_.SA_ID            as SA3_1_0_ 
FROM   ASSOCIATION associatio0_ 
WHERE  associatio0_.SA_ID = 2 /* :p0 */ 

- 陈述#4

SELECT associatio0_.AS_ID            as AS1_1_0_, 
       associatio0_.ASSOCIATION_NAME as ASSOCIAT2_1_0_, 
       associatio0_.SA_ID            as SA3_1_0_ 
FROM   ASSOCIATION associatio0_ 
WHERE  associatio0_.SA_ID = 3 /* :p0 */ 

有关如何克服此附加SQL语句和N + 1的任何想法 在这种情况下的问题。

谢谢。

1 个答案:

答案 0 :(得分:3)

这是NHibernate中的一个已知问题;加入提取并不能防止在各种情况下出现N + 1问题。请注意,使用连接提取的第一个查询实际上包括连接和映射字段; NHibernate正在根据映射构建正确的查询,但是没有将该信息传递到已连接的子集合中,因此它只返回并运行N个查询来拉动子项。

您可以在此处查看JIRA项目:https://nhibernate.jira.com/browse/NH-2534。 NHibernate团队已经开始进攻,并且积压了积压的错误,这个标记很重要(我会称之为;我正在等待一个新版本来解决我自己的N + 1问题,我的评论就是证明了这一点对于这个确切的错误),所以我猜他们一旦得到它就会得到修复。

就个人而言,我不介意使用2遍查询解决方案,默认情况下可以使用外键查询在映射中将NHibernate告知延迟加载。目前,您只能通过构建自己的IQuery来自行加载子项来指定此行为。由NH生成的惰性代理使用外键查询检索到的ID进行了预初始化,但随后它通过ID一次性提取一个记录的完整数据。那太疯狂了;如果你不使用NH,你就不会这样做。