[转载红鱼儿]kbmmw 开发点滴:kbmMW客户端提交事务的现场处理

多层应用中的事务处理,是必须的,如果处理不好,就会出现各种数据不同步的现象,无法投入使用。以前用ASTA实现的多层应用,是在客户端利用ASTA机 制,将要提交的数据集、执行的SQL及SP,统统生成到一个脚本中,在ASTA是TAstaParamList,然后一次性提交到服务器,在服务器端在执 行这个脚本时,开启事务,执行脚本,如果成功则Commit,失败则Rollback。

kbmMW提供了更好的事务处理机制,即可以在服务器端做事务处理(TkbmMWTransactionResolve)也可以在客户端处理(TkbmMWClientTransactionResolve)。

在客户端,只要利用kbmMWClientTransactionResolve.Resolve(Query1,Query2,...)即可实现事务控制,此方法返回True,表明事务提交成功。如果提交失败,服务器端自动Rollback,完成事务回滚。

有几个事件可以捕获提交失败信息。

1.kbmMWClientQuery.OnResolveError事件

2.kbmMWClientTransactionResolve.onResolveError事件

此外,当kbmMWClientQuery在kbmMWClientTransactionResolve提时失败时,kbmMWClientTransactionResolve会为kbmMWClientQuery生成具体的错误记录的信息,保存在kbmMWClientQuery属性中,该属性是kbmMWMemTable类型。

利用这几种方式,可以很好的处理事务失败后的错误提示等进一步处理。

问题是当提交失账时,会产生新的问题:

1.假设有T1,T2两个物理表,需要用户同时修改并提交

2.当用户对T1,T2都做了修改后,用kbmMWClientTransactionResolve.Resolve(T1,T2)提交给服务器,当t2提交失败时,比如主键重复,问题就来了。

3.如果用户修改了T2的主键重复错误,再次提交,这时,不会再产生提交错误。

4.重新打开T1,T2,会发现T1的修改丢失,而T2正常修改了。

 

这种情况,对用户的操作过程来讲,是大问题,明明改了业务数据,保存时产生错误,按错误提示修正了错误数据,再保存,系统保存成功,再次调出这笔业务数据,用户会发现,修改结果没有被正确的保存!

 

对开发者来说,就是要在事务提交失败后,能恢复失败前的现场,即要把T1,T2的Delta保存起来,不能因为提交失败,而毁掉T1,T2的修改!

 

试图查找kbmMW提供现成的机制(其实,感觉不应该发生这样的问题),没有找到。于是想一个方法,就是Resolve前将T1,T2保存起来,提交有错误再恢复。

用AllData方法不行

代码:

在BeforeResolve事件保存

procedure TForm2.kbmMWClientTransactionResolver1BeforeResolve(Sender: TObject;
  const ADatasets: TkbmMWClientCursorArray);
begin
  v1:=kbmMWClientQuery1.AllData;
  v2:=kbmMWClientQuery2.AllData;

end;

//提交不成功,试图恢复(这里只是测试,正常应恢复没有出错的Query,好能再一次提交)

procedure TForm2.Button2Click(Sender: TObject);
begin
  if not
kbmMWClientTransactionResolver1.Resolve([kbmMWClientQuery1,kbmMWClientQuery2,kbmMWClientQuery3])
then begin
   
kbmMWClientQuery1.AllData:=v1;//这句出错,提示非法的属性值
   
kbmMWClientQuery2.AllData:=v2;
  end;
end;

还有一种方法,就是把delta写入流,提交失败再恢复,不知是否可行?先放一下。

问题一定出在TkbmMWClientTransactionResolver.Resolve方法中,硬着头皮跟踪一下,咱这功力看大侠的套路,心惊胆颤,运气还不错,终于查到,问题出ProcessErrorTable方法中:

procedure TkbmMWCustomPooledCursor.ProcessErrorTable

在这个方法中,这里做了CheckPoint,把Delta干掉了!当提交的数据集没有返回ErrorTalbe时,系统认为这个数据集正确提交,所以做了CheckPoint。

    
// Check if no errortable defined.
    
if (FErrorTable.FieldCount=0) or
(FErrorTable.RecordCount<=0) then
    
begin
         
CheckPoint;
         
exit;
    
end;

原因找到了,一时间还想不出最好的修改方法来解决这个问题。试图找到一个属性来控制这种行为,也没有找到。等xalion来帮助了!

和xalion讨论该问题,也没有找到kbmMW已有的机制,对于上面提到的问题点,也可以确定。

再进一步分析一下ClientTransactionResolve.Resolve方法的执行过程,大体上分三步:

1.对提交的数据集,制作成Variant数组及Stream

2.将1生成的内容一次性提交到服务器端并接收返回的结果

3.利用返回结果对每一个提交的数据集进行ErrorTable处理,问题就出在这一步上。

首先,kbmMW从返回的结果,生成每个数据集的ErrorTable内容,然后根据ErrorTable,调用ProcessErrorTable方
法,ProcessErrorTable方法有两个主要目的:如果无错,则调用CheckPoint,取消这次提前的修改内容,有错,不调用
CheckPoint,触发OnResolveError事件。

对于一个数据集是否要Checkpoint,不应该取决于当前数据集是否有错,而应该由本次提要的结果来决定,就是说:当事务成功,则每一个数据集都应该做Checkpoint,事务不成功,则对于无错的数据集也不能Checkpoint!

按此想法,对ClientTransactionResolve.Resolve做了修改,测试上面遇到的问题,可以正确保存用户的修改。

修改方法是注册掉原ProcessErrorTable的调用,然后增加下面的代码,核心那行,我加黑了!

             
cnt:=0;
             
for i:=low(ADatasets) to high(ADatasets) do
             
begin
                  
with ADatasets[i] do
                  
begin
                       
if not IsDataModified then continue;
                       
v:=vResult[cnt];
                       
inc(cnt);
                       
if VarIsNull(v) then continue;
                       
if VarArrayHighBound(v,1)>2 then
                          
sFlags:=v[3]
                       
else
                          
sFlags:='';
                       
NoCheckpointOnError:=(pos('NOCHECKPOINTONERROR',sFlags)>0);

                       
DisableMasterDetail;
                       
try
                          
bm:=GetBookmark;
                          
try
                             
//如果整体事务成功或者当前表有错误
                             
if Result or (ErrorTable.RecordCount>0) then
                                
ProcessErrorTable(TransactionOperation<>mwtoResolve,NoCheckpointOnError);

                          
finally
                             
try
                                
GotoBookmark(bm);
                             
except
                             
end;
                             
FreeBookmark(bm);
                          
end;
                       
finally
                          
EnableMasterDetail;
                       
end;
                  
end;
             
end;

 

时间: 2024-10-26 11:24:34

[转载红鱼儿]kbmmw 开发点滴:kbmMW客户端提交事务的现场处理的相关文章

[转载红鱼儿]kbmmw 开发点滴:kbmMW在事务中批量执行SQL

按客户端使用事务更新多个表数据到kbmMW服务器,使用TkbmMWClientTransactionResolve, 可以一次性提交多个修改的数据集到服务器端,服务器利用事务来执行数据的更新,成功,提交事务,不成功,则Rollback事务.在实际开发中,可能遇到 在更新数据集的同时,还要执行单独的SQL,处理业务逻辑.方法是:用一个单独的TkbmMWClientQuery来执行.具体用法: kbmMWClietnQuery3.Query.Text:='Update T1 Set F2=1 whe

[转载红鱼儿]kbmmw 开发点滴:kbmMW:Unknown property:indexes

利用kbmMW的QueryServices,对数据集进行提交,当改用JSON格式时,客户端提交时,会产生这个错误,明明已经更新数据库,也产生这个错误!如果换成Bin格式,则正常. 能过查看kbmMWJSONStreamFormat单元,问题产生在这行代码:nIndexDefs:=nDefs.AsArray['indexes']; 可以这样理解: 客户端提交数据后,服务器会返回一个错误结果数据集,不管有无错误,都要返回这个结果集.客户端会把这个数据集加载到 ClientQuery.ErrorTab

[转载红鱼儿]kbmmw 开发点滴:kbmMW默认服务

这两天,用xalion的入门文章学习kbmMW的使用,当用kbmMW向导生成一个Service后,需要注册到kbmMWServer中,具体的方法在生成的Service中已经有说明: // INSTRUCTIONS FOR REGISTRATION/USAGE // ----------------------------------- // Please update the uses clause of the datamodule/form the TkbmMWServer is place

[转载红鱼儿]kbmmw 开发点滴:kbmMW数据集流化

kbmMW提供了QueryService,供我们在客户端象传统数据库应用一样,直接利用ClientQuery做SQL查询,以及提交,并且能够在客 户端以事务的方式提交多个数据集,应该说,QueryService为我们提供了非常好的便利,快速将两层应用转为多层应用,如果用过ASTA,就会发 现,QueryService在对两层应用的转化上,要比其实现的更完美,层次更清晰.但在实际项目中,如果考虑应用服务器对多平台的支持以及业务逻辑的 集中实现,那么仅仅使用QueryService是不够的,我们可能

[转载红鱼儿]kbmmw 开发点滴:kbmMW缓存机制

kbmMW提供了稳顾机制,分为服务端的Cache及客户端的Cache.以客户端为例,当客户端发出相同的数据查询请求时,kbmMW会在客户端的Cache中查找,如果找到,则不再向服务器请求数据. 为了启用一个ClientQuery使用稳存,首先要设置其以下两个属性: kbmMWClientQuery.Cached:=True;//打开数据集的稳存 kbmMWClientQuery.CacheFlags: mwcfDontAge - 缓存的内容不过期,一直在Cache保留 mwcfDontGarba

[转载红鱼儿]kbmmw 开发点滴:kbmmW服务端事务控制

关于kbmMW的事务控制,前文已经在客户端实现,通过kbmMWClientTransaction来控制多个数据集的提交,在客户端提交多数据集,实际上,是在客户端实现业务逻辑,并通过一个事务来完成数据处理,这是违反多层架构的做法,实际项目中,需要在服务端,往往是利用多个数据集进行业务处理,并且用事务来控制,那我们该怎么办呢?换句话说,如何在服务实现事务的控制. kbmMW在服务端,如同在客户端一样,为我们提供了方便的机制来处理事务. 下面的代码,转自kbmMW的新闻组: var tx: TkbmM

[转载红鱼儿]kbmmw 开发点滴:There be must at least one field

There be must at least one field 用uniDAC+kbmMW+SQL Server做服务器,当客户端查询时,出现这个错误,在QQ群请教,一位朋友说,在服务器端查询不对,憋了一天,最终,朋友改了TkbmMWUNIDACConnection,得以解决,原来是这样,如果uniDAC+SQL Server,则需要调用CoInitialize.而kbmMWUNIDACConnection中没有调用. 代码如下,红色是需要增加的代码,当然仅对于MS SQL Server,对于

[转载红鱼儿]kbmmw 开发点滴:解决QueryService重复查询问题

做一个简单的QueryService,在跟踪过程中发现,客户端一个查询,会被触发两次.怎么会这样,这严重影响服务器性能. 客户端的代码非常简单,就是执行一个SQL,通过QueryService返回结果:   kbmMWClientQuery2.Close;   kbmMWClientQuery2.Open; 于是利用服务器端的QueryService的事件OnQueryStatement进一步检查SQL的执行情况,把SQL写到Memo中: procedure TkbmMWQueryService

[转载红鱼儿]kbmmw 开发点滴:kbmMWEventService的本质

在kbmMW创建服务向导中,有个"Eventoperated service",其描述信息:同TkbmMWSimpleService一样,就是公布了一个附加的事件,来控制客户端请求,取代了ProcessRequest.   看一下TkbmMWEventService代码,原来是这样:   1.TkbmMWEventService = class(TkbmMWSimpleService) 这个事件服务类是从TkbmSimpleService继承的,那么,就具有了SimpleService