2010-09-16 82 views
1

我正在使用NHibernate 2.1.2.4000GA。我試圖在HQL和標準API中使用SQL Server的CONTAINS函數。這在HQL工作正常:從HQL/Criteria API使用CONTAINS

CONTAINS(:value) 

但是,我需要限定有問題的表格。這工作正常:

CONTAINS(table.Column, :value) 

但是,我需要搜索我的表中的所有索引列。我嘗試這樣做:

CONTAINS(table.*, :value) 

,但我得到:

NHibernate.Hql.Ast.ANTLR.QuerySyntaxException : Exception of type 'Antlr.Runtime.MissingTokenException' was thrown. near line ... [select table.Id from Entities.Table table where CONTAINS(table.*,:p0) order by table.Id asc] 
    at NHibernate.Hql.Ast.ANTLR.ErrorCounter.ThrowQueryException() 
    at NHibernate.Hql.Ast.ANTLR.HqlParseEngine.Parse() 
    at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.Parse(Boolean isFilter) 
    at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.DoCompile(IDictionary`2 replacements, Boolean shallow, String collectionRole) 
    at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.Compile(IDictionary`2 replacements, Boolean shallow) 
    at NHibernate.Engine.Query.HQLQueryPlan..ctor(String hql, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory) 
    at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(String queryString, Boolean shallow, IDictionary`2 enabledFilters) 
    at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(String query, Boolean shallow) 
    at NHibernate.Impl.AbstractSessionImpl.CreateQuery(String queryString) 

所以它似乎HQL解析器的扼流圈上的星號。我想這樣做的:

CONTAINS(table.Column1, :value) or CONTAINS(table.Column2, :value) 

這不僅是低效的,它也能產生不正確的結果取決於:value全文謂詞。

我嘗試根據these instructions自定義我的方言,但這並沒有幫助,因爲您仍然留下相同的問題:指定table.*會導致HQL分析程序翻倒。

我想指定查詢取代:

<property name="query.substitutions">TABLECONTAINS(=CONTAINS(table.*,</property> 

,然後簡單地做:

TABLECONTAINS(:value) 

但是,這並不工作。我不知道爲什麼 - 根據由此產生的錯誤來判斷,替換不會發生,因爲查詢中仍然存在「TABLECONTAINS」。 。此外,這不會對所有的情況下工作,因爲我需要知道表的別名,並動態地替代它

所以,我滾了一個基於攔截的方法:

using System; 
using NHibernate; 
using NHibernate.SqlCommand; 

public class ContainsInterceptor : EmptyInterceptor 
{ 
    public override SqlString OnPrepareStatement(SqlString sql) 
    { 
     var indexOfTableContains = sql.IndexOfCaseInsensitive("TABLECONTAINS("); 

     if (indexOfTableContains != -1) 
     { 
      var sqlPart = sql.ToString(); 
      var aliasIndex = sqlPart.LastIndexOf("Table ", indexOfTableContains, StringComparison.Ordinal); 

      if (aliasIndex == -1) 
      { 
       return sql; 
      } 

      aliasIndex += "Table ".Length; 
      var alias = sqlPart.Substring(aliasIndex, sqlPart.IndexOf(" ", aliasIndex, StringComparison.Ordinal) - aliasIndex); 
      sql = sql.Replace("TABLECONTAINS(", "CONTAINS(" + alias + ".*,"); 
     } 

     return base.OnPrepareStatement(sql); 
    } 
} 

這作品,我現在可以睡在今晚,但我確實感到突然想參加倫敦即將到來的教皇隊伍,並大聲懺悔。

任何人都可以提出一個更好的方法來實現這一目標嗎?

回答

0

我會認爲自定義方言是處理這個問題的適當方法。你可以在this article找到一些指導。我已經使用這種方法來註冊特定於SQL Server的函數,如ISNULL以供我們的項目使用。

+0

謝謝,但不幸的是這並不能幫助(試過這種過,但忘了把我的問題)。基本上,你仍然有同樣的問題。如果我想指定表中的所有列,我需要編寫'contains(table。*,:term)',但是HQL解析器對此扼殺。更新我的問題... – 2010-09-16 12:35:31

+0

如果您直接註冊了包含tablename和。*屬性的函數,這可能會有所幫助。很明顯,你需要爲每個表單獨註冊,但是(理論上)應該還是會減少很多重複... – DanP 2010-09-16 12:48:03

+0

嗯......也注意到hibernate支持hql的轉義序列(例如:http ://www.coderanch.com/t/441409/ORM/java/Special-characters-hql) - 不知道NHib是否允許這樣做,儘管... – DanP 2010-09-16 12:53:22

0

爲什麼不使用ISQLQuery呢?

,仍可以檢索實體,見http://nhibernate.info/doc/nh/en/index.html#querysql-creating

+0

謝謝,但希望它很簡單。這兩個問題:我的代碼會複雜化,特別是我必須進行分頁和排序的代碼。標準API對於簡化和維護代碼非常有用。其次,查詢需要針對多個數據庫(SQL Server和SQLite)運行。但是等等 - 我如何對SQLite運行全文查詢?我不。全文謂詞由用戶過濾器決定。如果過濾器包含一個文本謂詞,那麼我將不得不使用SQL Server,否則我可能會使用SQLite(緩存)。這對查詢代碼透明。 – 2010-09-17 12:07:53