избегайте дублирования родительских объектов в Nhibernate при отображении с помощью свойства ref - PullRequest
1 голос
/ 29 августа 2011

У меня есть 2 объекта:

Сущность A

    public class EntityA
        {
            protected IList<EntityB> _bList = new List<EntityB>();

            virtual public int Id { get; set; }
            virtual public int ExtId { get; set; }


            public virtual void AddB(EntityB b)
            {
                if (!_bList.Contains(b)) _bList.Add(b);
                b.A = this;
                b.ExtId  = this.ExtId;
            }

            public virtual void RemoveB(EntityB b)
            {
                _bList.Remove(b);
            }

            public virtual IList<EntityB> BList
            {
                get { return _bList.ToList().AsReadOnly(); }
            }
        }

Отображение сущности A

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
      <class name="hibernate.domain.mappings.EntityA, hibernate.domain" lazy="true">
        <id name="Id">
          <generator class="native" />
        </id>
        <property type="int" name="ExtId" column="[ExtId]" />
        <bag
          name="BList"
          table="EntityB"
          cascade="all"
          lazy="true"
          inverse="true"
          access="field.camelcase-underscore"
          optimistic-lock="false"
          >
          <key column ="ExtId" property-ref="ExtId" />
          <one-to-many class="hibernate.domain.mappings.EntityB, hibernate.domain" />
        </bag>
    </hibernate-mapping>

Сущность B

     public class EntityB
        {
            protected EntityA _a;

            virtual public int Id { get; set; }
            virtual public int ExtId { get; set; }
            virtual public EntityA A
            {
                get { return _a; }
                set { _a = value; }
            }
        }

Отображение сущности B

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
  <class name="hibernate.domain.mappings.EntityB, hibernate.domain" lazy="true">
    <id name="Id">
      <generator class="native" />
    </id>
    <property type="int" name="ExtId" column="[EXTID]" />
    <many-to-one
            name = "A"
      property-ref ="ExtId"
            not-null="true"
            class = "hibernate.domain.mappings.EntityA, hibernate.domain"
      access="field.camelcase-underscore"
            cascade = "save-update"
            fetch="select"
            insert = "false"
      lazy = "false"
            update = "false"
      column="ExtId"
      />
  </class>
</hibernate-mapping>

Проблема в том, что когда я загружаю объект EntityA и пытаюсь получить счетчик BList, он будет выполнять один SQL-запрос на EntityB в списке, чтобы получить ссылку на EntityA, но EntityA для каждого EntityB будет исходной сущностью загрузили первым. Это стало большим узким местом производительности, когда внутри entityA имеется большое количество entityB. База данных является устаревшей и используется с некоторыми устаревшими приложениями, поэтому изменение структуры базы данных не вариант. И использование кэша второго уровня также не вариант, так как это приведет к сбою системы, когда он работает с устаревшим кодом, который использует сырой SQL. Кто-нибудь может предложить решение этой проблемы без изменения структуры базы данных?

Ответы [ 2 ]

1 голос
/ 30 августа 2011

Проблема в том, что кеш работает только на основе первичного ключа.Если он связан каким-то другим свойством, он не знает, загружен он или нет, и загружает его снова.

В этом сообщении есть подсказка ayende , что-id может рассматриваться кешем второго уровня.Я знаю, ты не хочешь этого.И даже не уверен, что это работает в этом случае (ayende использует явный фильтр для естественного идентификатора).

Вы можете попытаться отобразить столбец EXTID в качестве первичного ключа в NH, а Id - как сгенерированныйсвойство ... Конечно, есть риск, что вы получите другие проблемы вместо этого.

Когда на самом деле ничего не работает, вам нужно выполнить запросы, чтобы получить точные данные, которые вам нужны.Например:

select a, size(a.BList)
from EntityA a

Что, конечно, не смешно, вы теряете естественный способ использования сущностей в качестве POCO.

0 голосов
/ 30 августа 2011

вы можете изменить fetch="select" на fetch="join" в <many-to-one name = "A">, тогда он будет выдавать соединение при извлечении Bs и не должен выбирать N + 1

...