Nhibernate:派生表上的左外连接

时间:2013-11-28 11:24:25

标签: c# sql nhibernate

我有以下两个实体:

public sealed class Parent
{
    public long ParentId { get; set; }
    public bool Deleted { get; set; }
    public ISet<Child> Children { get; set; }

    public Parent()
    {
        Children = new HashedSet<Child>();
    }
}

public sealed class Child
{
    public long ChildId { get; set; }
    public bool Deleted { get; set; }
    public Parent Parent { get; set; }

    public Child()
    {

    }
}

使用映射文件:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="OperationalExcellenceDiary.DomainModels"
                   namespace="OperationalExcellenceDiary.DomainModels.Entities">

    <class name="Parent" table="Parent" lazy="false">
        <id name="ParentId" column="ParentId">
            <generator class="native"/>
        </id>

        <set name="Children" inverse="false" cascade="all-delete-orphan" lazy="false">
            <key column="ParentId"/>
            <one-to-many class="Child"/>
        </set>

        <property name="Deleted" type="bool">
            <column name="Deleted" default="0"/>
        </property>

    </class>

</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="OperationalExcellenceDiary.DomainModels"
                   namespace="OperationalExcellenceDiary.DomainModels.Entities">

    <class name="Child" table="Child" lazy="false">
        <id name="ChildId" column="ChildId">
            <generator class="native"/>
        </id>

        <many-to-one name="Parent" class="Parent" column="ParentId" foreign-key="FK_Parent_Child" />

        <property name="Deleted" type="bool">
            <column name="Deleted" default="0"/>
        </property>
    </class>

</hibernate-mapping>

我希望所有未标记为“已删除”的父母及其子女以及未标记为“已删除”的父母

以下SQL查询就是这样做的:

select 
    Parent.*,
    Child.*
from Parent
left outer join (
                    select *
                    from Child
                    where Child.Deleted = 0
                ) as Child
    on Parent.ParentId = Child.ParentId
where Parent.Deleted = 0

如何将其转换为nHibernate查询?

我尝试过CreateSQLQuery:

IEnumerable<Parent> parents = session.CreateSQLQuery(@" 
select 
    {Parent.*},
    {Child.*}
from Parent
    left outer join (
                        select *
                        from Child
                        where Child.Deleted = 0
                    ) as Child
        on Parent.ParentId = Child.ParentId
where Parent.Deleted = 0")
    .AddEntity("Parent", typeof(Parent), LockMode.None)
    .AddEntity("Child", typeof(Child), LockMode.None)
    .List<Parent>();

但收到"The value "System.Object[]" is not of type "Parent" and cannot be used in this generic collection."例外。

我试过HQL:

IEnumerable<Parent> parents = session.CreateQuery(@"
from Parent as parent
    left outer join (
                        select *
                        from Child as child
                        where child.Deleted = :deleted
                    ) as Child
        on parent.ParentId = Child.ParentId
where parent.Deleted = 0")
.SetParameter("deleted", false)
.List<Parent>();

但收到"Exception of type 'Antlr.Runtime.NoViableAltException' was thrown."例外。

我也尝试过CreateCriteria:

IEnumerable<Parent> parents = session.CreateCriteria<Parent>()
        .Add(Restrictions.Eq("Deleted", false))
        .CreateCriteria("Children", "children", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
        .Add(Restrictions.Eq("children.Deleted", false))
        .SetResultTransformer(Transformers.DistinctRootEntity)
        .List<Parent>();

但如果父母没有任何子女,则不会返回父母,这是错误的。

我还尝试了一个分离查询:

DetachedCriteria children = DetachedCriteria.For<Child>()
    .SetProjection(Projections.Property("Parent.ParentId"))
    .Add(Restrictions.Eq("Deleted", false));

IEnumerable<Parent> parents = session.CreateCriteria<Parent>()
    .CreateAlias("Children", "child", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
    .Add(Subqueries.PropertyIn("ParentId", children))
    .List<Parent>();

但这甚至可以从简单的检查中明确表示它不起作用......

有什么想法吗?

谢谢! :)

UPDATE \ FIX

好的,所以找到了一个解决方法\ fix ...虽然不是我想的最优雅的解决方案。

我在我的Parent映射文件中添加了一个过滤器,并将条件添加到我的one-to-many关系中。 Parent映射文件现在如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="OperationalExcellenceDiary.DomainModels"
                   namespace="OperationalExcellenceDiary.DomainModels.Entities">

    <class name="Parent" table="Parent" lazy="false">
        <id name="ParentId" column="ParentId">
            <generator class="native"/>
        </id>

        <set name="Children" inverse="false" cascade="all-delete-orphan" lazy="false">
            <key column="ParentId"/>
            <one-to-many class="Child"/>
            <filter name="ChildIsDeleted" condition="Deleted = 0" />
        </set>

        <property name="Deleted" type="bool">
            <column name="Deleted" default="0"/>
        </property>

    </class>

    <filter-def name="ChildIsDeleted"></filter-def>

</hibernate-mapping>

现在将过滤掉未删除的子项。

当我执行查询时,我只启用新创建的过滤器:

session.EnableFilter("ChildIsDeleted");
IEnumerable<Parent> parents = session.CreateCriteria<Parent>()
    .Add(Restrictions.Eq("Deleted", false))
    .List<Parent>();

它仍然不会创建派生表,但会返回所需的结果。因此,即使所有Children都标记为已删除,但Parent未标记为Parent,仍会返回{{1}}。

0 个答案:

没有答案