当使用property-ref进行映射时,避免在Nhibernate中重复父对象
本文关键字:Nhibernate 对象 property-ref 映射 | 更新日期: 2023-09-27 18:07:09
我有两个实体:
实体 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将是我首先加载的原始实体。当实体a中有大量的实体b时,这就成为一个很大的性能瓶颈。数据库是遗留的,它与一些遗留应用程序一起使用,因此不能更改数据库结构。使用二级缓存也不是一个选择,因为它会使系统在使用原始SQL的遗留代码运行时失败。有没有人能在不改变数据库结构的情况下提出解决这个问题的方法?
问题是缓存只基于主键工作。如果它被其他属性链接,它不知道它是否已经加载,并再次加载。
ayende在这篇文章中暗示,第二级缓存可能会考虑自然id。我知道你不想要。它甚至不确定它在这种情况下是否有效(ayende对自然id使用了显式过滤器)。
您可以尝试将EXTID列映射为NH中的主键,并将Id映射为生成的属性…当然也有可能出现其他问题。
当什么都不起作用时,您需要执行查询以获得所需的确切数据。例如:
select a, size(a.BList)
from EntityA a
这当然不好玩,你失去了使用实体作为poco的自然方式。
你可以在<many-to-one name = "A">
中更改fetch="select"
为fetch="join"
,然后在获取b时发出连接,而不必选择N+1