【问题标题】:Querying a many-to-many collection or how to include a many-to-many table in a criteria query?查询多对多集合或如何在条件查询中包含多对多表?
【发布时间】:2011-07-20 12:31:37
【问题描述】:

我对 NHibernate 的世界很陌生,我似乎无法使用条件查询来解决这个问题:查询多对多关系或查询集合(集合/包)实体。我搜索了互联网并查看了我们拥有的所有 NHibernate 书籍,但我找不到我的“挑战”的具体答案。

我已经为我要解决的问题制作了一个简化示例。我有一个带有书籍的表格,一个带有类别的表格和一个带有每本书类别的多对多表格。以下是一些技术细节:

数据结构:

create table tableBook
(
    BkId       integer   not null default autoincrement,    
    BkTitle    char(40)  not null,
    BkWriter   char(40)  not null,
    primary key (BkId)
);

create table tableCategory
(
    CatId       integer   not null default autoincrement,    
    CatCode     char(3)   not null,
    CatDesc     char(40),
    primary key (CatId)
);

create table tableCategoriesPerBook
(
    CpbId        integer         not null default autoincrement,
    CpbBkId      integer         not null, /*foreign key to tableBook*/
    CpbCatId     integer         not null, /*foreign key to tableCategory*/
    primary key (CpbId)
);

alter table tableCategoriesPerBook add foreign key FK_CpbBkId (CpbBkId) references tableBook (BkId) on update Restrict on delete Cascade;
alter table tableCategoriesPerBook add foreign key FK_CpbCatId (CpbCatId) references tableCategory (CatId) on update Restrict on delete Cascade;

create unique index idx_CpbCatId_CpbBkId on tableCategoriesPerBook (CpbCatId, CpbBkId);

C# 类:

public class BookEntity
{
    public virtual Int32 BookId { get; set; }
    public virtual string BookTitle { get; set; }
    public virtual string BookWriter { get; set; }

    private readonly IEnumerable<CategoryEntity> _categories = new ObservableCollection<CategoryEntity>();
    public virtual IEnumerable<CategoryEntity> Categories
    {
        get { return _categories; }            
    }
}

public class CategoryEntity
{
    public virtual Int32 CategoryId { get; set; }
    public virtual string CategoryCode { get; set; }
    public virtual string CategoryDesc { get; set; }
}

NHibernate 映射:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Domain" assembly="Domain" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Domain.BookEntity" table="tableBook">
    <id name="BookId" column="BkId" type="Int32">
      <generator class="native" />
    </id>
    <property name="BookTitle" column="BkTitle" type="string" length="40"/>
    <property name="BookWriter" column="BkWriter" type="string" length="40"/>    
    <idbag name="_categories" access="field" table="tableCategoriesPerBook">        
        <collection-id type="Int32" column="CpbId">
          <generator class="native"/>
        </collection-id>        
        <key column="CpbBkId" property-ref="BkId"/>        
        <many-to-many column="CpbCatId" class="Domain.CategoryEntity, Domain" />
    </idbag>
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Domain" assembly="Domain" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Domain.CategoryEntity" table="tableCategory">    
    <id name="CategoryId" column="CatId" type="Int32">
      <generator class="native" />
    </id>
    <property name="CategoryCode" column="CatCode" type="string" length="3" />
    <property name="CategoryDesc" column="CatDesc" type="string" length="40" />    
  </class>
</hibernate-mapping>

我的问题:是否可以查询(使用 ICriteria 和/或分离标准)数据库,以便我获得属于我指定的类别之一的书籍(例如:在 catA 或 catB 中,可以也是“和”)?我想在查询中优化这一点,而不是在 C# 中(因为我需要先从数据库中读取所有书籍,然后才能根据它们的标签集合过滤对象)。如果我手动编写 SQL,我会生成如下内容:

SELECT * FROM tableBook                                                                                                                           
WHERE EXISTS 
    (
     SELECT 1 
     FROM   tableCategoriesPerBook 
            INNER JOIN tableCategory on (CpbCatId = CatId and CpbBkId = BkId) 
     WHERE  CatCode in ('001', '002')
    )

由于我没有 tableCategoriesPerBook 的实体,因此我看不到使用条件查询访问此表的方法。而且我宁愿不使用以下方法添加一些手写的 SQL 表达式:

criteria.Add(Expression.Sql("exists(.....)");

最后一个重要因素:我使用的是棕地数据库,所以我无法更改结构!这就是我必须在数据库方面进行的工作。

【问题讨论】:

    标签: nhibernate collections many-to-many icriteria querying


    【解决方案1】:

    这很简单。您可以使用分离的条件。

    DetachedCriteria bookCategoryCriteria = DetachedCriteria.For<BookEntity>("bookCat");
    bookCategoryCriteria
        .CreateAlias("Categories", "cat", JointType.LeftOuterJoin)
        .Add(Restrictions.In("cat.CategoryCode", categories)
        .Add(Restrictions.Eq("bookCat.BookId", "book.BookId")
        .SetProjection(Projections.Id());
    
    Session.CreateCriteria<BookEntity>("book")
       .Add(Subqueries.Exists(bookCategoryCriteria));
    

    【讨论】:

    • 嗨 Yads,非常感谢您的帮助。我看过很多其他的 stackoverflow 帖子,但没有一个能让我更接近解决这个问题。您的代码示例几乎为我做到了! .Add(Restrictions.Eq("bookCat.BookId", "book.BookId") 行不正确,但这对我来说很容易解决。我用这一行替换了它:.Add(Property.ForName("bookCat.BookId").EqProperty("book.BookId"))。非常感谢您的帮助。
    猜你喜欢
    • 2014-04-04
    • 2021-09-23
    • 2023-03-12
    • 2012-06-08
    • 2019-01-04
    • 2021-11-14
    • 2010-09-12
    • 1970-01-01
    相关资源
    最近更新 更多