NHibernate – 使用Junction / Joiner表进行多对多查询

我在这里发现了非常相似的问题,但没有一个与我正在寻找的完全匹配。 我发现的两个最接近的线程是(是的,它们是不同的线程):

NHibernate多对多标准 (1)

NHibernate多对多标准 (2)

但是,我认为这两者都使用直接的多对多关系。 我实际上通过与联结表具有两个一对多关系来模拟多对多关系,这是非常标准的做法。 这是我的NHibernate映射:

文件:

          

属性:

            

乔伊纳:

        

所以我的问题与上面的第二个链接完全相同,但是使用了连接表。 所以:

给定一组属性ID,我希望运行一个查询,该查询为我提供了具有所有匹配属性的文件。 我可以轻松地对集合中的每个属性ID运行“n”查询,并比较每个列表中出现的文件ID的每个列表,但我觉得应该有一种更简单的方法可以一次使用一个查询完成所有操作。

例:

 File | Attributes ----------+----------------------------------------------------- foo.txt | (mode = read-only, view = visible) bar.txt | (mode = read-write, security = all, view = visible) duck.txt | (mode = read-only, view = hidden) goose.txt | (more = read-only, security = owner, view = visible) 

鉴于以下属性: mode = read-onlyview = visible ,我想只返回foo.txtgoose.txt

谁能帮我这个? 谢谢。

实现此目的的一种方法是创建由AND连接的多个子查询,因为必须找到/与搜索文件相关的许多属性

我在寻找名字/价值

第一个解决方案使用上层的名称/值对。 即用户选择的模式是只读的… (第二个将更容易一些,期望我们已经拥有搜索到的Atttributes的ID)

 // Below I am using C# properties, which I guess are correct // based on the mapping. Naming convention is more Java (camel) // but this should work with above mapping // (also - class name Contact, not File) Files file = null; // this is an alias used below // here the attributes collection represents search filter // ... settings for which is user looking for var attributes = new List { new Attrs{ name = "mode", value = "read-only" }, new Attrs{ name = "view", value = "visible" } }; // Let's start with definition of the outer/top query // which will return all files, which do meet all filter requirements var query = session.QueryOver(() => file); 

在下一步中,我们将遍历属性,即filter集合

 // here we will take each attribute and create a subquery // all these subqueries, will be joined with AND // so only these files, which do have all attributes, will be selected foreach (var attr in attributes) { // create the subquery, returning the FileId Attrs attribute = null; var subQueryForAttribute = QueryOver.Of() .JoinQueryOver(fa => fa.attr, () => attribute) .Select(x => x.file.id) ; // now, take name and value var name = attr.name; var value = attr.value; // and convert them into where condition subQueryForAttribute.Where(() => attribute.name == name); subQueryForAttribute.Where(() => attribute.value == value); // finally, add this subquery as a restriction to the top level query query.WithSubquery .WhereProperty(() => file.id) .In(subQueryForAttribute); } 

现在我们有一个查询,它已经准备好支持分页 – 因为我们正在处理一个扁平的文件结构。 因此,如果需要,我们可以使用Take和skip,然后获取搜索文件列表

 // query.Take(25); // query.Skip(100); var list = query.List(); 

这是一个查询,会产生这样的SELECT

 SELECT ... FROM files WHERE id IN (SELECT file_Id FROM files_attrs INNER JOIN attrs ON attrs.id = file_attrs.attr_id WHERE name = 'mode' AND value = 'read-only' ) AND id IN (SELECT file_Id FROM files_attrs INNER JOIN attrs ON attrs.id = file_attrs.attr_id WHERE name = 'view' AND value = 'visible' ) 

II按属性ID搜索

第二个解决方案,有更简单的启动条件,而不是属性(名称和值)我们已经有他们的ID(引自一个问题:)

给定一组属性ID,我希望运行一个查询,该查询为我提供了具有所有匹配属性的文件。

 // Below I am using C# properties, which I guess are correct // based on the mapping. Naming convention is more Java (camel) // but this should work with above mapping // (also - class name Files, not File) Files file = null; // this is an alias used below // here the attributeIds collection represents attributes to be found var attributeIds = new List { 1, 4, 5 }; // Let's again start with definition of the outer/top query // which will return all files, which do meet all filter requirements var query = session.QueryOver(() => file); 

接下来是通过已知ID集合的迭代,这些ID必须作为关系存在(所有这些 ID

 // here we will take each attribute and create a subquery // all these subqueries, will be joined with AND // so only these files, which do have all attributes, will be selected foreach (var attrId in attributeIds) { // create the subquery, returning the Files.id var subQueryForAttribute = QueryOver.Of() // no need to join, all the stuff is in the pairing table .Select(x => x.file.id) ; var id = attrId; // local variable // and convert them into where condition subQueryForAttribute.Where(pair => pair.attr.id == id); // finally, add this subquery as a restriction to the top level query query.WithSubquery .WhereProperty(() => file.id) .In(subQueryForAttribute); } var list = query.List(); 

具有已知IDS的解决方案更容易一些(SQL状态网络中需要的表更少)

注意:不得不说:很高兴看到,你已经引入了many-to-oneone-to-many而不是多对多。 我个人会说,这个例子显示,它可以带来多大的利润……即使使用复杂的filter也可以进行搜索

一些链接,以显示QueryOver : 查询HasMany引用 ,以及为什么不使用many-to-many映射的一些很好的理由: 多对多额外列nhibernate

我不确定这是否是你需要的:

     

something是属性,或者是要过滤的数据所在的列。

试试这个查询:

 Files fAlias = null; Attrs aAlias = null; var disjunction = new Disjunction(); disjunction.Add(Restrictions.On(() => aAlias.value) .IsLike("mode = read-only", MatchMode.Anywhere)); disjunction.Add(Restrictions.On(() => aAlias.value) .IsLike("view = visible", MatchMode.Anywhere)); var subquery = QueryOver.Of .Inner.JoinAlias(x => x.file, () => fAlias) .Inner.JoinAlias(x => x.attr, () => aAlias) .Where(disjunction) .Select(() => fAlias); var files = session.QueryOver .WithSubquery.WhereExists(subquery) .List();