delphi支持泛型后,曾经想这样.但GetValue里不知道怎么写好了.type TXValue<T> = class private FIsNull: Boolean; FValue: T; function GetValue: T; procedure SetValue(const Value: T); function GetIsNull: Boolean; public constructor Create; procedure SetNull; public property XValue: T read GetValue write SetValue; property IsNull: Boolean read GetIsNull; end;...function TXValue<T>.GetValue: T; begin if FIsNull then ???? raise Exception.Create('The Value is: Null'); else Result := FValue; end;
个人偏爱用record形式 用工具根据Table结构生成对应的record定义以及针对这些record的读取、增加、删除、更新函数, 如: Function InsertGuest(var G : TGuestRecord; var NewID : integer ) : Boolean; 使用起来方便,用户层只需要整理好这个record的数据,就可以调用相应的函数来增加删除修改读取了,完全不理会到底是怎么增加的,查询也提供一个Record,附带一个各值的计算规格,以及min ,max值得注意的是以下情况:1.多表查询,2.单表查询部分字段的值(以及多表查询时,某些表只查询部分字段的情况, 再生产一个RecordToStr的过程,就方便作Socket的三层了
TPerson = class
private
...
public
property 姓名: TField;
property 性别: TField;
property 出生日期: TField;
end;
可是 TField 可以有 AsString, AsInteger, AsFloat...多种写法.数据类型不严谨.
TPerson = class
private
...
public
property 姓名: string;
property 性别: Boolean;
property 出生日期: TDateTime;
end;
后来感觉仅仅样还不够, 如果数据库里的值是NULL就有麻烦了.曾经也试着传TDataSet,感觉也不方便.
我觉得还是用delphi的三层架构吧
type
TData = reacord ???
感觉照搬HB的方式是不行,借鉴一此东西还是可以的.Delphi的三层又能怎么样?
TXValue<T> = class
private
FIsNull: Boolean;
FValue: T;
function GetValue: T;
procedure SetValue(const Value: T);
function GetIsNull: Boolean;
public
constructor Create;
procedure SetNull;
public
property XValue: T read GetValue write SetValue;
property IsNull: Boolean read GetIsNull;
end;...function TXValue<T>.GetValue: T;
begin
if FIsNull then
???? raise Exception.Create('The Value is: Null');
else
Result := FValue;
end;
其它类型,使用者自己去转换
//------------------------------------
kwer
(逐水)
http://download.csdn.net/source/795600回头下载学习一下.
//------------------------------------
sz_haitao
(解释型delphi?notes)
就一个asString就行了
其它类型,使用者自己去转换
考虑过这样,后来觉得这样不能编译时检查类型了.
//-------------------------------------abclm
(♬挥霍的青春♬)
多多指教.
//---------------------------------------XD19861130
(门前一棵草)
哎,别拿我开涮了, 我这点水....
//--------------------------------------
private
FFieldName: string;
FData: TClientDataSet;
public
constructor create(AValueObj: TValueObject);
procedure SetNull; //设置NULL值
property IsNull;
end;TIntValue = class(TXValue);
TDateTimeValue = class(TXValue);
...
基本上和TField的子类对应.TValueObject = class
priate
FData: TClientDataSet;
public
procedure MergeChangeLog;
procedure CancelUpdates;
...将CDS有用的方法/属性"暴露"出来.
property Modified: Boolean;
end;//由工具生成
TPerson = class(TValueObject)
public
姓名: TStringValue;
生日: TDateTimeValue;
...
end;TDBXXX = class
public
//根据CDS.Delta生成insert.update, delete语句.
procedure SaveToDB(AValueObj: TValueObject);
...
end;
想起我的MIS框架:上方的browser(每记录占1行),下面是edit(半屏都是1条记录的各个字段)
load(把上方的当前记录的各个字段显示到下面的各个Tedit)和save()也都是提供按钮由用户按
具体的行为则是自动进行的,无须为每个table写对应的加载和保存代码
这样,根本没感觉有使用hibernate的需要。。
大体的思路是TXValue保存对应的数据库表的 列名. TXValue的子类的直接取TVolueObject内部CDS对应 字段 的值.TIntValue = class(TXValue)
public
property Value: Integer read GetIntValue;
end;function TIntValue.GetIntValue: Integer;
begin
FData.FieldByName(FFieldName).AsInteger; //如果是TDateTimeValue则AsDateTime;
end;TValueObject的内部用TClientDataSet,方便数据的"克隆",并且可以执行"撤消".保存"结点"等功能.TValueObject的子类对应数据表, 代码可以写个工具来生成.比如上面的TPerson;
1. 写FieldByName('XXX').Value时可能会拼错字段名. 另外偶尔字段名变的时候还要检查代码,一处一处改. (或用工具).所以大多时候要双击数据集加入字段.
2. TField的 AsXXXX有时候方便,有时候也会带出问题.
3. 查询返回TDataSet时意义不明确. TDataSet应该是算是"记录"的集合. 用主键查的时候至多返回一条记录.另外. DELPHI的DBX中有了TDBXRow.
//------------------
sz_haitao
(解释型delphi?notes)
"这样,根本没感觉有使用hibernate的需要。。"想在DELPHI中全照搬过来可能行不通. 但我觉得HB里的不少东西还是值得借鉴的, 想办法在DELPHI中实现/模仿也是挺有乐趣的.
TDataField = class(TPersistent)
private
FCaption: string;
FName : string;
FIsNull : boolean;
procedure SetCaption( const Value: string );
procedure SetName( const Value: string );
public
constructor Create( Name : string ='' ); virtual;
published
property IsNull : Boolean read FIsNull;
property FieldName: string read FName write SetName;
property Caption: string read FCaption write SetCaption;
end; TFInteger = class( TDataField )
private
FValue: Integer;
function GetValue(): Variant;
procedure SetValue( const Value: Variant );
procedure SetIntVal( const Value: Integer );
function GetStrValue: string;
published
property FieldName;
property Caption;
property Value: variant read GetValue write SetValue;
property AsInteger: Integer read FValue write SetIntVal;
property ToString: string read GetStrValue;
end;
...
TFDateTime = class(TDataField)
...
1. TDataField的FieldName是不是可以隐藏掉.
TDataSetProxy 的 constructor Create( aDataSet: TDataSet; KeyIndex: Integer =0 ); virtual;在创建时已经执行"绑定"了.2. property Value: variant read GetValue write SetValue;
是不是可以不要这个属性.既然已经细分了TDataField类型,(如Fint, Fnumber, Fdatetime.)
用这个属性在赋值的时候不能进进编译时值的检查.是不是可以去掉.3. 设置NULL值如何处理.虽然可以使用上面的property Value: variant, 不如直接有个SetNULL的方法.
TDataSetProxy = class( TPersistent )
private
FDataSet : TDataSet; // 数据成员
KeyIndex : Integer;
FKeyFieldName: string;
FFieldList: TObjectList;// 保存字段类型实体
FCount : Integer;
FRTTI : TClassRTTI;
FDataSetConnected: Boolean;
private
procedure Initialize; virtual;
public
constructor Create( aDataSet: TDataSet; KeyIndex: Integer =0 ); virtual;
destructor Destroy; override;
protected
function fnValidProperty(): Boolean;
function GetDataField(Index: Integer): TObject;
function GetDataEntity(FieldName: string): TObject; function GetIntegerField( Index: Integer ): TFInteger; virtual;
function GetNumberField( Index: Integer ) : TFNumber; virtual;
function GetStringField( Index: Integer ) : TFString; virtual;
function GetVariantField( Index: Integer ): TFVariant; virtual;
function GetBooleanField( Index: Integer ): TFBoolean; virtual;
function GetDateTimeField( Index: Integer ): TFDateTime; virtual;
function GetBlobField( Index: Integer ): TFBlob; virtual;
public
function GeTDataFieldValue( DataEntity: TDataField ): Variant;
procedure BeginEdit();
procedure EndEdit();
//*********数据集操作*********************
procedure First();
function Next() : Boolean;
function Prior(): Boolean;
procedure Last();
function Locate( Value: Variant ): Boolean; overload; // 定位
function Locate( EntityField: TDataField ): Boolean; overload; function Delete( KeyValue: Variant ): Boolean; overload;
function Delete( KeyField: TDataField ): Boolean;overload; //删除实体
procedure Insert(); overload; // 实体的全部成员插入数据库
procedure Insert( DataFields: array of TDataField ); overload;
procedure Update( KeyField: TDataField ); overload; // 修改全部实体内容
procedure Update( KeyField: TDataField; DataFields: array of TDataField ); overload; public
property DataSet : TDataSet read FDataSet;
property KeyField : string read FKeyFieldName; //关键字段
property Connected : Boolean read FDataSetConnected write FDataSetConnected;
property Count : Integer read FCount;
property FieldList[Index: Integer]: TObject read GetDataField; default;//index是可信任的
property FieldByName[FieldName: string]: TObject read GetDataEntity;
end;在程序代码中定义了 THR_Employee = class(TDataSetProxy)
published
property Emp_id : TFString index 0 read GetStringField;
property Emp_name : TFString index 1 read GetStringField;
property Birthday : TFDateTime index 2 read GetDateTimeField;
property Photo : TFBlob index 3 read GetBlobField;
property Memo : TFString index 4 read GetStringField;
end;1. THR_Employee表示一个员工还是一组员工?
2. THR_Employee是个(ValueObject)值对象还是什么?
3. THR_Employee继承自TDataSetProxy是不是知道的太多了?
4. 如果再有TOrder, TOrderItem都承继自TDataSetProxy, 提交时事务如何处理?
5. procedure Update( KeyField: TDataField; DataFields: array of TDataField ); overload;
更新前"键"应该是已知的,而不应该让"使用者"去指定.先说这么多,不周之处请见谅.
用工具根据Table结构生成对应的record定义以及针对这些record的读取、增加、删除、更新函数,
如:
Function InsertGuest(var G : TGuestRecord; var NewID : integer ) : Boolean;
使用起来方便,用户层只需要整理好这个record的数据,就可以调用相应的函数来增加删除修改读取了,完全不理会到底是怎么增加的,查询也提供一个Record,附带一个各值的计算规格,以及min ,max值得注意的是以下情况:1.多表查询,2.单表查询部分字段的值(以及多表查询时,某些表只查询部分字段的情况,
再生产一个RecordToStr的过程,就方便作Socket的三层了
如果表结构因为其它应用(如asp/jsp)需要而变化了,delphi程序也需要重新生成pas文件、重新编译吗?
这种情况几乎是不可能的,除非业务永远不会有变化或者变化的非常非常小.界面和业务逻辑决定了数据类型.当用户提出在某一界面上添加/移除一个数据信息时,或者需要保存更多的数据信息时,数据类型都会变化.当业务逻辑变化,假如(仅假如)个人信用等级和家属信用等级挂钩时,数据类型也会变化.我上面指的"数据类型"应该是class或record定义的复合类型.
其它使用到这个表的应用要求增加字段,而暂时不影响另外的某些应用,是很正常的
一年没更新DELPHI..原来还支持C++的模板来了...