日期:2008-06-02  浏览次数:20389 次

本文将对HQL查询文本的解析过程进行分析,这个可以说是NH中比较复杂的一块了(个人认为),涉及到的类也比较多。
建议阅读之前先深呼吸十下,看完之后脑袋成浆糊可不要找我哟。:-)


在HQL数据加载一文中,我们有提到QueryTranslator的创建过程,代码如下:

//*** SessionFactoryImpl.cs 429行 ***
private QueryTranslator GetQuery(string query, bool shallow)
{
   QueryCacheKey cacheKey = new QueryCacheKey(query, shallow);

   QueryTranslator q = (QueryTranslator) Get(cacheKey);
   if ( q==null) {
      q = new QueryTranslator(dialect);
      Put(cacheKey, q);
   }

   q.Compile(this, query, querySubstitutions, shallow);

   return q;
}
当创建(或从缓存取得)QueryTranslator对象后,就调用它的Compile方法对HQL文本进行解析,
参数querySubstitutions是用于指定要替换的关健字,它可在配置文件中给出。

//*** QueryTranslator.cs 116行 *** 
[MethodImpl(MethodImplOptions.Synchronized)]
public void Compile(ISessionFactoryImplementor factory, string queryString,
   IDictionary replacements, bool scalar) {
   if (!compiled) {
      this.factory = factory;
      this.replacements = replacements;
      this.shallowQuery = scalar;

      Compile(queryString);
   }
}
这个方法上加上了Synchronized选项,指定这是一个单线程的方法(同一时间只能有一个线程执行此方法,与使用Lock是等价的),不知出于什么原因考虑?

//*** QueryTranslator.cs 132行 ***
protected void Compile(string queryString) {
   this.queryString = queryString;

   try {
      ParserHelper.Parse(
         new PreprocessingParser(replacements),
         queryString,
         ParserHelper.HqlSeparators,
         this);
      RenderSql();
   }
   catch (QueryException qe) {
      qe.QueryString = queryString;
      throw qe;
   }
   catch (MappingException me)
   {
      throw me;
   }
   catch (Exception e)
   {
      log.Debug("unexpected query compilation problem", e);
      QueryException qe = new QueryException("Incorrect query syntax", e);
      qe.QueryString = queryString;
      throw qe;
   }

   PostInstantiate();

   compiled = true;
}
将解析HQL文本的任务交给ParserHelper处理,这是一个用于解析的帮助类,注意Parser方法的第一个参数是一个PreprocessingParser对象。

//*** ParserHelper.cs 25行 ***
public static void Parse(IParser p, string text, string seperators, QueryTranslator q)
{
   StringTokenizer tokens = new StringTokenizer(text, seperators, true);
   p.Start(q);
   foreach(string token in tokens) {
      p.Token(token, q);
   }
   p.End(q);
}
首先创建一个StringTokenizer对象,这是一个迭代HQL文本的类,稍后有详细分析。然后启动解析器,并迭代处理HQL文本片断。

所有的处理HQL文本片断的解析器都实现了IParser接口,
public interface IParser {
   void Token(string token, QueryTranslator q);
   void Start(QueryTranslator q);
   void End(QueryTranslator q);
}

StringTokenizer类

StringTokenizer类是一个实现了枚举接口IEnumerable的类, 用于迭代HQL文本中的片断,下面详细说明一下。

publ