我编写的代码实际上是它的工作,但它不是非常优化。
在我的例子中,我有两个非常简单的表
获取人名和相关国家/地区名称(如果可用)的简单SQL查询将如下所示
SELECT Person.Name AS Name, Country.CountryName AS Country FROM Person LEFT OUTER JOIN Country ON Country.CountryId = Person.CountryId
使用NHibernate获取数据的构造良好的对象看起来像这样
很简单,对。我的模型看起来像这样
namespace NHibernateLeftJoin.Models
{
public class Country
{
public virtual int CountryId { get; set; }
public virtual string CountryName { get; set; }
}
public class Person
{
public virtual int PersonId { get; set; }
public virtual string Name { get; set; }
public virtual int CountryId { get; set; }
public virtual Country CountryObject { get; set; }
}
}
和映射
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="NHibernateLeftJoin.Models.Country, NHibernateLeftJoin" lazy="true">
<id name="CountryId">
<generator class="native" />
</id>
<property name="CountryName" />
</class>
<class name="NHibernateLeftJoin.Models.Person, NHibernateLeftJoin" lazy="true">
<id name="PersonId">
<generator class="native" />
</id>
<property name="Name" />
<property name="CountryId" />
<many-to-one name="CountryObject" class="NHibernateLeftJoin.Models.Country" lazy="false"
column="CountryId" outer-join="true" unique="true" not-null="false" cascade="none" />
</class>
</hibernate-mapping>
这种方法的问题是NHibernate获取一个'Person'行并为每一行调用'Contry'表,如果我们有数千行不是很好。似乎不可能像我们使用SQL一样整洁地执行此操作,或者是我使用完全错误的方法。
可以在此处找到感兴趣的https://dl.dropboxusercontent.com/u/6208162/NHibernateLeftJoin.zip
的VS项目由于
==编辑==
查询可能如下所示
private static IList<Person> GetPersons()
{
using (var session = NHibernateHelper.OpenSession())
{
//using (var transaction = session.BeginTransaction()) {}
IQuery query = session.CreateQuery("FROM Person");
return query.List<Person>();
}
}
由此生成的sql将如下所示
NHibernate: select person0_.PersonId as PersonId1_, person0_.Name as Name1_, person0_.CountryId as CountryId1_ from Person person0_
NHibernate: SELECT country0_.CountryId as CountryId0_0_, country0_.CountryName as CountryN2_0_0_ FROM Country country0_ WHERE country0_.CountryId=@p0;@p0 = 1 [Type: Int32 (0)]
NHibernate: SELECT country0_.CountryId as CountryId0_0_, country0_.CountryName as CountryN2_0_0_ FROM Country country0_ WHERE country0_.CountryId=@p0;@p0 = 2 [Type: Int32 (0)]
NHibernate: SELECT country0_.CountryId as CountryId0_0_, country0_.CountryName as CountryN2_0_0_ FROM Country country0_ WHERE country0_.CountryId=@p0;@p0 = 3 [Type: Int32 (0)]
这与我的数据非常符合逻辑,第一个查询它获取所有人,然后它使用三个不同的查询获取CountryObject的数据(有三个唯一的国家映射到用户)。
由于
答案 0 :(得分:1)
首先,删除此属性... public virtual int CountryId { get; set; }
您
可以通过... CountryObject.CountryId
访问CountryId。我认为你所寻找的是你的ManyToOne上的fetch="join"
。这告诉NH,每次查询Person时都要加入到这个表中。这会使ManyToOne变得非常懒惰。
http://nhibernate.info/doc/nh/en/#mapping-declaration-manytoone
通常,我会避免此设置并使用QueryOver手动加入或急切获取任何ManyToOnes。如果我正在使用单个实体,我将使用ManyToOnes的默认延迟加载。
Session.QueryOver<Person>()
.Fetch(x => x.CountryObject).Eager
.List();
答案 1 :(得分:0)
解决这个问题的一种方法是在编写查询时投射到类中。
执行类似此操作的linq查询可能如下所示
var people = from p in _session.Query<Person>()
select new
{
Name = p.Name,
Country = p.Country == null ? String.Empty : p.Country.Name;
}
此外,您不需要Person对象上的CountryId。你应该能够像这个人一样抓住countryId.Country.Id如果一个人与一个国家有联系。
答案 2 :(得分:0)
解决方案是在CreateQuery方法中实现连接,如下所示
IQuery query = session.CreateQuery("FROM Person AS P LEFT JOIN FETCH P.CountryObject");
return query.List<Person>();
并像这样编写人物的映射文件
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="NHibernateLeftJoin.Models.Person, NHibernateLeftJoin" lazy="true">
<id name="PersonId">
<generator class="native" />
</id>
<property name="Name" />
<property name="CountryId" />
<many-to-one name="CountryObject" class="NHibernateLeftJoin.Models.Country"
column="CountryId" not-found="ignore"
cascade="none" />
</class>
</hibernate-mapping>
在我看来,我无法理解为什么在向映射文件中的多对一元素添加参数fetch =“join”outer-join =“true”时不会自动写入此查询。