NHibernate:用左连接替换多对一

时间:2013-08-20 17:57:31

标签: c# nhibernate nhibernate-mapping

我编写的代码实际上是它的工作,但它不是非常优化。

在我的例子中,我有两个非常简单的表

enter image description here

获取人名和相关国家/地区名称(如果可用)的简单SQL查询将如下所示

SELECT Person.Name AS Name, Country.CountryName AS Country FROM Person LEFT OUTER JOIN Country ON Country.CountryId = Person.CountryId

enter image description here

使用NHibernate获取数据的构造良好的对象看起来像这样

enter image description here

很简单,对。我的模型看起来像这样

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的数据(有三个唯一的国家映射到用户)。

由于

3 个答案:

答案 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”时不会自动写入此查询。