【问题标题】:Querying with NHibernate使用 NHibernate 查询
【发布时间】:2009-05-08 03:54:42
【问题描述】:

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

下面是配置xml。仅显示配方。

我希望能够从输入的关键字中按食谱标题查询食谱 还有成分名称中的成分。

例如,您可以输入“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 中对我来说很容易。

马尔科姆

  <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>

【问题讨论】:

    标签: nhibernate hql nhibernate-criteria


    【解决方案1】:

    要构建动态查询,我会使用条件 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>();
    

    编辑:

    对于急切加载,您可以设置获取模式:

    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
    • 如果您真的打算使用子对象,为什么要使用 FetchMode.Join 而不是 FetchMode.Eager 来处理这些负载?
    • 当您对关键字进行硬编码时,我看不出它是如何动态的。如果我给你一串用空格分隔的单词,你会怎么查询???
    • @Malcolm:我不太明白这个问题。这个实现不是动态的,它是一个展示它是如何工作的例子。动态添加表达式非常容易 - 而不是构建可能有语法错误的 HQL 字符串(例如,当没有给出过滤器并且您编写一个空的 WHERE 子句时)。在我们的项目中,我什至会动态添加子查询和连接,仅在需要时。使用标准,这很简单,使用 HQL 你会遇到麻烦。
    • @Chad:FetchMode.Join 和 FetchMode.Eager 是一样的。 hibernate.org/hib_docs/v3/api/org/hibernate/FetchMode.html
    【解决方案2】:

    Stefan 和 Sathish 的示例都将 % 运算符连接到 SQL 中。这是不必要的,因为 Restrictions.Like(nhib 2.0+)和 Expression.Like(v2.0 之前)有 3 个带有 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's example

    【讨论】:

      【解决方案3】:

      这是上面带有动态关键字的条件

      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>();
      

      【讨论】:

        猜你喜欢
        • 2011-02-27
        • 1970-01-01
        • 2010-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多