2011-03-31 27 views
1

我们正面临一一对一的关联问题,NHProf显示了选择的N + 1 警报,改变标准使用FetchMode.Join是不解析N + 1 问题。以下是详细信息。一一对一的关联问题,NHProf显示了选择的N + 1警报,改变标准使用FetchMode.Join不解决N + 1个问题有什么想法?

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

HBM文件和类实体。

描述:A '样品' 实体可以包含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 ; 
     } 
    } 
} 

标准WITHOUT 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(); 
     } 
    } 

NHProf显示N + 1个警报Criteria.List()已经被执行时。 以下是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 */ 

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

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 问题的任何想法。

谢谢。

回答

3

这是NHibernate中的一个已知问题;联接提取并不能防止各种情况下的N + 1问题。注意,你的第一个带连接读取的查询实际上包含了连接和映射的字段; NHibernate根据映射构造适当的查询,但不会将这些信息传递到联接的子集合中,因此它只是返回并运行N个查询来提取子集。

您可以在这里看到JIRA项目:https://nhibernate.jira.com/browse/NH-2534。 NHibernate团队已经开始发挥作用,并正在积压bug,并且这个标志性专业(我会这样称呼它;我正在等待一个新的构建来解决我自己的N + 1问题,我的评论就是证明)到这个确切的错误),所以我想它会得到修复,只要他们能够得到它。

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

+1

当前JIRA链接:https://nhibernate.jira.com/browse/NH-2534 – 2011-09-19 14:51:42