2010-09-16 3 views
1

Я использую NHibernate 2.1.2.4000GA. Я пытаюсь использовать функцию SQL Server CONTAINS из HQL и API критериев. Это отлично работает в HQL:Использование CONTAINS из API HQL/Criteria

Однако мне нужно квалифицировать таблицу, о которой идет речь. Это отлично работает:

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 задыхается от этого. Обновление моего вопроса ... –

+0

Возможно, это помогло бы, если бы вы зарегистрировали функцию, включающую имя tablename и свойство. *.Очевидно, вам понадобится отдельная регистрация для каждой таблицы, но это должно (теоретически) по-прежнему сокращать повторение совсем немного ... – DanP

+0

Хммм ... также заметил, что hibernate поддерживает escape-последовательности для hql (пример: http : //www.coderanch.com/t/441409/ORM/java/Special-characters-hql) - не уверен, что NHib разрешает это, хотя ... – DanP

0

Почему бы не использовать ISQLQuery вместо этого?

Вы все еще можете получить объекты, см http://nhibernate.info/doc/nh/en/index.html#querysql-creating

+0

Спасибо, но желаю, чтобы все было так просто. Две проблемы с этим: мой код будет взрываться по сложности, особенно код, который я должен выполнить подкачки и сортировку. Критерии API были благом для получения этого кода кратким и поддерживаемым. Во-вторых, запросы должны выполняться против нескольких БД (SQL Server и SQLite). Но подождите - как я могу запустить полнотекстовый запрос для SQLite? Я не. Полнотекстовый предикат продиктован фильтрами пользователей. Если в фильтр включен предикат текста, мне придется использовать SQL Server, иначе я мог бы уйти с SQLite (кешем). Это происходит прозрачно с кодом запроса. –

Смежные вопросы