從delphi 6 開始,datasnap 作為delphi 自帶的多層框架,一直更新到最新的delphi 10.3 。同時逐步增加了很多新的功能
,比如支持REST 調用,支持 IIS ,apache 等服務器。今天簡單介紹一下如何在datasnap 中使用unidac 訪問數據庫。
首先,按照向導生成一個標准的datasnap 服務器。
、
點完成就自動生成對應的工程文件
其中
- DSServer — 用來控制數據c傳輸和服務類;
- DSServerClass — 用來定義可供客戶端調用的服務器類方法;
- DSHTTPService — 用來使用HTTP 協議訪問服務器;
- DSAuthenticationManager —控制調用服務器方法的用戶認證及授權;
- DSTCPServerTransport — 用來使用TCP/IP訪問服務器。
我們可以通過代碼做一些對應的設置
主界面代碼:
procedure Tmainf.btStartClick(Sender: TObject); begin if not ServerContainer1.DSServer1.Started then begin ServerContainer1.DSTCPServerTransport1.Port := StrToInt(edTCPIPPort.Text); ServerContainer1.DSHTTPService1.DSPort := StrToInt(edHTTPPort.Text); ServerContainer1.DSServer1.Start; mStatus.Lines.Add( Formatdatetime('yyyy-mm-dd hh:nn:ss',now) + 'TCP/IP 啟動偵聽'); ServerContainer1.DSHTTPService1.Start; mStatus.Lines.Add(Formatdatetime('yyyy-mm-dd hh:nn:ss',now) +'HTTP 啟動偵聽'); btStart.Caption := '停止'; end else begin ServerContainer1.DSHTTPService1.Stop; mStatus.Lines.Add(Formatdatetime('yyyy-mm-dd hh:nn:ss',now) + 'TCP/IP 停止偵聽'); ServerContainer1.DSServer1.Stop; mStatus.Lines.Add(Formatdatetime('yyyy-mm-dd hh:nn:ss',now) +'HTTP 停止偵聽'); btStart.Caption := '啟動'; end; end;
procedure TServerContainer1.DSServer1Connect( DSConnectEventObject: TDSConnectEventObject); var ci: TDBXClientInfo; begin ci := DSConnectEventObject.ChannelInfo.ClientInfo; mainf.mStatus.Lines.Add(Format('客戶端以 %s 方式連接 IP: %s, 端口: %s', [ci.Protocol, ci.IpAddress, ci.ClientPort])); end; procedure TServerContainer1.DSServer1Disconnect( DSConnectEventObject: TDSConnectEventObject); var ci: TDBXClientInfo; begin ci := DSConnectEventObject.ChannelInfo.ClientInfo; mainf.mStatus.Lines.Add(Format('客戶端以 %s 方式斷開 IP: %s, Port: %s', [ci.Protocol, ci.IpAddress, ci.ClientPort])); end;
以上datasnap 服務器端基本上就設置好了,現在我們要增加數據庫訪問功能。數據庫訪問控件毫無疑問,使用unidac.
本次還是以postgresql 為例。
啟動postgresql 服務器
(這個服務器在哪里下載?請猛戳 www.haosql.com).
運行數據庫腳本
CREATE TABLE dept ( deptno serial NOT NULL, dname character varying(14), loc character varying(13), cnt integer, CONSTRAINT "PK_MASTER" PRIMARY KEY (deptno) ); CREATE TABLE emp ( empno serial NOT NULL, ename character varying(10), job character varying(9), mgr integer, hiredate timestamp without time zone, sal real, comm real, deptno integer NOT NULL, CONSTRAINT "PK_DETAIL" PRIMARY KEY (deptno, empno) ); CREATE TABLE users ( id numeric NOT NULL, username character(20) NOT NULL, passwd character(20) NOT NULL, CONSTRAINT pk_users_id PRIMARY KEY (id), CONSTRAINT uq_users_name UNIQUE (username) ); CREATE OR REPLACE FUNCTION sp_check_user(a_username character DEFAULT ''::bpchar, a_passwd character DEFAULT ''::bpchar) RETURNS boolean AS $BODY$BEGIN IF EXISTS (SELECT 1 FROM users WHERE username = a_username AND passwd = a_passwd) THEN RETURN True; ELSE RETURN False; END IF; END; $BODY$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION sp_dept_modify(a_deptno numeric DEFAULT (-1), a_dname character DEFAULT ''::bpchar, a_loc character DEFAULT ''::bpchar, a_op_type numeric DEFAULT 0) RETURNS void AS $BODY$begin case a_op_type when 0 then delete from dept where deptno = a_deptno; when 1 then insert into dept (deptno, dname, loc) values (a_deptno, a_dname, a_loc); else update dept set dname = a_dname, loc = a_loc where deptno = a_deptno; end case; end;$BODY$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION sp_emp_modify(a_empno numeric DEFAULT (-1), a_ename character DEFAULT ''::bpchar, a_job character DEFAULT ''::bpchar, a_mgr numeric DEFAULT 0, a_hiredate timestamp without time zone DEFAULT (now())::timestamp without time zone, a_sal numeric DEFAULT 0, a_comm numeric DEFAULT 0, a_deptno numeric DEFAULT (-1), a_op_type numeric DEFAULT 0) RETURNS void AS $BODY$begin case a_op_type when 0 then delete from emp where empno = a_empno; when 1 then insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values (a_empno, a_ename, a_job, a_mgr, a_hiredate, a_sal, a_comm, a_deptno); else update emp set ename = a_ename, job = a_job, mgr = a_mgr, hiredate = a_hiredate, sal = a_sal, comm = a_comm where empno = a_empno; end case; end;$BODY$ LANGUAGE plpgsql;
在ServerMethodsUnit1 里面放置對應的控件。
設置數據庫連接
設置對應的sql 參數
添加對應的 服務器端代碼
function GetDept: TDataSet; function GetEmp(DeptNo: integer = -1): TDataSet; function CheckUser(username: string = ''; passwd: string = ''):boolean; procedure DeptModify(DeptNo: integer = -1; DName: string = ''; Loc: string = ''; op_type: integer = 0); procedure EmpModify(Empno: integer = -1; EName: string = ''; Job: string = ''; Mgr: integer = 0; HireDate: TDateTime = 0; Sal: double = 0; Comm: double = 0; Deptno:integer = -1; op_type: integer = 0);
如圖
procedure TServerMethods1.DeptModify(DeptNo: integer; DName, Loc: string; op_type: integer); begin usp_dept_modify.ParamByName('a_deptno').AsInteger := DeptNo; usp_dept_modify.ParamByName('a_dname').AsString := DName; usp_dept_modify.ParamByName('a_loc').AsString := Loc; usp_dept_modify.ParamByName('a_op_type').AsInteger := op_type; usp_dept_modify.Execute; end; procedure TServerMethods1.EmpModify(Empno: integer; EName, Job: string; Mgr: integer; HireDate: TDateTime; Sal, Comm: double; Deptno, op_type: integer); begin usp_emp_modify.ParamByName('a_empno').AsInteger := Empno; usp_emp_modify.ParamByName('a_ename').AsString := EName; usp_emp_modify.ParamByName('a_job').AsString := Job; usp_emp_modify.ParamByName('a_mgr').AsInteger := Mgr; usp_emp_modify.ParamByName('a_hiredate').AsDateTime := HireDate; usp_emp_modify.ParamByName('a_sal').AsFloat := Sal; usp_emp_modify.ParamByName('a_Comm').AsFloat := Comm; usp_emp_modify.ParamByName('a_deptno').AsInteger := Deptno; usp_emp_modify.ParamByName('a_op_type').AsInteger := op_type; usp_emp_modify.Execute; end; function TServerMethods1.GetDept: TDataSet; begin uqDept.Close; uqDept.Open; Result := uqDept; end; function TServerMethods1.GetEmp(DeptNo: integer): TDataSet; begin uqEmp.Close; uqEmp.ParamByName('DEPTNO').AsInteger := DeptNo; uqEmp.Open; Result := uqEmp; end; function TServerMethods1.EchoString(Value: string): string; begin Result := Value; end; function TServerMethods1.ReverseString(Value: string): string; begin Result := System.StrUtils.ReverseString(Value); end;
服務器端就OK 了。
運行起來。
在瀏覽器里面我們也可以直接訪問它的REST 服務
剩下就是客戶端的編寫了,我們放到下一篇。
先來個客戶端效果照。