我试图让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做同样的事情。对于所有人,我得到一个运行时异常:
“无法解析财产:TrackingNo of:Account”。
奇怪的是,TrackingNo
是TrackingRecord
属性,而不是帐户属性。它甚至以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; }
...
答案 0 :(得分:3)
这里的答案基于最新更新的问题,清晰易懂!映射包含错误的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>
从那一刻起,所有的东西都会正常工作,问题开头的查询是正确的。不需要子查询等
...
如果您的对象/实体将被链接到这样的
,那么您正在尝试实现的功能将起作用Master
有多个(或参考)帐户 Account
有多个(或参考) TrackingRecords 但根据您的问题,您的映射似乎是
Master
有多个(或参考)帐户 Master
有多个(或参考) TrackingRecords 在这种情况下,你只能实现像这样的
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"));
此映射不适合:
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" />
...
答案 1 :(得分:0)
由于您只通过SQL选择主数据,没有来自T_Account和T_TrackingRecord的字段/列,我建议使用DetachedCriteria和子查询。
如果你有TrackingRecord.AccountId和Account.MasterId属性,你可以这样做:
var trackRecAccountIdsSubq = DetachedCriteria.For(typeof(TrackingRecord))
.SetProjection(Projections.Property("AccountId"))
.Add(
Restrictions.Eq("TrackingNo", "123456"));
var accountMasterIdsSubq = DetachedCriteria.For(typeof(Account))
.SetProjection(Projections.Property("MasterId"))
.Add(
Subqueries.PropertyIn("AccountId", trackRecAccountIds));
var mastersByTrackRecNoCriteria = session.CreateCriteria<Master>()
.Add(
Subqueries.PropertyIn("MasterId", accountMasterIdsSubq);
var mastersByTrackRecNo = mastersByTrackRecNoCriteria.List<Master>();
生成的SQL将类似于:
Select * from t_master where master_id in (
select master_id from t_account where account_id in (
select account_id from t_tracking where tracking_no=123456
)
)
不确定它是否以及如何使用实体对象属性(没有外部ID属性),但子查询在数据库上创建快速半连接(理想情况下通过父实体ID的索引)。
默认情况下,这也只会获取Master,而不是Account或TrackingRecord;如果你不需要它们,那就好又快。如果您需要它们,并且每个TrackingNo只有一个帐户和主服务器,则连接提取可能是更好的选择。