下面用一个Demo展示本框架的应用,对前述数据库的Members、Researchs和Warnings表进行CRUD的操作。本例来自于CnPack的CnDHibernate,根据本框架进行了修改。
程序主窗体如下:
在主窗体创建时,也实例化TDataController对象。
procedure TFormMain.FormCreate(Sender: TObject);
begin
//ADOConnection
Connection.Open;
//创建TDataController对象
dc := TDataController.Create(Connection.ConnectionString);
dc.OpenConnection;
//打开Members表
qry1.Open;
end;
点击“添加成员”按钮,进入子窗口:
输入数据后,点击确认,就会增加一条记录。修改成员和增加成员的共用一个窗口。代码如下:
procedure TFormAddOrEdit.btnOKClick(Sender: TObject);
var
Member: TMembers;
QQC: string;
dq: TDataOperator;
begin
dq := TDataOperator.Create;
dq.DataController := FormMain.dc;
// save or update
try
Member := TMembers.Create();
with Member do
begin
QQCode := edtQQCode.Text;
UserName := edtUserName.Text;
if cbSex.ItemIndex = 1 then
Sex := 'M'
else if cbSex.ItemIndex = 2 then
Sex := 'F'
else
Sex := EmptyStr;
Age := edtAge.Text;
Area := edtArea.Text;
NameCard := edtIdCard.Text;
Email := edtEmail.Text;
WebSite := edtWebSite.Text;
Research := mmResearch.Lines.Text;
if cbStatus.ItemIndex = 0 then
Status := 'In'
else
Status := 'Out';
OutReason := mmOutReason.Lines.Text;
if cbIdentity.ItemIndex = 0 then
Identity1 := 'Member'
else
Identity1 := 'Admin';
if FormMode = fmAdd then
InTime := Now
else if FormMode = fmEdit then
InTime := FInTime;
if Status = 'Out' then
OutTime := Now;
end;
Member.IsNew := True;
dq.saveOrUpdate(member);
//dq.Save(Member);
except
on E: Exception do
MessageBox(Handle, PChar(E.Message), '出错!', MB_OK or MB_ICONERROR);
end;
end;
按钮“删除成员”OnClick事件对应的方法如下:
procedure TFormMain.btnDeleteMemberClick(Sender: TObject);
var
member: TMembers;
QQC: string;
dq: TDataOperator;
begin
dq := TDataOperator.Create;
dq.DataController := dc;
member := TMembers.Create;
member.QQCode := qry1.FieldByName('QQCode').Value;
if QQC <> EmptyStr then
begin
if MessageBox(Handle, '是否要删除指定的会员?', '提示', MB_YESNO or MB_ICONINFORMATION) = IDNO
then
Exit;
// delete member
dq.Delete(member);
qry1.Close;
qry1.Open;
member.Free;
end;
选择主窗口DBGrid中一条记录,双击该记录,弹出子窗口:
DBGrid的OnDoubleClick事件对应的代码如下:
procedure TFormMain.DBGrid1DblClick(Sender: TObject);
var
member: TMembers;
QQC: string;
dq: TDataOperator;
begin
dq := TDataOperator.Create();
dq.DataController := dc;
// 根据关键字段加载持久对象
member := TMembers(dq.get('Members', 'QQCode', qry1.FieldByName('QQCode').Value));
// view record
QQC := member.QQCode;
if QQC <> EmptyStr then
begin
with TFormAddOrEdit.Create(Application, fmView, member) do
begin
ShowModal;
Free;
end;
end;
end;
从这个例子可以看到,对数据库中记录的CRUD操作,完全可以通过实例化记录对应的持久对象,以该对象为参数,用DataOperator相应的方法进行增删查改,无须再写复杂的SQL语句。
每一个表对应的持久类的源码文件可以通过工具自动生成。
到目前为止,我们已经实现了一个简单的Delphi版本的ORM框架,但是这个还有一些缺点,主要表现在:
1. 没有对象的主键键值生成机制,如自增字段、GUID、自定义主键的键值生成机制。
2. 不支持Blob字段的存取。
3. 数据库的字段名的定义有很大的限制,目前要求字段名和属性名相同。
4.字段类型的支持不够全面。
5.未提供对多数据库的支持。
这些缺点必须加以克服,以使之适应更大项目的需要,这些在C++ Builder版本的ORM框架中都得到了一一解决。
本人是先研究了这个Delphi的ORM框架,发现可行之后,就直接移植到C++ Buider上,在移植和开发的过程不断地解决上面的问题。
遗憾的是C++ Buider的RTTI机制要比Delphi弱很多,无法根据类名生成实例,而这点Delphi可以,至少目前我没有在C++ Buider中解决这个问题。只能先生成实例,再进行访问。希望得到大家的指点。
以上代码都是在RAD XE2中实现。框架的开发伴随Delphi/BCB环境的升级,从Delphi 2010一直到XE2。