使用property-ref进行映射时,避免在Nhibernate中出现重复的父对象

时间:2011-08-29 12:23:22

标签: c# nhibernate nhibernate-mapping

我有两个实体:

实体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的计数时,它将在列表中为每个EntityB执行一个SQL来获取它的EntityA引用,但每个EntityB的EntityA将是原始实体I先加载了。当entityA中存在大量entityB时,这已经成为一个巨大的性能瓶颈。数据库是遗留的,它与一些遗留应用程序一起使用,因此无法更改数据库结构。使用二级缓存也不是一个选项,因为当它使用原始SQL的遗留代码运行时会导致系统失败。任何人都可以在不改变数据库结构的情况下建议解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

问题是缓存仅基于主键工作。如果它由某个其他属性链接,则它不知道它是否已经加载并再次加载它。

this post by ayende中有一条提示,二级缓存可能会考虑自然ID。我知道你不想要它。并且它甚至不确定它是否适用于这种情况(ayende使用显式过滤器来自然id)。

您可以尝试将EXTID列映射为NH中的主键,将Id映射为生成的属性......当然,您可能会遇到其他问题。

如果没有任何效果,您需要执行查询以获取所需的确切数据。例如:

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

当然,这并不好笑,你放弃了将实体用作POCO的自然方式。

答案 1 :(得分:0)

您可以在fetch="select"中将fetch="join"更改为<many-to-one name = "A">,然后在获取B时发出连接,而不必选择N + 1