OQL中的Name Resolver, Mapping Resolver

OQL中的两个概念对象

case 1: 把属性替换为列名,对象替换为表名

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  UserId,UserCode,UserName  from   User

结果:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  Usr_Id,Usr_Code,Usr_Name  from  Sys_User


case 2:
User对象指定了别名,所以a.UserId要根据别名确定属性UserId来自User对象,然后将UserId替换为列名
有子查询,所以t.CreateBy需要到子查询里面搜索CreateBy属性来自哪个对象,这个case中可以查到来自Org,所以需要替换为Org对象上CreateBy属性映射的自段名

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  a. *  
from   User  a 
inner   join (
    
select   distinct  CreateBy  from  Org  where  OrgId = ?
) t 
on  t.CreateBy = a.UserId

结果:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  a. *  
from  Sys_User a 
inner   join (
    
select   distinct  Create_By  from  Sys_Org  where  Org_Id = ?
) t 
on  t.Create_By = a.Usr_Id


case 3: 深入下去,情况就越复杂。
这个case跟case 2的唯一区别是,子查询里面select出来的属性又指定了别名,并且跟属性名字还是一样。所以t.CreateBy不应当替换

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  a. *  
from   User  a 
inner   join (
    
select   distinct  CreateBy  as  CreateBy  from  Org  where  OrgId = ?
) t 
on  t.CreateBy = a.UserId

结果:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  a. *  
from  Sys_User a 
inner   join (
    
select   distinct  Create_By  as  CreateBy  from  Sys_Org  where  Org_Id = ?
) t 
on  t.CreateBy = a.Usr_Id


case 4: 更复杂一点的情况。一个假想的语句,贫血模型中界面显示经常有类似需求,业务上大致意义可能是: 一个父子关联关系,存在一张表中。按父节点名称模糊查询,显示所有子节点信息,包括父节点名称、子节点的创建用户
进行映射替换时,Name Resolver的决策就比较难
决策过程进入子查询后,遇到2个对象。对于t.ParentName,子查询的select列表指定了别名,所以不应当替换。对于t.CreateBy、t.OrgCode这两个,在子查询中得进行猜测。首先子查询select列表中明确指定的字段里找不到这两个,所以得考察未明确指定列名的地方,只发现一个c.*,所以确定t.CreateBy、t.OrgCode这两个属性一定来自c.*,根据别名c找到Org as c中的Org对象,这两个属性是需要替换为自段名的。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  t. * ,a.UserName  as  CreateUser
from   User  a
inner   join (
    
select  c. * ,p.OrgName  as  ParentName  from  Org p,Org  as  c
    
where  c.ParentId = p.OrgId
) t 
on  t.CreateBy = a.UserId
where  t.ParentName  like  ?parent
order   by  t.OrgCode

结果:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> select  t. * ,a.Usr_Name  as  CreateUser
from  Sys_User a
inner   join (
    
select  c. * ,p.Org_Name  as  ParentName  from  Sys_Org p,Sys_Org  as  c
    
where  c.Parent_ID = p.Org_Id
) t 
on  t.Create_By = a.Usr_Id
where  t.ParentName  like  ?parent
order   by  t.Org_Code


完全从语句中确定对象-别名、属性-别名的关系,是不可能的,例如上面case 4中,如果在子查询的select列表里面发现两个*,该如何决策?
方法1: 在语法上做限制,使得通过OQL语句可以明确确定这种关系
方法2: 搜索O-R映射的元数据,进一步分析确认。这一方面要求在应用启动时对所有映射元数据有个整体加载的过程(类似Hibernate的SessionFactory创建时的编译处理),另一方面不同对象上同名属性、不同命名空间下的同名对象,仍然不可避免出现这种问题
方法3: 留下这个空间,交由Client端灵活运用。存在这种问题的地方必须明确指定出选择列表

从OQL语句里面搜索,试图确定对象、属性、别名之间的关系,这一职责赋给Name Resolver
维护映射信息,确定对象、属性应当映射到哪些表、自段,怎样映射,这一职责由Mapping Resolver/Manager承担

Name Resolver可以准确做出判断时,告诉Mapping Resolver进行精确的映射处理。当Name Resolver无法决策时,同样将接力棒交给Mapping Resolver,这时Mapping Resolver可以采取一次模糊搜索。例如在一个ObjectQuery范围里面,搜索Attach到这个ObjectQuery的所有Type映射信息(要求用户显示调用ObjectQuery.Attach(Type type, string name)这样的方法,避免对所有元数据的整体加载);或者加载所有映射元数据,从整个映射元数据中模糊搜索(名字有冲突时误判的可能性比较大,让用户显示Attach将缩小范围,有利于冲突的解决)。

ObjectQuery可以返回实体对象集合,或者返回一个IList<object>,或者DataTable。NHibernate选择返回IList<object>倒是可以省却一件事情,因为如果返回DataTable,对每个返回字段,需要有个名字对应,而不光是索引访问。上面看到,执行查询的时候,属姓名已经被替换为字段名,如果就这样返回DataTable,提供的这个功能就别扭了。首先查询语句里面写的是对象、属性名,而返回的DataTable中却是数据库字段名,使用这个DataTable的时候还是用数据库字段名访问?
所以如果选择返回DataTable,最好的方式是,Mapping Resolver将语句中的对象、属性替换为数据库的表名、字段名,这样才能执行这个SQL查询。但对于select列表,还得以别名、属性名方式返回,尤其是对于select a.*, b.*这样的情况,Name Resolver需要找出a, b具体是什么对象,由Mapping Resolver找出这些对象有哪些属性名,考虑到子查询等各种情况,情况很复杂。
所以简单的选择,还是返回IList<object>。

更复杂的情况,就是实体间的关联关系,体现在OQL中,同样要求Name Resolver与Mapping Resolver之间的协同工作。
结果:
Name Resolver与Mapping Resolver之间的协同比较多,从交互机制上看关联关系比较强;但它们拥有完全不同的专业知识,在不同的领域上工作,所以要求良好的分离。

猜你喜欢

转载自jackyhongvip.iteye.com/blog/1559110