我现在和朋友一起开发一个小项目,把每个表结构都定义为一个类,然后每条记录都为一个对象,然后读写都对该对象进行操作,这样的开发方法好吗?感觉有点烦索,但又好像用起来比较方便,到底我们这种方法是不是在走弯路呢?迷茫。高人指点下!

解决方案 »

  1.   

    那是浪费时间,你们的操作,本身就封装好了的
    表,数据集,都已经在delphi中可直接操作的。
      

  2.   

    如果是20年前你这么干绝对是最好的办法,因为你这本身是再做数据库,只是没有通用引擎而已。现在你这么干绝对是浪费时间,除非是你的东西要求极其保密而且数据量很小!20年前你最多看到dBase和FoxBase,现在你能看到的数据库当时统统没有或者根本就见不到
      

  3.   

    在数据库开发中分为两中模式的,一种是dataset,另一种是对象的,也就是楼主现在想用的这种方式
    这两种方式各有优点,并且一直在争论到底哪一种更好
    delphi中对dataset的支持非常好,用delphi的人也对该模式运用十分熟练,如果你在delphi版问这个问题,绝大部分人都不会支持你的做法的,如果你去java或者.net,支持你的人可能会多些
    如果你是基于VCL架构来开发的话,我还是建议你使用dataset,如果你是基于delphi.net,那么我建议你使用ECO,其实你要做的,ECO已经做得很好了,没必要重复前人的工作
    我个人觉得,面向对象的数据库开发方式是以后的趋势,dataset的方式只适合(中)小型应用
      

  4.   

    用ECO就解决了,不过小项目,实在是画蛇添足,没必要。
      

  5.   

    像楼主这种干法比较像JAVA那套,JAVA最大优势就是简单问题复杂话,呵呵。
      

  6.   

    基类如下:unit Unit4;interfaceuses
      SysUtils, Classes, DB, ADODB;const MAX_LINE = '20';
    type
       TMyField = Class(TObject)
      private
        FIsChange: Boolean;
        FFieldName: string;
        FFieldValue: string;
        FDataType: TFieldType;
        procedure SetFieldValue(const Value: string);
        public
         property IsChange : Boolean read FIsChange;    //是否改变,只读
         property FieldValue:string read FFieldValue write SetFieldValue; //字段值
         property FieldName:string read FFieldName;   //字段名 ,只读
         property DataType:TFieldType read FDataType; //字段的数据类型,只读
         constructor Create(aFieldName:string;aFieldType:TFieldType;aValue:string='');
         destructor Destroy;override;
        end;
    type
        TCommonInfo = class(TObject)    //该类可以作为其它类的基类进行扩展
      private
        FFieldCount: integer;
        UpdateFields:string;       //要修改的字值和值串 ,保存到数据库时用
        FFields:array of TMyField;
        FQry:TAdoQuery;
        FTableName: String;
        FKeyFields: string;
        function GetFieldType(aFieldName:String):TFieldType; overload;
        function GetFieldType(Index:integer):TFieldType;  overload;
        function IsValidOp(aop:String):Boolean;  //是否为有效的操作符
        function GetMainCondition:string;    //根据主键获得条件,获取 where 后面的条件子句
        function UniteField(aField:TMyField;aand:boolean=true;aValue:String=''):String;  //连接字段值和条件,中间的参数代表是否加and运算符
        function UniteStr(aFieldName:string;aFieldValue:string;aand:boolean=true):String;//联接含有字段串的字段,加引号 ,最后一个参数代表是否加 and
        function UniteInt(aFieldName:string;aFieldValue:string;aand:boolean=true):String;//接接不加引号的字段 ,最后一个参数代表是否加 and
        function IsValidFieldIndex(index:integer):boolean;  //检查索引是否有效
        procedure InitField;      //初始化字段数组
        function IsValidFieldName(aFieldName:string) : Boolean;   //是否为有效的字段名
        function IsKeyField(aFieldName:string):boolean;  //字段名是否为主键
        function SelectSQL(aSQL:string):Boolean;    //执行返回结果的SQL,并给相应的域赋值
      protected
        property TableName:String read FTableName;   //只读,表名
        function GetInfoWithCondition(aFieldName,aOp,aValue: string):Boolean;overload; //根据条件获取信息  三个参数分别为:操作数,操作符,值
        function GetInfoWithCondition(aCondition:String):boolean;overload;  //根据条件取数据
        function IsChanged:Boolean;                //判断所有的字段值是否改变
        function GetFieldValue(aFieldName:string) : String; overload; //根据字段名获取字段值
        function getFieldValue(Index:integer):string;  overload;    //根据索引获取字段值
        function SetFieldValue(aFieldName:String;aValue:String):boolean; overload; //根据字段名设置字段名
        function SetFieldValue(Index:integer;aValue:String):Boolean;overload;   //根据索引设置字段值
        function GetFieldName(Index:integer):String;   //根据索引获取字段名
      public
         function PerformSQL(aSql:string):boolean;  //值行不返回结果的SQL
         property FieldCount:integer read FFieldCount;  //字段总数
         property KeyFields:string read FKeyFields;   //主键字段
         function SaveToDataBase():Boolean;            //保存当前数据到数据库
         constructor Create(aTableName:String;Constr:String;aKeyFields:String); //参数分别为:表名,连接字符串,主键的的集合,如:ID;ANAME;
         Destructor Destroy; override;
         end;implementation{ TMyField }constructor TMyField.Create(aFieldName: string;aFieldType: TFieldType;aValue:String='');
    begin
    self.FFieldName := aFieldName;
    //showmessage(self.FFieldName);
    self.FisChange := false;
    self.FDataType := aFieldType;
    self.FFieldValue := aValue;
    inherited create;
    end;destructor TMyField.Destroy;
    begin
      inherited;
    end;procedure TMyField.SetFieldValue(const Value: string);
    begin
    if FFieldValue <> Value then
      begin
      FFieldValue := Value;
      FIsChange := true;
    //  showMessage('change'+Value);
      end;
    end;{ TCommonInfo }constructor TCommonInfo.Create(aTableName, Constr: String; aKeyFields:String);
    begin
    FTableName := aTableName;
    FQry := TADOQuery.Create(nil);
    FQry.ConnectionString := Constr;
    FKeyFields := aKeyFields;
    InitField;
    inherited create;
    end;destructor TCommonInfo.Destroy;
    var
    i:integer;
    begin
      FQry.Free;
      for i := 0 to high(FFIelds) do
        begin
          if assigned(FFIelds[i]) then
           FFields[i].Free;
        end;
      inherited;
    end;function TCommonInfo.GetFieldValue(aFieldName: string): String;
    var
    i:integer;
    begin
     result := '';
    if not IsValidFIeldName(aFieldName) then exit;
     for i := 0 to FFieldCount-1 do
      if UpperCase(FFields[i].FieldName) = UpperCase(aFieldName) then
        begin
          result := FFields[i].FieldValue;
          break;
        end;
    end;function TCommonInfo.GetFieldValue(Index: integer): string;
    begin
    result := '';
    if (Index < 0) or (Index >= FieldCount) then
      raise Exception.Create('Field Index out of bounds!');
     result := FFields[Index].FieldValue;
    end;function TCommonInfo.GetFieldName(Index: integer): String;
    begin
    result := '';
    if not IsValidFieldIndex(Index) then exit;
    result := FFIelds[Index].FieldName;
    end;procedure TCommonInfo.InitField;
    var
    i:integer;
    begin
      with FQry do
        begin
          close;
          sql.Text := Format('select Top %s * from %s where 1<>2',[MAX_LINE,FTableName]);
          open;
        end;
     FFieldCount:= FQry.FieldCount;
     SetLength(FFields,FFieldCount);
     for i:=0 to FFieldCount-1 do
       begin
         FFields[i] := TMyField.Create(Fqry.Fields[i].FieldName,Fqry.Fields[i].DataType);
       end;
    end;function TCommonInfo.IsChanged: Boolean;
    var
    i:integer;
    begin
    result := false;
    UpdateFields := '';
      for i:=0 to FFieldCount-1 do
        begin
         if self.FFields[i].IsChange = true then
            begin
              Result := true;
              if Length(UpdateFields) <= 0 then
                UpdateFields := UpdateFields + UniteField(FFields[i],false)   //将改变的字段和值连起来,保存到数据库的时候用
              else UpdateFields := UpdateFields + ' , ' + UniteField(FFields[i],false); //false 代表不加 and连接符
            end
         end;
    end;
      

  7.   

    function TCommonInfo.SetFieldValue(aFieldName, aValue: String): boolean;
    var
    i:integer;
    begin
     result := false;
    if not IsValidFIeldName(aFieldName) then begin {showmessage('set value exit');}exit;end;
     for i := 0 to FFIeldCount-1 do
       begin
        if UpperCase(FFields[i].FieldName) = UpperCase(aFieldName) then
           begin
            FFIelds[i].FieldValue := aValue;
            break;
           end;
        end;
    result := true;
    end;function TCommonInfo.SaveToDataBase: Boolean;
    var
    i:integer;
    s:string;
    begin
    result := false;
    if IsChanged then
      s := 'Update ' + FTableName + ' set ' + UpdateFields + 'Where 1=1 ' + GetMainCondition
    else begin {showmessage('not change!');}exit;end;
     // showmessage(s);
    result := self.PerformSQL(s);
    end;function TCommonInfo.SetFieldValue(Index: integer; aValue: String): Boolean;
    begin
    result := false;
    if not IsValidFieldIndex(Index) then exit;
     FFIelds[Index].FieldValue := aValue;
    result := true;
    end;
    function TCommonInfo.IsValidFieldIndex(index:integer): boolean;
    begin
    result := false;
      if (Index < 0) or (Index >= FieldCount) then
       begin
        raise Exception.Create('Field Index out of bounds!');
        exit;
       end;
    result := true;
    end;function TCommonInfo.IsKeyField(aFieldName:string): boolean;
    begin
    result := pos(UpperCase(aFieldName),UpperCase(FKeyFields)) > 0;
    //showmessage(aFieldName+':'+FKeyFIelds+':'+booltostr(result,true));
    end;function TCommonInfo.IsValidFieldName(aFieldName: string): Boolean;
    var
    i:integer;
    begin
    result := true;
     for i := 0 to FFIeldCount-1 do
       begin
        if UpperCase(FFIelds[i].FieldName) = UpperCase(aFieldName) then
          begin
            exit;
          end;
        end;
    result := false;
    end;function TCommonInfo.UniteInt(aFieldName, aFieldValue: string;aand:boolean=true): String;
    begin
    if aand then
    result := Format(' and %s = %s ',[aFieldName,aFieldValue])
    else result := Format(' %s = %s ',[aFieldName,aFieldValue]);
    //showmessage('UniteInt result:'+result);
    end;function TCommonInfo.UniteStr(aFieldName, aFieldValue: string;aand:boolean=true): String;
    begin
    if aand then
    result := Format(' and %s = ''%s'' ',[aFieldName,aFieldValue])
    else result := Format(' %s = ''%s'' ',[aFieldName,aFieldValue]);
    end;function TCommonInfo.GetMainCondition: string;
    var
    i:integer;
    begin
    result := '';
    //showmessage('getmaincondition'); 这句执行到了,IsKeyField这句有总题
    for i := 0 to FFieldCount -1 do
       begin
          if IsKeyField(FFields[i].FieldName) then
            begin
            //  showmessage(FFIelds[i].FieldName);
            result := result + UniteField(FFIelds[i]);
            end;
       end;
    end;function TCommonInfo.UniteField(aField: TMyField;aand:boolean=true;aValue:string=''): String;
    begin
    result := '';
    if aField.DataType = ftstring then
       begin
       if aValue <>'' then
          result := UniteStr(aField.FieldName,aValue,aand)
       else
       result := UniteStr(aField.FieldName,aField.FieldValue,aand)
       end
    else
        begin
        if aValue <>'' then
          result := UniteInt(aField.FieldName,aValue,aand)
        else
        result := UniteInt(aField.FieldName,aField.FieldValue,aand);
        end;
    end;function TCommonInfo.PerformSQL(aSql: string): boolean;
    var
    i:integer;
    begin
    result := false;
      try
       with FQry do
         begin
           close;
           sql.Text := aSql;
           ExecSQL;
         end;
      except
        result := false;
        exit;
      end;
    result := true;
    end;function TCommonInfo.GetInfoWithCondition(aFieldName:string;aOp:string;aValue: string): Boolean;
    var
    i:integer;
    s:string;
    begin
    result := false;
    if not IsValidOp(aop)  then begin  exit;end;
    if not IsValidFieldName(aFieldName) then begin exit;end;
    if GetFieldType(aFieldName) = ftString then
    s :=Format('select top %s  * From  %s where %s %s ''%s''',[MAX_LINE,FTableName,aFieldName,aop,aValue])
    else  s :=Format('select top %s  * From  %s where %s %s %s',[MAX_LINE,FTableName,aFieldName,aop,aValue]);
    result := self.SelectSQL(s)
    end;
    function TCommonInfo.IsValidOp(aop: String): Boolean;
    begin
    result := (aop = '<') or (aop = '>') or (aop = '>=') or (aop = '<=') or (aop = '<>') or (aop = '=');
    end;function TCommonInfo.GetFieldType(aFieldName: String): TFieldType;
    var
    i:integer;
    begin
    if not IsValidFieldName(aFieldName) then exit;
       for i :=0 to FFIeldCount -1 do
          begin
           if UpperCase(FFIelds[i].FieldName) = UpperCase(aFieldName) then
             begin
             result := FFields[i].DataType;
             break
             end;
          end
    end;function TCommonInfo.GetFieldType(Index: integer):TFieldType;
    begin
    if not IsValidFieldIndex(Index) then exit;
     result := FFields[Index].DataType;
    end;function TCommonInfo.GetInfoWithCondition(aCondition: String): boolean;
    var
    s:String;
    begin
    result := false;
    s :=Format('select top %s  * From  %s where %s',[MAX_LINE,FTableName,aCondition]);
    if SelectSQL(s) then
    result := true;
    end;function TCommonInfo.SelectSQL(aSQL: string): Boolean;
    var
    i:integer;
    begin
    result := true;
    try
    with FQry do
       begin
        close;
         sql.Text := aSQL;
         open
        end;
     for i := 0 to FFIeldCount -1 do
            begin
              FreeAndNil(FFields[i]);
              if FQry.IsEmpty then
                FFIelds[i] := TMyField.Create(FQry.Fields[i].FieldName,FQry.Fields[i].DataType,'')
              else
              FFields[i] := TMyField.Create(FQry.Fields[i].FieldName,FQry.Fields[i].DataType,Fqry.Fields[i].AsString);
    //          showmessage(FFIelds[i].FieldValue);
            end
    except
     result := false;
    end;
    end;end.
      

  8.   

    下面是一个在上面的基础上扩展后的类:
    unit Unit5;interfaceuses
    SysUtils, Classes,unit4;Type
     TStuInfo = class(TCommonInfo)
      private
        procedure SetAddr(const Value: string);
        procedure SetAge(const Value: string);
        procedure SetID(const Value: String);
        procedure SetRes(const Value: String);
        procedure SetSex(const Value: string);
        procedure SetSname(const Value: String);
        procedure SetTel(const Value: String);
        function GetID: String;
        function GetAddr: string;
        function GetAge: string;
        function GetRes: String;
        function GetSex: string;
        function GetSname: String;
        function GetTel: String;
         public
         property ID:String read GetID write SetID;
         property Sname:String read GetSname write SetSname;
         property Sex:string read GetSex write SetSex;
         property Age:string read GetAge write SetAge;
         property Tel:String read GetTel write SetTel;
         property Addr:string read GetAddr write SetAddr;
         property Res:String read GetRes write SetRes;
         Constructor Create(TableName,ConnStr,KeyFields:string);
         Destructor Destroy; override;
         function GetInfoWithId(aid:String):boolean;
         function GetInfoWithSname(aSname:String):Boolean;
         function GetInfoWithSex(aSex:String):Boolean;
         function GetInfoWithTel(aTel:String):Boolean;
         function GetInfoWithAge(aAge:String):Boolean;
         function GetInfoWithAddr(aAddr:String):Boolean;
         function GetInfoWithRes(aRes:String):boolean;
         end;implementation{ TStuInfo }constructor TStuInfo.Create(TableName, ConnStr, KeyFields: string);
    begin
    inherited;
    end;destructor TStuInfo.Destroy;
    begin
      inherited;
    end;function TStuInfo.GetAddr: string;
    begin
    result := GetFieldValue('addr');
    end;function TStuInfo.GetAge: string;
    begin
    result := GetFieldValue('age');
    end;function TStuInfo.GetID: String;
    begin
    result := GetFieldValue('id');
    end;function TStuInfo.GetInfoWithAddr(aAddr: String): Boolean;
    begin
    result := GetInfoWithCondition('Addr','=',aAddr);
    end;function TStuInfo.GetInfoWithAge(aAge: String): Boolean;
    begin
    result := GetInfoWithCondition('Age','=',aAge);
    end;function TStuInfo.GetInfoWithId(aid: String): boolean;
    begin
    result := GetInfoWithCondition('Id','=',aId);
    end;function TStuInfo.GetInfoWithRes(aRes: String): boolean;
    begin
    result := GetInfoWithCondition('Res','=',aRes);
    end;function TStuInfo.GetInfoWithSex(aSex: String): Boolean;
    begin
    result := GetInfoWithCondition('Sex','=',aSex);
    end;function TStuInfo.GetInfoWithSname(aSname: String): Boolean;
    begin
    result := GetInfoWithCondition('Sname','=',aSname);
    end;function TStuInfo.GetInfoWithTel(aTel: String): Boolean;
    begin
    result := GetInfoWithCondition('Tel','=',aTel);
    end;function TStuInfo.GetRes: String;
    begin
    result := GetFieldValue('res');
    end;function TStuInfo.GetSex: string;
    begin
    result := GetFieldValue('sex');
    end;function TStuInfo.GetSname: String;
    begin
    result := GetFieldValue('Sname');
    end;function TStuInfo.GetTel: String;
    begin
    result := GetFieldValue('tel');
    end;procedure TStuInfo.SetAddr(const Value: string);
    begin
    SetFieldValue('addr',Value);
    end;procedure TStuInfo.SetAge(const Value: string);
    begin
    SetFieldValue('Age',Value);
    end;procedure TStuInfo.SetID(const Value: String);
    begin
    SetFieldValue('id',Value);
    end;procedure TStuInfo.SetRes(const Value: String);
    begin
    SetFieldValue('Res',Value);
    end;procedure TStuInfo.SetSex(const Value: string);
    begin
    SetFieldValue('sex',Value);
    end;procedure TStuInfo.SetSname(const Value: String);
    begin
    SetFieldValue('sname',Value);
    end;procedure TStuInfo.SetTel(const Value: String);
    begin
    SetFieldValue('Tel',Value);
    end;end.
      

  9.   

    晕死,原来楼主开头所说的和代码的东西完全不是一回事啊。我现在和朋友一起开发一个小项目,把每个表结构都定义为一个类,然后每条记录都为一个对象,然后读写都对该对象进行操作,这样的开发方法好吗?>>>>>>>>>>>>>>>>实际上不是那么回事,譬如一个字段名称改了或者数据库加了一个字段,你还不是要修改基类,比修改代码还罗嗦。最好看看ADO的原生资料及Delphi对ADO的引用后再说,就他们都保证不了这些。
      

  10.   

    TO 星星农场,请看下面的几行代码:
    with FQry do
        begin
          close;
          sql.Text := Format('select Top %s * from %s where 1<>2',[MAX_LINE,FTableName]);
          open;
        end;
     FFieldCount:= FQry.FieldCount;
     SetLength(FFields,FFieldCount);
     for i:=0 to FFieldCount-1 do
       begin
         FFields[i] := TMyField.Create(Fqry.Fields[i].FieldName,Fqry.Fields[i].DataType);
       end;如果数据库的结构改了这个通用的基类的结构也会跟着改的,然后在派生类中添加一个属性和读写方法就可以了!当然这个类并没有考虑到Blob字段!
      

  11.   

    当然这个类并没有考虑到Blob字段!---------------------------------------------------------------
    频繁地使用Blob字段的系统性能会好吗?Blob字段这样的数据应该放在数据库里?
      

  12.   

    支持楼主,应该有限的使用ORM
      

  13.   

    不是没有用,只是火候未到而已!
    http://blog.csdn.net/badboy19800808/archive/2006/04/28/695610.aspx
      

  14.   

    楼主,我先前公司的产品架构就是把数据库的信息都转化成类,对象的。
    多层的结构,客户端没有如adoquery,clientdataset之类的东西,只有业务类,对象,及显示控件(没有数据感知控件)。
    客户端有个业务的基类,它最主要是利用RTTI方法,读取后代类的实例,将它的数据信息传到中间层和接收中间层的数据把它转化客户端业务类的信息。
    中间层也有个基类,也是利用RTTI将后代的实例数据转化成一组sql命令,由它进行对数据库的操作,同样从数据库得到数据转成对象的信息,传给客户端。
    在客户端写好了n多的方法,比如将对象的所有信息显示到界面等等
    经过了这样的封装,并不是像有些人讲的为了后期维护方便,最重要的是提高代码复用,加速以后项目开发速度。公司做了n个项目了(基本都是大几十万的单),都是同一个架构(当然基类也在不断维护扩充)。还有很多好处,比如客户端的灵活性等等,不一一多说。当然也有缺点,效率肯定不如直接用dataset高等
    要提醒楼主的是,这样的基类编写不是一两天搞定的,需要积累,实践,沉淀。(公司的底层基类是经过几年才逐渐成熟的)
    如果只是为了一个,两个项目而去弄这个,那没必要。
      

  15.   

    相当于给本来就很臃肿的公司结构再加一层中间领导层。从效率来讲,最理想的公司当然是一个老板,其余人全部是works;
    在老板忙活不过来的时候可以考虑加中间管理层。这样的话,相对高效的做法也应该是完全n叉树。
    1-n-n*n
    如果在关系数据库已经可以处理的情况下;再添加中间管理结构,肯定要以牺牲运行效率为代价。
      

  16.   

    估计批评都基本上说完了,只能说你的想法很好,但是,这样做确实没有什么必要,就仅此而已,我们讲求的是效率,如果你在子模块中只需要很少的代码就可以实现普通模式中的一堆代码,那才是OO的精髓所在。还有就是有效域问题,每一个记录为一个对象,没错,但是,有的数据库设计是设计到多表的,很可能是一个记录或者2个记录为一个对象,甚至是一个表的1条记录+另外一些表的1条记录为一个对象。就像单据(票据)一样,一张单据可以看成一个对象,这单据的每一个消费项目看成一个对象,或者是某天的营业额为一个对象,在不同范畴,一个对象的理解是不同的,无论如何,如何看待对象,都要从开发效率以及功能出发,去考虑问题,而不是硬追求这种所谓的OO。OO靠的就是要灵活。