当前位置:首页>>ASP.NET教程>>Asp.NET综合技巧>>nhibernate源码分析之七: HQL数据加载
nhibernate源码分析之七: HQL数据加载
2009/11/8 13:19:18
NH中,HQL是一个十分强大的面向对象的查询语言,简单的说,就是不需要使用实际的表名和列名来查询数据,而改用类名和属性。
有两种方式来执行HQL数据加载,一种是直接使用ISession的Find方法,另一种是使用IQuery接口。

IQuery接口提供了一些额外的设置,最重要的就是分页了,这个和ICriteria差不多,另外一些就是设置参数的值了。

IQuery最终还是会调用ISession的Find方法,下面来分析一下IQuery中HQL语句的处理.

示例代码:

IQuery query = session.CreateQuery( " from User u where u.Name = :Name " )
query.SetString( "Name", "billy" );
IList users = query.List();

源码分析:

//*** SessionImpl.cs 1760行 ***/
public IQuery CreateQuery(string queryString)
{
   return new QueryImpl(queryString, this);
}
创建一个QueryImpl对象,并传递HQL查询文本和会话.
注意: IQuery的实现类QueryImpl是一个Internal类, 因此不能在nh程序集外直接创建.

//*** QueryImpl.cs 70行 **/
public IQuery SetParameter(int position, object val, IType type) {
   int size = values.Count;
   if ( position<size ) {
      values[position] = val;
      types[position] = type;
   }
   else {
      for (int i=0; i<position-size; i++) {
         values.Add(null);
         types.Add(null);
      }
      values.Add(val);
      types.Add(type);
   }
   return this;
}

public IQuery SetParameter(string name, object val, IType type) {
   namedParameters[name] = new TypedValue(type, val);
   return this;
}
在IQuery中,参数有两种情况,一种是命名参数,一种是未命名参数, 上面给出的示例代码是使用命名参数。
SetString方法直接调用SetParameter方法来设置命名参数,SetParameter参数有多个重载的版本,用于适应不同的IType, 如果要设置未命名参数,那么应通过参数的位置来设置。
TypedValue用对象来于保存参数的类型和值。

//*** QueryImpl.cs 47行 ***/
public virtual IList List() {
   IDictionary namedParams = new Hashtable( namedParameters );

   string query = BindParameterLists(namedParams);
   return session.Find(query, (object[]) values.ToArray(typeof(object)),
      (IType[])types.ToArray(typeof(IType)),
   selection, namedParams, lockModes);
}
在执行查找之前,必须先处理所有的集合参数,像in关健字就用到了集合参数。

//*** QueryImpl.cs 280行 ***
protected string BindParameterLists(IDictionary namedParams) {
   string query = queryString;
   foreach( DictionaryEntry de in namedParametersLists ) {
      query = BindParameterList( query, (string) de.Key, (TypedValue) de.Value, namedParams );
   }
   return query;
}
所有集合参数都存储在namedParametersLists集合中(可参见SetParameterList方法),这里通过枚举对各个集合参数进行绑定。

//*** QueryImpl.cs 288行 ***
private string BindParameterList(string queryString, string name,
   TypedValue typedList, IDictionary namedParams) {
   ICollection vals = (ICollection) typedList.Value;
   IType type = typedList.Type;
   StringBuilder list = new StringBuilder(16);
   int i=0;
   foreach( object obj in vals ) {
      string alias = name + i++ + StringHelper.Underscore;
      namedParams.Add( alias, new TypedValue( type, obj ) );
      list.Append( ':' + alias );
      if ( i < vals.Count ) list.Append( StringHelper.CommASPace );
   }
   return StringHelper.Replace( queryString, ':' + name, list.ToString() );
}
BindParameterList方法遍历值集合,然后将它们加入到命名参数集合(namedParams)中,最后修改HQL查询文本。
例如:在HQL中是" ... name in ( :Name )", 如果值集合有三个值,那么修改后的HQL是 "... name in ( :Name0_, :Name1_, :Name2_ ) ";

//*** SessionImpl.cs 1571行 ***
public IList Find(string query, object[] values, IType[] types,
   RowSelection selection,IDictionary namedParams, IDictionary lockModes) {

   QueryTranslator[] q = GetQueries(query, false);
   IList results = new ArrayList();

   dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called

   //execute the queries and return all result lists as a single list
   try {
      for (int i=0; i<q.Length; i++ ) {
         IList currentResults;
         try {
            currentResults = q[i].FindList(this, values, types, true, selection, namedParams, lockModes);
         }
         catch (Exception e) {
            throw new ADOException("Could not execute query", e);
         }

         for (int j=0;j<results.Count;j++) {
            currentResults.Add( results[j] );
         }
         results = currentResults;
      }
   }
   finally {
      dontFlushFromFind--;
   }
   return results;
}
根据HQL查询文本生成QueryTranslator对象,然后调用QueryTranslator的FindList方法返回查询结果。

//*** QueryTranslator.cs 1051行 ***
public IList FindList( ISessionImplementor session, object[] values, IType[] types,
   bool returnProxies, RowSelection selection, IDictionary namedParams, IDictionary lockModes) {
   return base.Find(session, values, types, returnProxies, selection, namedParams, lockModes);
}
FindList直接调用父类Loader的Find方法来查询数据,所有的数据加载(当然包括Criteria加载)最终都由Loader进行处理,关于这个类,将在后续的文章中单独进行分析。

//*** SessionImpl.cs 1616行***
private QueryTranslator[] GetQueries(string query, bool scalar) {
   // a query that naemes an interface or unmapped class in the from clause
   // is actually executed as multiple queries
   string[] concreteQueries = QueryTranslator.ConcreteQueries(query, factory);

   // take the union of the query spaces (ie the queried tables)
   QueryTranslator[] q = new QueryTranslator[concreteQueries.Length];
   ArrayList qs = new ArrayList();
   for (int i=0; i<concreteQueries.Length; i++ ) {
      q[i] = scalar ? factory.GetShallowQuery( concreteQueries[i] )
                    : factory.GetQuery( concreteQueries[i] );
     &nbs