Неполная ассоциативная проблема, NHProf показывает «Выбор оповещения N + 1, изменение критериев для использования FetchMode». Присоединяйтесь к решению проблемы N + 1. Есть мысли? - PullRequest
1 голос
/ 31 марта 2011

Мы сталкиваемся с проблемой взаимно-однозначного сопоставления. NHProf показывает «Выбор оповещения N + 1», «Изменение критериев для использования FetchMode.Join не решает проблему N + 1».Ниже приведены подробности.

Сведения о версии программного обеспечения: NH версия: 1.2 .net Версия: 3.5 БД: Oracle 11g Кэш второго уровня: включено

Файлы Hbm и сущности классов.

Описание: сущность 'Sample' может содержать 0 или 1 сущность 'Association'.У объекта «Association» есть ограничение внешнего ключа от объекта «Sample».

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

NHProf показал N + 1 предупреждение, когда Criteria.List () был выполнен.Ниже приведены SQL-отчеты, представленные NHProf.

- выписка № 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 предлагает изменение циртерии с помощью 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 Ответ

3 голосов
/ 31 марта 2011

Это известная проблема в NHibernate;Выборка соединений не предотвращает проблему N + 1 в различных ситуациях.Обратите внимание, что ваш первый запрос с извлечением соединения действительно включает поля соединения и сопоставления;NHibernate создает правильный запрос в соответствии с отображением, но не передает эту информацию в объединенную дочернюю коллекцию, поэтому он просто возвращается назад и запускает N запросов для извлечения дочерних элементов.

Здесь вы можете увидеть элемент JIRA: https://nhibernate.jira.com/browse/NH-2534. Команда NHibernate включилась в работу и продвинулась вперед в отставании от ошибок, и этот отмечен как основной (я бы назвал это так; я жду новой сборки, чтобы решить мой собственный N +1 проблема, о чем свидетельствует мой комментарий к этой точной ошибке), поэтому я предполагаю, что она будет исправлена, как только они смогут ее найти.

Лично я не возражаю против решения с 2-проходным запросомгде NHibernate может быть указан в сопоставлении с отложенной загрузкой с использованием запроса внешнего ключа по умолчанию.В настоящее время вы можете указать это поведение, только создав собственный IQuery для загрузки дочерних элементов.Ленивый прокси-сервер, созданный NH, предварительно инициализируется идентификаторами, полученными с помощью запроса внешнего ключа, но затем он извлекает полные данные по одной записи за раз по идентификатору.Это безумие;ты бы никогда так не поступил, если бы не использовал NH.

...