在Hibernate中有PO对象和POJO对象,
POJO = pure old java object or plain ordinary java object or what ever.
PO = persisent object 持久对象
就是说在一些Object/Relation Mapping工具中,能够做到维护数据库表记录的persisent object完全是一个符合Java Bean规范的纯Java对象,没有增加别的属性和方法。
持久对象实际上必须对应数据库中的entity,以如下数据库QQGroup为例,其中有5张表,Member为成员表,一个成员具有成果Researchs,也可能受到警告Warnings。
- CREATE DATABASE [QQGroup]
- GO
-
- USE [QQGroup]
- GO
-
- CREATE TABLE [Constants] (
- [Code] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [Name] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- PRIMARY KEY CLUSTERED
- (
- [Code]
- ) ON [PRIMARY]
- ) ON [PRIMARY]
- GO
-
- CREATE TABLE [Idg] (
- [CODE] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [NAME] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
- PRIMARY KEY CLUSTERED
- (
- [CODE]
- ) ON [PRIMARY]
- ) ON [PRIMARY]
- GO
-
- CREATE TABLE [Members] (
- [QQCode] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [UserName] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
- [Sex] [char] (1) COLLATE Chinese_PRC_CI_AS NULL ,
- [Age] [varchar] (10) COLLATE Chinese_PRC_CI_AS NULL ,
- [Area] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
- [NameCard] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL ,
- [Email] [varchar] (200) COLLATE Chinese_PRC_CI_AS NULL ,
- [WebSite] [varchar] (200) COLLATE Chinese_PRC_CI_AS NULL ,
- [Research] [ntext] COLLATE Chinese_PRC_CI_AS NULL ,
- [Status] [varchar] (12) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [OutReason] [ntext] COLLATE Chinese_PRC_CI_AS NULL ,
- [InTime] [datetime] NULL ,
- [OutTime] [datetime] NULL ,
- [Identity1] [varchar] (12) COLLATE Chinese_PRC_CI_AS NULL ,
- [GotWarn] [char] (1) COLLATE Chinese_PRC_CI_AS NULL ,
- [GotResearch] [char] (1) COLLATE Chinese_PRC_CI_AS NULL ,
- CONSTRAINT [PK__Members__76CBA758] PRIMARY KEY CLUSTERED
- (
- [QQCode]
- ) ON [PRIMARY]
- ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
- GO
-
- CREATE TABLE [Researchs] (
- [SelfId] [varchar] (18) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [QQCode] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [Time] [datetime] NULL ,
- [Context] [ntext] COLLATE Chinese_PRC_CI_AS NULL ,
- PRIMARY KEY CLUSTERED
- (
- [SelfId]
- ) ON [PRIMARY]
- ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
- GO
-
- CREATE TABLE [Warnings] (
- [SelfId] [varchar] (18) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [QQCode] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
- [WarnTime] [datetime] NOT NULL ,
- [Reason] [ntext] COLLATE Chinese_PRC_CI_AS NULL ,
- PRIMARY KEY CLUSTERED
- (
- [SelfId]
- ) ON [PRIMARY]
- ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
- GO
-
- INSERT INTO [Constants] VALUES('M', '男')
- INSERT INTO [Constants] VALUES('F', '女')
- INSERT INTO [Constants] VALUES('In', '在群')
- INSERT INTO [Constants] VALUES('Out', '已退群')
- INSERT INTO [Constants] VALUES('Admin', '管理员')
- INSERT INTO [Constants] VALUES('Member', '会员')
- INSERT INTO [Constants] VALUES('Y', '是')
- INSERT INTO [Constants] VALUES('N', '否')
在Delphi或C++ Builder中,我们可以定义表Members对应的持久对象TMembers为:
- (* This unit is created by My PODO generator *)
-
- unit PODO_MEMBERS;
-
- {$M+}
-
- interface
-
- uses
- Classes, SysUtils, UnitBaseTable;
-
- type
- TMembers = class(TTableData)
- private
- FQQCode: String;
- FUserName: String;
- FSex: String;
- FAge: String;
- FArea: String;
- FNameCard: String;
- FEmail: String;
- FWebSite: String;
- FResearch: Variant;
- FStatus: String;
- FOutReason: Variant;
- FInTime: TDateTime;
- FOutTime: TDateTime;
- FIdentity1: String;
- FGotResearch: String;
- FGotWarn: String;
- published
- property QQCode: String read FQQCode write FQQCode;
- property UserName: String read FUserName write FUserName;
- property Sex: String read FSex write FSex;
- property Age: String read FAge write FAge;
- property Area: String read FArea write FArea;
- property NameCard: String read FNameCard write FNameCard;
- property Email: String read FEmail write FEmail;
- property WebSite: String read FWebSite write FWebSite;
- property Research: Variant read FResearch write FResearch;
- property Status: String read FStatus write FStatus;
- property OutReason: Variant read FOutReason write FOutReason;
- property InTime: TDateTime read FInTime write FInTime;
- property OutTime: TDateTime read FOutTime write FOutTime;
- property Identity1: String read FIdentity write FIdentity;
- property GotWarn: String read FGotWarn write FGotWarn;
- property GotResearch: String read FGotResearch write FGotResearch;
- public
- class
- function KeyColumnName: string;
- override;
- class
- function TableName: string;
- override;
- end;
-
- implementation
-
- { TMembers }
-
- class function TMembers.KeyColumnName: string;
- begin
- result := 'QQCode';
- end;
-
- class function TMembers.TableName: string;
- begin
- result := 'Members';
- end;
-
- initialization
-
- RegisterClass(TMembers);
-
- end.
从代码中可以看到TMembers类的定义的属性与表Members中的字段一一对应。
所有的持久类继承自TTableData, TTableData的定义很关键,它是所有持久类的基类。如要新加一个子类,只需从TTableData类继承,然后在Published里申明相应的属性,属性名称与字段需要相同。同时为了对象内存管理方便,TTableData类从TPersistent继承,可以减少内存泄漏的可能性。VCL 的基类 TObject 本身不支持 RTTI(运行时类型信息),TPersistent 类通过 { $M+ } 编译指令提供了 RTTI(运行时类型信息) 的功能,打开了 M 开关后,Delphi 在编译该对象时,会把对象的类型信息也编译进可执行文件,这样在运行时就可以动态的获得对象的属性,方法等信息,所有的 VCL 可视化组件都是从 TPersistent 派生出来的,因此可以将组件信息保存成 DFM 文件,可以在运行时加载。
那么如何实现一般对象的持久化(Persistent)呢, 解决方案如下:
- unit UnitBaseTable;
-
- // {$I MyUtils.inc}
-
- interface
-
- uses
- SysUtils, Windows, Messages, Classes, Contnrs, TypInfo,
- DB, Variants,
- MyUtils;
-
- type
-
- TTableData = class;
- TTableDataClass = class of TTableData;
- TTableClassArray = array of TTableDataClass;
-
- TTableData = class(TPersistent)
- private
- FFieldList: TStrings;
- FIsNew: Boolean;
- FModified: Boolean;
- FUniqueID: string;
- FDeleteFlag: Boolean;
- FGenerator: String;
- function GetFieldType(AName: string): Pointer;
- function GetValues(Name: string): Variant;
- procedure LoadFieldList;
- procedure SetValues(Name: string; Value: Variant);
- class function GetKeyValueWhere: string;
- protected
- procedure UpdateData; virtual;
- public
- constructor Create(); reintroduce; overload;
- constructor Create(AData: TDataSet); reintroduce; overload;
- constructor CreateNew(); virtual;
- destructor Destroy; override;
-
- // 判断字段类型
- function FieldIsBoolean(AName: string): Boolean;
- function FieldIsDateTime(AName: string): Boolean;
- function FieldIsFloat(AName: string): Boolean;
- function FieldIsInteger(AName: string): Boolean;
- function FieldIsString(AName: string): Boolean;
-
- // 字段是否存在
- function FieldExists(AName: string): Boolean;
-
- // 主键字段值
- function KeyValue: Variant;
- procedure UpdateValues(ASource: TDataSet);
-
- class function AutoKeyValue: Boolean; virtual;
- class function KeyColumnName: string; virtual;
- class function TableName: string; virtual;
- class function GeneratorType: string; virtual;
-
- // 是否使用主键PrimaryKey
- class function UseUniqueID: Boolean; virtual;
-
- class function OrderByList: string; virtual;
-
- class function PropertyExists(AName: string): Boolean;
-
- property FieldList: TStrings read FFieldList;
- property IsNew: Boolean read FIsNew write FIsNew;
- property Modified: Boolean read FModified write FModified;
- property Values[Name: string]: Variant read GetValues write SetValues;
- property DeleteFlag: Boolean read FDeleteFlag write FDeleteFlag;
- property UniqueID: string read FUniqueID write FUniqueID;
- property Generator: string read FGenerator write FGenerator;
- published
- //
- end;
-
- const
- COL_UNIQUEID = 'UniqueID';
-
- implementation
-
- uses
- unitDataOperator;
-
- { TBaseTable }
-
- {
- ********************************** TTableData **********************************
- }
- constructor TTableData.Create();
- begin
- inherited Create();
- FFieldList := TStringList.Create;
- LoadFieldList;
- end;
-
- constructor TTableData.Create(AData: TDataSet);
- begin
- UpdateValues(AData);
- UpdateData;
- end;
-
- constructor TTableData.CreateNew();
- begin
- FIsNew := True;
- // {$ifdef USE_UNIQUEID}
- // if UseUniqueID then
- // FUniqueID := GetNewGUID; //added by sunweijun
- // {$endif} // USE_UNIQUEID
- end;
-
- destructor TTableData.Destroy;
- begin
- FFieldList.Free;
- inherited Destroy;
- end;
-
- class function TTableData.AutoKeyValue: Boolean;
- begin
- // {$ifdef Use_UniqueID}
- Result := False;
- if UseUniqueID then
- Result := (GeneratorType = 'increment') or (GeneratorType = 'native') or
- (GeneratorType = 'identity');
- // {$endif} // Use_UniqueID
- end;
-
- function TTableData.FieldIsBoolean(AName: string): Boolean;
- begin
- Result := GetFieldType(AName) = TypeInfo(Boolean);
- end;
-
- function TTableData.FieldIsDateTime(AName: string): Boolean;
- begin
- Result := GetFieldType(AName) = TypeInfo(TDateTime);
- end;
-
- function TTableData.FieldIsFloat(AName: string): Boolean;
- begin
- Result := GetFieldType(AName) = TypeInfo(Real);
- end;
-
- function TTableData.FieldIsInteger(AName: string): Boolean;
- begin
- Result := GetFieldType(AName) = TypeInfo(Integer);
- end;
-
- function TTableData.FieldIsString(AName: string): Boolean;
- begin
- Result := GetFieldType(AName) = TypeInfo(String);
- end;
-
- class function TTableData.GeneratorType: string;
- begin
- Result := 'assigned';
- end;
-
- function TTableData.GetFieldType(AName: string): Pointer;
- begin
- Result := PPropInfo(FFieldList.Objects[FFieldList.IndexOf(AName)])^.PropType^;
- end;
-
- function TTableData.GetValues(Name: string): Variant;
- begin
- Result := GetPropValue(Self, Name, False);
- end;
-
- class function TTableData.KeyColumnName: string;
- begin
- Result := EmptyStr;
- end;
-
- function TTableData.KeyValue: Variant;
- begin
- if KeyColumnName <> EmptyStr then
- Result := GetValues(KeyColumnName);
- end;
-
- procedure TTableData.LoadFieldList;
- var
- PropCount, I: SmallInt;
- PropList: PPropList;
- PropName: string;
- begin
- PropCount := GetTypeData(ClassInfo).PropCount;
- GetPropList(ClassInfo, PropList);
- try
- for I := 0 to PropCount - 1 do
- begin
- PropName := PropList[I]^.Name;
-
- FFieldList.AddObject(PropName, TObject(PropList[I]));
- end;
- finally
- // free resources
- FreeMem(PropList);
- end; // try/finally
- end;
-
- procedure TTableData.SetValues(Name: string; Value: Variant);
- begin
- if not VarIsNull(Value) then
- SetpropValue(Self, Name, Value);
- end;
-
- class function TTableData.TableName: string;
- begin
- Result := EmptyStr;
- end;
-
- procedure TTableData.UpdateData;
- begin
- end;
-
- procedure TTableData.UpdateValues(ASource: TDataSet);
- var
- I: Integer;
- fName: string;
- fValue: Variant;
- begin
- if ASource = nil then
- Exit;
-
- if not ASource.Active then
- ASource.Open;
- if ASource.Eof then
- raise Exception.CreateFmt('%s.UpdateValues: Not found data', [ClassName]);
- for I := 0 to ASource.FieldCount - 1 do // Iterate
- begin
- try
- fName := ASource.Fields[I].DisplayName;
- fValue := ASource.Fields[I].Value;
- if UseUniqueID and SameText(fName, COL_UNIQUEID) and VarIsNull(fValue)
- then
- // fValue := GetNewGUID; //added by sunweijun
- // {$endif} // Use_UniqueID
-
- if FFieldList.IndexOf(fName) > -1 then
- begin
- SetValues(fName, fValue);
- end;
- except
- on e: Exception do
- begin
- raise;
- end;
- end; // try/except
- end; // for
- end;
-
- class function TTableData.UseUniqueID: Boolean;
- begin
- Result := False;
- end;
-
- class function TTableData.OrderByList: string;
- begin
- Result := EmptyStr;
- end;
-
- class function TTableData.PropertyExists(AName: string): Boolean;
- var
- FPropInfo: PPropInfo;
- begin
- FPropInfo := GetpropInfo(ClassInfo, AName);
- Result := FPropInfo <> nil;
- // if result then
- // FreeMem(FPropInfo);
- end;
-
- function TTableData.FieldExists(AName: string): Boolean;
- begin
- Result := FieldList.IndexOf(AName) > -1;
- end;
-
- class function TTableData.GetKeyValueWhere: string;
- begin
- Result := KeyColumnName + ' = :' + KeyColumnName;
- end;
-
- end.
本框架的设计不采用XML配置文件,因此在持久类的定义中要包含一些配置信息,类似于Java Hibernate里的注解功能。此处定义了两个类函数,KeyColumnName和TableName,分别返回关键字段名和表名。
(1)返回表的关键列名:
class function KeyColumnName: string; virtual;
(2)返回表的名称:
class function TableName: string; virtual;
(3)表中的唯一字段是否使用GUID(默认不使用,如果不改的话可以不覆盖):
class function UseUniqueID: Boolean; virtual;
子类必须覆盖这些类函数。