我试图让 NHibernate 基于 3 个表的内部联接执行一个简单的查询:
var sessionCriteria = session.CreateCriteria<FoobarMaster>("M")
.CreateCriteria("Accounts", "A", NHibernate.SqlCommand.JoinType.InnerJoin)
.CreateCriteria("TrackingRecords", "T", NHibernate.SqlCommand.JoinType.InnerJoin)
.Add(Restrictions.Eq("T.PicNumber", "123456"));
var foobarMaster = sessionCriteria.UniqueResult<FoobarMaster>();
LINQ 中也有同样的事情:
from m in session.Query<FoobarMaster>()
from a in m.Accounts
from t in a.TrackingRecords
where t.PicNumber == "12345"
select m
我使用 QueryOvers 和 JoinAliases 也有同样的效果。对于所有人来说,我遇到了运行时异常:
"could not resolve property: TrackingNo of: Account".
这很奇怪,因为 TrackingNo
是 TrackingRecord
属性而不是 Account 属性。它甚至以 T 为前缀 - TrackingRecord
的别名。
这是我的映射:
<class name="FoobarMaster" table="T_FOOBAR_MASTER">
<id name="FoobarMasterId" column="FOOBAR_MASTER_ID" type="int">
<generator class="identity"/></id>
<bag name="Accounts" cascade="all" inverse="true">
<key column="FOOBAR_MASTER_ID" />
<one-to-many class="FoobarAccount" />
</bag>
...
<class name="FoobarAccount" table="T_FOOBAR_ACCOUNT">
<id name="FoobarAccountId" column="FOOBAR_ACCOUNT_ID" type="int">
<generator class="identity"/></id>
<many-to-one name="FoobarMaster" class="FoobarMaster" column="FOOBAR_MASTER_ID" />
<property name="AccountId" column="ACCOUNT_ID" />
<bag name="TrackingRecords" cascade="all" inverse="true">
<key column="ACCOUNT_ID" />
<one-to-many class="FoobarAccount" />
</bag>
...
<class name="TrackingRecord" table="T_TRACKING">
<id name="TrackingId" column="TRACKING_ID" type="int"><generator class="identity"/></id>
<many-to-one name="FoobarAccount" class="FoobarAccount" column="ACCOUNT_ID" />
<property name="PicNumber" column="PICNUMBER" type="AnsiString" length="25" />
...
以下是类/实体:
public class FoobarMaster
{
public virtual int FoobarMasterId { get; set; }
public virtual IList<FoobarAccount> Accounts { get; set; }
...
public class FoobarAccount
{
public virtual int FoobarAccountId { get; set; }
public virtual FoobarMaster FoobarMaster { get; set; }
public virtual int AccountId { get; set; }
public virtual IList<TrackingRecord> TrackingRecords { get; set; }
...
public class TrackingRecord
{
public virtual long TrackingId { get; set; }
public virtual FoobarAccount FoobarAccount { get; set; }
public virtual string PicNumber { get; set; }
...
最佳答案
真正的问题
这里的答案基于最新更新的问题,清晰且易于修复!该映射包含错误的 one-to-many
设置。查看第一级列表:
<bag name="Accounts" cascade="all" inverse="true">
<key column="FOOBAR_MASTER_ID" />
<!-- here we can see the CORRECT reference -->
<one-to-many class="FoobarAccount" />
</bag>
另一方面,第二个级别具有相同的目标,这是错误的:
<bag name="TrackingRecords" cascade="all" inverse="true">
<key column="ACCOUNT_ID" />
<!-- WRONG. In deed, the Account does NOT contain 'PicNumber' -->
<one-to-many class="FoobarAccount" />
</bag>
答案:
更改 <one-to-many class="FoobarAccount" />
进入 <one-to-many class="TrackingRecord" />
正确的映射应该是这样的:
<bag name="TrackingRecords" cascade="all" inverse="true">
<key column="ACCOUNT_ID" />
<!-- now we won't recieve the Account does not contain 'PicNumber' -->
<one-to-many class="TrackingRecord" />
</bag>
From that moment, all the stuff will work properly, the query in the begining of the question is correct. No need for subqueries etc
...
原始提示 - 与之前可用的信息相关
如果你的对象/实体像这样链接起来,你正在尝试实现的目标将会起作用
-
Master
有多个(或引用)帐户 -
Account
有很多(或引用)TrackingRecords
但是根据您遇到的问题,您的映射似乎是
-
Master
有多个(或引用)帐户 -
Master
有很多(或引用)TrackingRecords
那么,就只能这样实现SQL了
select m.* from t_master m
inner join t_account a on m.master_id = a.master_id
//inner join t_tracking t on a.account_id = t.account_id
inner join t_tracking t on m.account_id = t.account_id // the m.account_id
where t.tracking_no = '123456'
查询应该是这样的:
// do some filter over A
var rootQuery = session.CreateCriteria<Master>("M")
.CreateCriteria("Accounts", "A", NHibernate.SqlCommand.JoinType.InnerJoin);
// working with the T here
rootQuery.CreateCriteria("TrackingRecords", "T", NHibernate.SqlCommand.JoinType.InnerJoin)
.Add(Restrictions.Eq("T.TrackingNo", "123456"));
更新,反射(reflect)问题扩展:
此映射不适合在一起:
TrackingRecords 的关键列是 ACCOUNT_ID
<class name="Account" table="T_ACCOUNT">
...
<bag name="TrackingRecords" cascade="all" inverse="true">
<key column="ACCOUNT_ID" />
...
虽然来自 TrackingRecord 的引用是通过 ACCOUNT_NUMBER 完成的
<class name="TrackingRecord" table="T_TRACKING">
...
<many-to-one name="Account" class="Account" column="ACCOUNT_NUMBER" />
...
关于NHibernate 多重内连接选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21253258/