Murphy的记事本

若教眼底无别离,不信人间有白头
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

利用接口特性构建可自动回收的Query对象池

Posted on 2009-03-19 17:40  Murphy(土豆)  阅读(398)  评论(0编辑  收藏  举报

在C/S系统开发中,需要操作数据库,最简便的方法是使用TADOQuery,通常的做法是创建一个TDataModule,然后组合一个或多个TADOQuery部 件进行数据库操作。

这种方法简单实用,但也常常会带来一些问题,例如:外部调用者必须小心的使用TADOQuery,如果在同一时刻多处同时使用TADOQuery,结果 将不可预知;又例如:TDataModule中的TADOQuery在需要多个组合操作的场合会出现不够用的情况,此时,只能自已动态创建,而在操作较少时,在TDataModule模块中申请多个 TADOQuery则又会显得多余;

有什么办法可以让TDataModule中的TADOQuery够用呢,动态创建是一个不错的方法,但是有个问题:在TDataModule中创建的TADOQuery对象必须要等外部使用完毕后才能释放,那么这个释放的工作就必须交由外部调用者来完成。看似挺合理,但如果外部调用者使用后忘记释放,就会造成内存泄漏,而且一个对象的生命周期由多个对象来决定,本来就是有问题的。

希望解决上述的问题时,想到了接口的生存期自管理特性,可以利用接口的特性,将TADOQuery置于接口管理之下,在外部调用者使用完成后,自动回收TADOQuery对象,代码如下:

  IRDataSet = interface(IUnknown)
    function Exec(ASql: String; AExecMode: Boolean = False): TDataSet;
  end;

  TRDataSet = class(TInterfacedObject, IRDataSet)
  private
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    FDataSet: TADOQuery;

    constructor Create(AConn: TADOConnection);
    destructor Destroy; override;
  end;

{ TRDataSet }

function TRDataSet._AddRef: Integer;
begin
  inherited _AddRef;
end;

function TRDataSet._Release: Integer;
begin
  inherited _Release;
  showmessage('释放了!');

  if Assigned(FDataSet) then
  begin
    FDataSet.Close;
    FreeAndNil(FDataSet);
  end;
end;

constructor TRDataSet.Create(AConn: TADOConnection);
begin
  FDataSet := TADOQuery.Create(nil);
  FDataSet.Connection := AConn;

  TADODataSet(FDataSet).CommandTimeout := 0;
  FDataSet.Prepared := True;
end;

destructor TRDataSet.Destroy;
begin
  inherited;
end;

如下引用:

var ADS: IRDataSet;
begin
  ADS := TRDataSet.Create(nil);
  showmessage('开始使用了!');
end;

貌似挺好,但要使用TADOQuery对象,还必须先定义一个IRDataSet,还是有点小麻烦。所以,应该有必要甩掉这个小尾巴。
这里采用的方法是:通过继承TCustomADODataSet来实现自已的TADOQuery,并使用具备生存期自管理功能。实现代码略。

既然可以对TADOQuery的生存周期进行管理了,那进一步想,可以根据外部使用的情况标记该TADOQuery的使用状态,在内部并不实时创建和释放。再进一步,将TADOQuery操作放到线程中执行,将可以有效的解决外部调用时界面僵死的状况。再再进一步,构建一个该对象的线程池,统一管理连接和使用,最后扩展相关代码后,便可形成一个相对通用且独立DB操作单元。

需要完成上述构思的关键类描述如下:

TCDataSet
继承于TCustomADODataSet的基类,完成自动回收的相关工作;

TCQuery
提供外部调用的可回收TADOQuery部件,用于支持一般查询及增删改操作;

TCDataSetThread
数据库操作管理线程;

TCDataSetConnect
封装TADOConnection后的可回收的连接部件,提供外部调用;

TDataFactory
对象工厂,负责完成基础的创建工作;


对象管理关系如下:

TDataFactory
    |
    --- TCDataSetConnect
               |
               --- TCDataSetThread
                          |
                          --- TCDataSet
                                  |
                                  --- TCQuery

由于实现代码太多,详细代码及示例请到:http://www.2ccc.com/article.asp?articleid=5119 下载,并欢迎提出意见交流!