java连接mongodb测试(11.Lazarus数据库编程7.ZeosDBO)
java连接mongodb测试(11.Lazarus数据库编程7.ZeosDBO)数据模块声明代码:如下图所示:以上组件除 TZReadOnlyQuery 外,均可以在 Delphi 找到类似的组件,从使用方法的角度来看 ZeosDBO 的组件更加易于使用,而 TZReadOnlyQuery 组件大体上相当于一个只读的 TZQuery 组件。我们可以看到,在 ZeosDBO中,没有事务控制控件,ZeosDBO 会自动为其创建默认的事务控制 。所以,在这里我们就不做过多的介绍,使用时可以参考 Delphi 中类似组件即可轻松地实现应用程序的功能。本节我们通过窗体应用程序实现一个简单的人事档案数据表的 CRUD 操作,数据表结构及数据如下:create table c_dep ( dep_id varchar(64) primary key dep_name varchar(16) ); insert into c_dep (dep_id dep_nam
7.ZeosDBO Database CRUD7.1 CRUDCRUD 是一个计算机术语,是指在进行计算处理时的增加(Create)、检索(Retrieve)、更新(Update)和删除(Delete)几个单词的首字母简写。CRUD 主要被用在描述软件系统中数据库或者持久层的基本操作功能。
某些计算机语言,在实际应用中,创造了大量的术语,CRUD 只是其中的一个而已。对于 Delphi、Lazarus 来说,做这个简单的事情简直是小菜一碟。
某些语言为什么会提出 CRUD 这个概念? 这些语言一般是没有指针,表达复杂数据能力不足,所以提出分层概念,我们来看看这些概念:
- VO - Value Object 负责内存中保存数据的对象。相当于 Pascal 的 Type Record
- MVC - Modal View Control 其实相当于 Dataset Form Control 编码
- DAO - Data Access Object 相当于 Dataset Connection
这些概念的提出是为了将程序编写划分更细,维护更方便;而 Lazarus 这样的开发方式也可以实现这些概念,但说实话,真的没有必要。首先是因为 FCL 这样的库克已经非常优秀,而且如 Dataset、MemDataset 这样的数据结构已经能够表达出足够强大的功能。所以在开发中建议:我们还是坚持原生的开发方法。当然以后也可以研究和学习一下这些分层方法,当然如果您已经接触过这些概念就不必学习和研究了(本来也很简单)。这些东西在 Delphi、Lazarus中,如果在实际开发中使用,相当于画蛇添足,多此一举。
7.2 ZeosDBO 常用组件- TZConnection
- TZQuery
- TZTable
- TZStoredProc
- TZReadOnlyQuery
以上组件除 TZReadOnlyQuery 外,均可以在 Delphi 找到类似的组件,从使用方法的角度来看 ZeosDBO 的组件更加易于使用,而 TZReadOnlyQuery 组件大体上相当于一个只读的 TZQuery 组件。我们可以看到,在 ZeosDBO中,没有事务控制控件,ZeosDBO 会自动为其创建默认的事务控制 。所以,在这里我们就不做过多的介绍,使用时可以参考 Delphi 中类似组件即可轻松地实现应用程序的功能。
7.3 窗体应用示例本节我们通过窗体应用程序实现一个简单的人事档案数据表的 CRUD 操作,数据表结构及数据如下:
create table c_dep (
dep_id varchar(64) primary key
dep_name varchar(16)
);
insert into c_dep (dep_id dep_name)
values ('yfb' '研发部');
insert into c_dep (dep_id dep_name)
values ('xsb' '销售部');
insert into c_dep (dep_id dep_name)
values ('jszcb' '技术支持部');
insert into c_dep (dep_id dep_name)
values ('cwb' '财务部');
insert into c_dep (dep_id dep_name)
values ('xzb' '行政部');
create table d_dep_staff (
staff_id varchar(64) primary key
dep_id varchar(64)
name varchar(32)
sex varchar(8)
birthday date
edu_level varchar(16)
school varchar(64)
speciality varchar(32)
native_place varchar(256)
);
INSERT INTO d_dep_staff
(staff_id dep_id name sex birthday edu_level school speciality native_place)
VALUES(gen_random_uuid() 'yfb' '张三' '女' '1981-9-10' '本科' '内蒙古大学' '经济管理' '内蒙古乌兰察布市集宁区');
INSERT INTO d_dep_staff
(staff_id dep_id name sex birthday edu_level school speciality native_place)
VALUES(gen_random_uuid() 'xsb' '刘五' '男' '1978-7-11' '本科' '内蒙古大学' '计算机科学与技术' '内蒙古鄂尔多斯市东胜区');
INSERT INTO d_dep_staff
(staff_id dep_id name sex birthday edu_level school speciality native_place)
VALUES(gen_random_uuid() 'xzb' '弓九' '女' '1983-3-10' '本科' '内蒙古科技大学' '计算机科学与技术' '内蒙古呼和浩特市托克托县');
SELECT staff_id s.dep_id name sex birthday edu_level school speciality native_place d.dep_name
FROM d_dep_staff s left join c_dep d on s.dep_id = d.dep_id;
本例也是一个基于 PostgreSQL 数据库,采用 ZeosDBO 数据库组件开发的一个综合示例,设计如下:
7.3.1 数据模块如下图所示:
数据模块声明代码:
TDataModule1 = class(TDataModule)
ZConnection1: TZConnection;
ZReadOnlyQuery1: TZReadOnlyQuery;
// 字段定义
ZReadOnlyQuery1_birthday: TDateField; // 生日
ZReadOnlyQuery1_dep_id: TStringField; // 部门ID
ZReadOnlyQuery1_dep_name: TStringField; // 部门名称
ZReadOnlyQuery1_edu_level: TStringField; // 学历
ZReadOnlyQuery1_name: TStringField; // 姓名
ZReadOnlyQuery1_native_place: TStringField; // 籍贯
ZReadOnlyQuery1_school: TStringField; // 毕业学校
ZReadOnlyQuery1_sex: TStringField; // 性别
ZReadOnlyQuery1_speciality: TStringField; // 所学专业
ZReadOnlyQuery1_staff_id: TStringField; // 员工ID
private
public
end;
属性设置如下:
ZConnection1
- Protocol - postgresql-9
- HostName - 127.0.0.1
- Port - 9432
- User - postgres
- Password - ***
- Database - demodb
- Connected - True
ZReadOnlyQuery1
- Connection - ZConnection1
- SQL - SELECT staff_id s.dep_id name sex birthday edu_level school speciality native_place d.dep_name FROM d_dep_staff s left join c_dep d on s.dep_id = d.dep_id
- Active - True
【备注】对于 ZReadOnlyQuery1 组件要进行字段定义操作,字段定义有助于在表单窗体中读取当前记录的各个字段值。
7.3.2 主窗体如下图所示:
窗体声明代码:
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
KeyEdit: TEdit; // 查询关键字输入框
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure DBGrid1DblClick(Sender: TObject);
private
public
end;
属性设置如下:
DataSource1
- DataSet - DataModule1.ZReadOnlyQuery1
DBGrid1
- DataSource - DataSource1
- Align - alClient
- Options - dgRowSelect=True
DBNavigator1
- DataSource - DataSource1
- VisibleButtons - [nbFirst nbPrior nbNext nbLast]
【备注】对于 DBGrid1 组件,需要进行列定义操作,有助于各个列的标题等显示方式的设置。
7.3.3 表单窗体如下图所示:
表单窗体上没有数据库相关组件,都是一些表单组件,而且没有使用 TDB* 组件,使用的都是普通组件,所以不在这里列出组件的属性设置。
窗体声明代码:
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
NativePlaceEdit: TEdit;
SpecialityEdit: TEdit;
SchoolEdit: TEdit;
EduLevelComboBox: TComboBox;
BirthdayDateTimePicker: TDateTimePicker;
DepComboBox: TComboBox;
NameEdit: TEdit;
SexRadioGroup: TRadioGroup;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
public
isNew: Boolean; // 标识数据是否为新建
end;
7.3.4 主窗体功能实现
主窗体实现的功能就是各个操作按钮及数据导航组件的操作。
1.数据导航组件的单击事件
数据导航组件的 OnClick 事件会在单击任何一个按钮后都去触发,当我们使用数据导航组件时,会出现数据记录指针指向开头或末尾的情况,当指向开头或末尾时,可能会导致编辑或删除数据出现问题,所以,我们需要对数据记录指针进行调整,代码如下:
procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TDBNavButtonType);
begin
if DataModule1.ZReadOnlyQuery1.BOF then
if DataModule1.ZReadOnlyQuery1.RecordCount > 0 then
DataModule1.ZReadOnlyQuery1.First;
if DataModule1.ZReadOnlyQuery1.EOF then
if DataModule1.ZReadOnlyQuery1.RecordCount > 0 then
DataModule1.ZReadOnlyQuery1.Last;
end;
2.新建
新建按钮的功能是打开表单窗体,如果返回值是 mrOk ,则表示在表单窗体中已经插入了数据,需要对 DBGrid1 中的数据进行刷新。代码如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
// 新建
Form2.isNew := True;
if Form2.ShowModal = mrOk then
begin
DataModule1.ZReadOnlyQuery1.Refresh;
end;
end;
首先,设置表单窗体的 isNew 属性为 True,表示要进行新建数据记录,然后以模式窗体打开,等待窗体关闭,如果窗体返回值为 mrOk,则对数据集组件 ZReadOnlyQuery1 中的数据进行刷新。
3.编辑
编辑按钮的功能与新建按钮相似,代码如下:
procedure TForm1.Button2Click(Sender: TObject);
begin
// 编辑
if DataModule1.ZReadOnlyQuery1.BOF or DataModule1.ZReadOnlyQuery1.EOF then Exit;
Form2.isNew := False;
if Form2.ShowModal = mrOk then
begin
DataModule1.ZReadOnlyQuery1.Refresh;
end;
end;
4.删除
删除按钮实现的功能是对当前记录进行删除操作,删除后需要对数据记录进行刷新,代码如下:
procedure TForm1.Button3Click(Sender: TObject);
var
Query: TZQuery;
begin
// 删除
if DataModule1.ZReadOnlyQuery1.BOF or DataModule1.ZReadOnlyQuery1.EOF then Exit;
Query := TZQuery.Create(Self);
try
Query.Connection := DataModule1.ZConnection1;
Query.Close;
Query.SQL.Text := 'DELETE FROM d_dep_staff where staff_id = :staffId';
Query.Params.ParamByName('staffId').AsString := DataModule1.ZReadOnlyQuery1_staff_id.Value;
Query.ExecSQL;
except
on D: EDatabaseError do
MessageDlg('Error' 'A database error has occurred. Technical error message: ' D.Message mtError [mbOK] 0);
end;
Query.Destroy;
DataModule1.ZReadOnlyQuery1.Refresh;
end;
在上面的代码中,声明了一个 TZQuery 查询对象,在删除按钮的事件中,使用该对象执行 Delete 语句。
5.刷新
刷新按钮的功能很简单,直接调用数据模块中的 TZQuery 组件的 Refresh 方法即可。代码如下:
procedure TForm1.Button4Click(Sender: TObject);
begin
// 刷新
DataModule1.ZReadOnlyQuery1.Refresh;
end;
6.查询
查询按钮实现在关键字输入框中输入内容,根据输入内容进行数据查询,本例没有使用 Filter,而是使用直接执行SQL 语句进行查询,代码如下:
procedure TForm1.Button5Click(Sender: TObject);
begin
// 查询
if KeyEdit.Text = '' then
begin
MessageDlg('提示' '请输入查询关键字!' mtError [mbOK] 0);
Exit;
end;
with DataModule1 do
begin
ZReadOnlyQuery1.Close;
ZReadOnlyQuery1.SQL.Text:='SELECT staff_id s.dep_id name sex birthday edu_level school speciality native_place d.dep_name FROM d_dep_staff s left join c_dep d on s.dep_id = d.dep_id WHERE concat(name sex edu_level school speciality native_place d.dep_name) like :Key';
ZReadOnlyQuery1.Params.ParamByName('Key').AsString:='%' KeyEdit.Text '%';
ZReadOnlyQuery1.Open;
end;
end;
7.全部
全部按钮实现将数据全部显示出来,主要是在关键字查询后回到全部显示的状态。代码如下:
procedure TForm1.Button6Click(Sender: TObject);
begin
// 全部
with DataModule1 do
begin
ZReadOnlyQuery1.Close;
ZReadOnlyQuery1.SQL.Text:='SELECT staff_id s.dep_id name sex birthday edu_level school speciality native_place d.dep_name FROM d_dep_staff s left join c_dep d on s.dep_id = d.dep_id';
ZReadOnlyQuery1.Open;
end;
end;
8.DBGrid1 的双击事件
为了方便用户操作,一般情况下,我们需要当用户双击数据网格时进入编辑功能,所以,DBGrid1 的双击事件应该直接调用编辑按钮的单击事件,代码如下:
procedure TForm1.DBGrid1DblClick(Sender: TObject);
begin
// 双击修改
Button2Click(Sender);
end;
7.3.5 表单窗体功能实现
表单窗体的功能主要实现记录的新建和修改,不管是新建记录还是编辑记录,表单窗体中的组件均需要做初始化,新建时,将表单窗体中的各个组件置为初始数据,字符串一般为空字符串,日期为当前日期,类似性别这样的单选数据置一个常用的数据等,编辑时,将表单窗体中的各个组件的值设置为当前记录数据的值,以便于修改;另外,无论是新建还是修改,都需要对列表数据进行初始化,也就是读取数据表中的数据作为列表中的选项,如本例中的所在部门就是从部门表中读取数据并初始化到 ComboBox 组件中。
1.表单窗体初始化
procedure TForm2.FormActivate(Sender: TObject);
var
Query: TZQuery;
index: Integer;
begin
// 初始化部门选项
try
Query := TZQuery.Create(Self);
Query.Connection := DataModule1.ZConnection1;
Query.Close;
Query.SQL.Text := 'SELECT dep_id dep_name FROM c_dep';
Query.Open;
DepComboBox.Items.Clear;
while not Query.EOF do
begin
DepComboBox.Items.AddObject(
Query.FieldByName('dep_name').AsString
TObject(NewStr(Query.FieldByName('dep_id').AsString))
);
Query.Next;
end;
except
on D: EDatabaseError do
MessageDlg('Error' 'A database error has occurred. Technical error message: ' D.Message mtError [mbOK] 0);
end;
// 初始化数据项
if isNew then
begin
NameEdit.Text:='';
SexRadioGroup.ItemIndex:=0;
SchoolEdit.Text:='';
SpecialityEdit.Text:='';
NativePlaceEdit.Text:='';
end
else
with DataModule1 do
begin
NameEdit.Text:=ZReadOnlyQuery1_name.Value;
if ZReadOnlyQuery1_sex.Value = '男' then
SexRadioGroup.ItemIndex:=0
else
SexRadioGroup.ItemIndex:=1;
index := DepComboBox.Items.IndexOf(ZReadOnlyQuery1_dep_name.Value);
DepComboBox.ItemIndex:=index;
BirthdayDateTimePicker.Date := ZReadOnlyQuery1_birthday.Value;
EduLevelComboBox.Text := ZReadOnlyQuery1_edu_level.Value;
SchoolEdit.Text := ZReadOnlyQuery1_school.Value;
SpecialityEdit.Text:=ZReadOnlyQuery1_speciality.Value;
NativePlaceEdit.Text:= ZReadOnlyQuery1_native_place.Value;
end;
end;
在上面的代码中,第一部分是对部门选项 DepComboBox 中的列表数据进行初始化,该部分使用了过程中声明的 TZQuery 组件执行对应的 SQL:SELECT dep_id dep_name FROM c_dep,然后遍历 TZQuery 组件中的数据行,逐行读取字段值填充到 DepComboBox 组件的 Items 属性中。
DepComboBox.Items.AddObject(
Query.FieldByName('dep_name').AsString
TObject(NewStr(Query.FieldByName('dep_id').AsString))
);
Query.Next;
第二部分则根据 isNew 的值来决定对窗体上组件的初始化方式。
2.取消
取消按钮的单击事件很简单,代码如下:
procedure TForm2.Button2Click(Sender: TObject);
begin
// 取消
ModalResult := mrCancel;
end;
3.确定
确定按钮的单击事件就是根据 isNew 的值来决定执行 Insert 还是 Update Sql 语句。代码如下:
procedure TForm2.Button1Click(Sender: TObject);
var
Query: TZQuery;
index: Integer;
depId: String;
begin
// 确定
if NameEdit.Text = '' then
begin
MessageDlg('提示' '请输入姓名!' mtError [mbOK] 0);
Exit;
end;
if DepComboBox.ItemIndex < 0 then
begin
MessageDlg('提示' '请选择所在部门!' mtError [mbOK] 0);
Exit;
end;
index := DepComboBox.ItemIndex;
depId := PAnsiString(DepComboBox.Items.Objects[index])^;
Query := TZQuery.Create(Self);
try
Query.Connection := DataModule1.ZConnection1;
Query.Close;
if isNew then
begin
Query.SQL.Text := 'INSERT INTO d_dep_staff (staff_id dep_id name sex birthday edu_level school speciality native_place) VALUES(gen_random_uuid() :depId :name :sex :birthday :eduLevel :school :speciality :nativePlace)';
Query.Params.ParamByName('depId').AsString := depId;
Query.Params.ParamByName('name').AsString := NameEdit.Text;
if SexRadioGroup.ItemIndex = 0 then
Query.Params.ParamByName('sex').AsString := '男'
else
Query.Params.ParamByName('sex').AsString := '女';
Query.Params.ParamByName('birthday').AsDate := BirthdayDateTimePicker.Date;
Query.Params.ParamByName('eduLevel').AsString := EduLevelComboBox.Text;
Query.Params.ParamByName('school').AsString := SchoolEdit.Text;
Query.Params.ParamByName('speciality').AsString := SpecialityEdit.Text;
Query.Params.ParamByName('nativePlace').AsString := NativePlaceEdit.Text;
Query.ExecSQL;
end
else
begin
Query.SQL.Text := 'UPDATE d_dep_staff SET dep_id=:depId name=:name sex=:sex birthday=:birthday edu_level=:eduLevel school=:school speciality=:speciality native_place=:nativePlace WHERE staff_id=:staffId';
Query.Params.ParamByName('depId').AsString := depId;
Query.Params.ParamByName('name').AsString := NameEdit.Text;
if SexRadioGroup.ItemIndex = 0 then
Query.Params.ParamByName('sex').AsString := '男'
else
Query.Params.ParamByName('sex').AsString := '女';
Query.Params.ParamByName('birthday').AsDate := BirthdayDateTimePicker.Date;
Query.Params.ParamByName('eduLevel').AsString := EduLevelComboBox.Text;
Query.Params.ParamByName('school').AsString := SchoolEdit.Text;
Query.Params.ParamByName('speciality').AsString := SpecialityEdit.Text;
Query.Params.ParamByName('nativePlace').AsString := NativePlaceEdit.Text;
Query.Params.ParamByName('staffId').AsString := DataModule1.ZReadOnlyQuery1_staff_id.Value;
Query.ExecSQL;
end;
except
on D: EDatabaseError do
MessageDlg('Error' 'A database error has occurred. Technical error message: ' D.Message mtError [mbOK] 0);
end;
Query.Destroy;
ModalResult := mrOk;
end;
上面的代码中,仍然是使用过程中声明的 TZQuery 变量来执行 SQL 语句来完成新增或更新。
7.4 CRUD 典型代码通过上面的代码,我们可以看到,在实际开发中,如果采用这种方式来开发(不使用 *Query 组件的写入,DBGrid组件只用于显示数据),比较典型的代码如下:
Query := TZQuery.Create(Self);
try
Query.Connection := DataModule1.ZConnection1; // 设置连接
Query.Close;
Query.SQL.Text := '......';
Query.Params.ParamByName('staffId').AsString := ......;
Query.ExecSQL;
except
on D: EDatabaseError do
MessageDlg('Error' 'A database error has occurred. Technical error message: ' D.Message mtError [mbOK] 0);
end;