Delphi和C++ Builder中的Hibernate开发(二)

在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;

       子类必须覆盖这些类函数。

 

转载于:https://www.cnblogs.com/gowithyou/archive/2012/02/25/delphiORM.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值