使用NHibernate查询

7

我是NHibernate的新手,正在尝试学习如何查询我的数据。

以下是配置xml。只显示了recipe。

我想通过输入的关键词从recipetitle和ingredientname中查询食谱。

所以你可能会输入"pasta wine"。

这是我尝试过的,但是给我一个错误。

    hql = "from Recipe r " +
    "left join r.Images " +
    "inner join r.User " +
    "inner join r.Ingredients i " +
    "where i.IngredientName Like '%pasta%' OR i.IngredientName Like '%wine%' OR r.RecipeTitle Like '%pasta' OR r.RecipeTitle Like '%wine%'";

我希望你也能预加载集合。 我的查询方式正确吗?我需要能够根据搜索条件构建查询字符串,这在 SQL 中很容易实现。 Malcolm
  <class name="Recipe" table="Recipes" xmlns="urn:nhibernate-mapping-2.2">
    <id name="RecipeID" type="Int32" column="RecipeID">
      <generator class="identity" />
    </id>
    <property name="RecipeTitle" type="String">
      <column name="RecipeTitle" />
    </property>
    <property name="Completed" type="Boolean">
      <column name="Completed" />
    </property>
    <property name="ModifiedOn" type="DateTime">
      <column name="ModifiedOn" />
    </property>
    <property name="Rating" type="Double">
      <column name="Rating" />
    </property>
    <property name="PrepTime" type="Int32">
      <column name="PrepTime" />
    </property>
    <property name="CookTime" type="Int32">
      <column name="CookTime" />
    </property>
    <property name="Method" type="String">
      <column name="Method" />
    </property>
    <bag name="Images" inverse="true" cascade="all">
      <key column="RecipeID" />
      <one-to-many class="OurRecipes.Domain.RecipeImage, OurRecipes.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
    <many-to-one name="Category" column="CategoryID" />
    <bag name="Comments" inverse="true" cascade="all">
      <key column="RecipeID" />
      <one-to-many class="OurRecipes.Domain.Comment, OurRecipes.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
    <many-to-one name="User" column="EnteredByID" />
    <bag name="Ingredients" inverse="true" cascade="all">
      <key column="RecipeID" />
      <one-to-many class="OurRecipes.Domain.Ingredient, OurRecipes.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
3个回答

22

为了构建动态查询,我会使用Criteria API。这样做可以使动态查询更加稳定,因为您不需要使用字符串操作来构建它。

ICriteria query = Session.CreateCriteria(typeof(Recipe), "r")
  .CreateCriteria("Ingredients", "i", JoinType.InnerJoin)
  .Add(
    Expression.Disjunction() // OR
      .Add(Expression.Like("i.IngredientName", "%pasta%"))
      .Add(Expression.Like("i.IngredientName", "%wine%"))
      .Add(Expression.Like("r.RecipeTitle", "%pasta%"))
      .Add(Expression.Like("r.RecipeTitle", "%wine%")));

List<Recipe> result = query.List<Recipe>();

编辑:

为了进行急切加载,您可以设置提取模式(fetch-mode):

ICriteria query = Session.CreateCriteria(typeof(Recipe), "r")
  .SetFetchMode("Images", FetchMode.Join)
  .SetFetchMode("Comments", FetchMode.Join)
  .SetFetchMode("Ingredients", FetchMode.Join)

但我不建议这样做,因为你会得到与图像数、评论数和配料数相乘的结果。所以如果你有4张图片、2条评论和12个配料,你会得到你的菜谱96次。你没有意识到这一点,因为NHibernate将它们重新组合在一起,但它会在应用程序和数据库之间生成流量。因此最好让NHibernate使用单独的查询来加载它。


再作一个修改以展示动态查询组合。

// filter arguments, all are optional and should be omitted if null
List<string> keywords;
TimeSpan? minCookingTime;
TimeSpan? maxCookingTime;
int? minRating;
int? maxRating;

ICriteria query = Session.CreateCriteria(typeof(Recipe), "r");

if (keyword != null)
{
  // optional join
  query.CreateCriteria("Ingredients", "i", JoinType.InnerJoin);

  // add keyword search on ingredientName and RecipeTitle
  var disjunction = Expression.Disjunction();
  foreach (string keyword in keywords)
  {
    string pattern = String.Format("%{0}%", keyword);
    disjunction
      .Add(Expression.Like("i.IngredientName", pattern))
      .Add(Expression.Like("r.RecipeTitle", pattern)); 
  }
  query.Add(disjunction)
}

if (minCookingTime != null)
{
  query.Add(Expression.Ge(r.CookingTime, minCookingTime.Value));
}
if (maxCookingTime != null)
{
  query.Add(Expression.Le(r.CookingTime, maxCookingTime.Value));
}

if (minRating != null)
{
  query.Add(Expression.Ge(r.Rating, minRating.Value));
}
if (maxRating != null)
{
  query.Add(Expression.Le(r.Rating, maxRating.Value));
}

为了解决这个问题,您可以使用DistinctRootEntityResultTransformer。 - Frederik Gheysels
如果您真的打算使用子对象,为什么要在这些加载中使用FetchMode.Join而不是FetchMode.Eager呢? - Chad Ruppert
1
@Chad:FetchMode.Join和FetchMode.Eager是相同的。https://www.hibernate.org/hib_docs/v3/api/org/hibernate/FetchMode.html - Stefan Steinegger
好的,你能给我一个例子吗?假设有一个单词字符串,创建一个Criteria查询,假设正在搜索的字段是RecipeTitle。我不明白你会怎么做??? - Malcolm
@Malcolm:添加了一个动态查询如何组成的示例。当然,这取决于具体情况,但我认为你现在可以自己构建它了。 - Stefan Steinegger
显示剩余4条评论

5

Stefan和Sathish的例子都将%运算符连接到SQL中。这是不必要的,因为Restrictions.Like(nhib 2.0+)和Expression.Like(v2.0之前)都有带有MatchMode参数的三个版本。

Disjunction keywordsCriteria = Restrictions.Disjunction();
foreach (var keyword in keywords)
{
    keywordsCriteria.Add(Restrictions.Like("i.IngredientName", keyword, MatchMode.Anywhere));
    keywordsCriteria.Add(Restrictions.Like("r.RecipeTitle", keyword, MatchMode.Anywhere));
}

NHibernate Search也支持全文查询。请参考Ayende的示例以获取更多详细信息。


1

这是带有动态关键词的上述标准

string searchQuery = "wine pasta";

ICriteria query = Session.CreateCriteria(typeof(Recipe), "r")
                    .CreateCriteria("Ingredients", "i", JoinType.InnerJoin)
                    .SetFetchMode("Images", FetchMode.Join)
                    .SetFetchMode("Comments", FetchMode.Join)
                    .SetFetchMode("Ingredients", FetchMode.Join)
                    .SetResultTransformer(new DistinctRootEntityResultTransformer());

var keywords = searchQuery.Split(' ');

Disjunction keywordsCriteria = Restrictions.Disjunction();
foreach (var keyword in keywords)
{
    keywordsCriteria.Add(Restrictions.Like("i.IngredientName", string.Format("%{0}%", keyword)));
    keywordsCriteria.Add(Restrictions.Like("r.RecipeTitle", string.Format("%{0}%", keyword)));
}

query.Add(keywordsCriteria);

List<Recipe> result = query.List<Recipe>();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接