让kbmMWClientQuery更新视图
先说下问题,有一个物理表t1,基于t1,建立一个视图v1,然后用ClientQuery查询视图:
ClientQuery.Query.Text:='Select f1,f2 from v1'
查询到结果后,增删改记录,然后:
ClientQuery.Resolve提交修改结果,问题出现:
当ClientQuery.TableName='t1'时,可以提交,等于'v1'时,无法提交,生成一个无字段的sql
Insert into v1 ( ) values ( )
那如何让ClientQuery.TableName='v1'时也能正常提交呢?
解决方法:在运行期修改Field.Origin值
ClientQuery.FieldByName('f1').Origin:='v1.f1';
ClientQuery.FieldByName('f2').Origin:='v1.f2';
或者:
ClientQuery.FieldByName('f1').Origin:='.f1';
ClientQuery.FieldByName('f2').Origin:='.f2';
或者:
ClientQuery.FieldByName('f1').Origin:='';
ClientQuery.FieldByName('f2').Origin:='';
视图能正常更新了!注意,必须在运行期用代码来修改Origin,设计期不行的。
进一步分析原因,在ClientQuery.Open后,ClientQuery.FieldByName('f1').Origin等于't1.f1',也就是查询视图,这里返回实际物理表名,在服务端解析时,如果ClientQuery.TableName=v1,与字段的Origin中的物理表名t1不一致,造成无法生成正确的sql语句。
具体的逻辑在TkbmMWCustomTableFieldResolver.BuildFieldFilter方法中实现,感兴趣可以去看看。
通过查看这个方法,得知,当设置Origin为空时,还受kbmMWUNIDACResolver.SkipFieldsWithoutOrigin属性控制,当为True时,则不处理这个字段,默认值为False,即处理Origin为空的字段。
写到这里,也许你已经想到,不仅对于View的更新操作,我们完全可以通过设置ClientQuery.TableName及Field.Origin属性,实现ClientQuery.Resolve的更新行为。这必须为kbmMW赞一个!
最后,感谢Q友不会呼吸274001335,帮我跟踪了好几天,最终搞清这块的逻辑,同时感谢作者kim在news group
上的支持与回复!
下面按不会呼吸的想法,利用Class Helper来修改ClientQuery.Open的行为:
//声明kbmMWClientQuery的Helper类: TkbmMWClientQueryHelper=class helper for TkbmMWClientQuery procedure Open; end; //重新实现kbmMWClientQuery.Open方法 { TkbmMWClientQueryHelper } procedure TkbmMWClientQueryHelper.Open; var i:Integer; begin inherited Open; for I := 0 to Self.FieldCount-1 do begin if Self.Fields[i].Origin.Substring(0,1)<>'.' then //如果是虚字段则设置为一个不存在的表名,保证不生成到sql里去更新 // Self.Fields[i].Origin:=Self.TableName+'.'+Self.Fields[i].FieldName Self.Fields[i].Origin:='.'+Self.Fields[i].FieldName // Self.Fields[i].Origin:='' else //如果是sql中的虚字段则设置为一个不存在的表名,保证不生成到sql里去更新,避免出错. Self.Fields[i].Origin:='xxxxxxxxxx.'+Self.Fields[i].FieldName; end; end;
对于Helper Class,执行ClientQuery.Open的单元,必须引用这个Helper类所在的单元,否则不会执行上面的代码!
上面的方法,只是解决当查询一个视图时,自动不提交虚拟字段,那对于下面朋友遇到的问题,又如何处理呢?
朋友HY遇到这样的问题,查询视图,还要修改视图并提交修改结果,因为是视图,有些字段不需要提交到后台。
查了下新闻组,作者提到几种方法可以控制提交哪些字段到后台:
方法一:
通过设置TField.Origin属性,需要提交的填上内容,不需要为空,然后设置服务端的Resolver.SkipFieldWithoutOrigin=True;
方法二:
通过设置TField.ProviderFlags属性,不需要提交的将pfInUpdate去掉。通过这个属性还可以进一步控制字段是否出现在Where子句中。
procedure TPersonManagerFrame.qOneTableAfterOpen(DataSet: TDataSet); begin inherited; //不更新这两个字段. DataSet.FieldByName('FPositionName').ProviderFlags := DataSet.FieldByName('FPositionName').ProviderFlags- [pfInUpdate,pfInWhere]; DataSet.FieldByName('FPositionID').ProviderFlags := DataSet.FieldByName('FPositionID').ProviderFlags- [pfInUpdate,pfInWhere]; end;
方法三:
处理Resolver的事件:ExcludeFromUpdateInsert和ExcludeFromWhere,简单返回字段的ProviderFlags,类似方法二。