2010-09-16 81 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