首页 / 软件开发 / Delphi / 第十八章-Delphi客户服务器应用开发(四)(2)
第十八章-Delphi客户服务器应用开发(四)(2)2007-05-0718.4.1.2 数据库结构介绍 IBLOCAL数据库的结构都是由InterBase服务器工具交互式SQL工具(ISQL)定义的。用ISQL定义数据库,首先要用Create Database命令建立数据库,建立的新数据库一般是以GDB为扩展名。建立好后,就可以用SQL语言定义数据库表,例如建立EMPLOYEE表的SQL语句如下:定义域名数据类型:CREATE DOMAIN FIRSTNAME AS VARCHAR(15);CREATE DOMAIN LASTNAME AS VARCHAR(20);CREATE DOMAIN COUNTRYNAME AS VARCHAR(15);CREATE DOMAIN EMPNO AS SMALLINT;CREATE DOMAIN DEPTNO AS CHAR(3)CHECK (VALUE = "000" OR (VALUE > "0" AND VALUE <= "999") OR VALUE IS NULL);CREATE DOMAIN JOBCODE AS VARCHAR(5)CHECK (VALUE > "99999");CREATE DOMAIN JOBGRADE AS SMALLINTCHECK (VALUE BETWEEN 0 AND 6);CREATE DOMAIN SALARY AS NUMERIC(15, 2)DEFAULT 0CHECK (VALUE > 0);建立EMPLOYEE表:CREATE TABLE EMPLOYEE (EMP_NO EMPNO NOT NULL,FIRST_NAME FIRSTNAME NOT NULL,LAST_NAME LASTNAME NOT NULL,PHONE_EXT VARCHAR(4),HIRE_DATE DATE DEFAULT "NOW" NOT NULL,DEPT_NO DEPTNO NOT NULL,JOB_CODE JOBCODE NOT NULL,JOB_GRADE JOBGRADE NOT NULL,JOB_COUNTRY COUNTRYNAME NOT NULL,SALARY SALARY NOT NULL,FULL_NAME COMPUTED BY (last_name || ", " || first_name),PRIMARY KEY (EMP_NO));CHECK语句是给数据库字段取值范围加约束条件。PRIMARY_KEY语句是给表建立关键字索引。如法炮制,就可以定义IBLOCAL中的所有表。IBLOCAL中的表包括:EMPLOYEE CUSTOMER DEPARTMENT EMPLOYEE_PROJECTPROJECT SALES SALARY_HISCORY各数据库表中的内容如下:表18.14 EmployeeDemoDB中各数据库表的内容 ━━━━━━━━━━━━━━━━━━━━━━━━━━━ 数据库表名 表中内容 ─────────────────────────── EMPLOYEE 雇员信息 CUSTOMER 客户信息DEPARTMENT 部门信息 EMPLOYEE_PROJECT 雇员负责的工程 PROJECT 工程信息SALES 销售信息SALARY_HISTORY 雇员薪水调整的历史信息 ━━━━━━━━━━━━━━━━━━━━━━━━━━━每个数据库表中都定义了关键字段。关于数据库表中的字段名、类型、大小,这里不再赘述。 18.4.2 应用程序分析18.4.2.1 TDatabase部件的使用CSDEMO程序中定义了一个数据库模块部件——TDmEmployee,它是继承于TDataModule。TDataModule是在Delphi2.0中才出现的专门放置数据访问部件(如TDatabase、TTable和TQuery等)的框架。其它涉及数据库访问的窗体,只要在uses语句中插入数据库模块所在的库单元,该窗体上的数据库部件就可引用相应的数据库访问部件。在TDmEmployee中定义了一个TDatabase类型的部件──EmployeeDatabase。EmployeeDatagase的主要属性及属性值如下:表18.15 EmployeeDatabase部件主要属性的取值 ━━━━━━━━━━━━━━━━━━━━━━━ 属性 属性值 ─────────────────────── AliasName IBLOCALDatabaseName EmployeeDemoDBKeepConnection TrueLoginPrompt FalseTransIsolation tiReadCommittedParams USERNAME = SYSDBAPASSWORD = masterkeyConnected True━━━━━━━━━━━━━━━━━━━━━━━AliasName属性所指定的IBLOCAL,必须已经在BDE中配置好,DatabaseName属性指定要使用的数据库名,该数据库名是由应用程序自己定义的,因此不反应到BDE中,该属性值被TTable、TQuery等DataSet部件引用,并且出现在DataSet部件的DatabaseName 下拉式列表框中。本例中的“EmployeeDemoDB”,被EmployeeTable,SalesTable等所有DataSet部件引用。 Connected为True表明,应用程序与数据库将保持联接。KeepConnection属性为True,表明多次打开和关闭EmployeeDemoDB数据库中的任意表,应用程序将始终与数据库保持联接,这省却了重复注册的开销。LoginPrompt 属性为False,表明应用程序自动处理与数据库的联接注册,因此,Params属性中定义了注册的用户名和口令:USERNAME = SYSDBAPASSWORD = masterkeyTransIsolation属性为tiReadCommitted表明,如果存在多个同时事务,则某一事务只允许读由其它事务提交了的数据。程序中EmployeeDatabase的应用还与事务控制等有关。下文中会介绍这方面的内容。18.4.2.2 不同数据库表的切换在许多数据库应用中都要在不同数据库表之间相互切换,以响应用户输入条件或系统状态的变化。这时,往往需要特别的处理,例如改变光标形状或隐藏数据改变等,尤其是在客户/服务器应用程序中。因为是用SQL语句访问远程数据库,有时还要在服务器端执行计算任务,所以客户端的数据变化会有一定的间隔,因此应该让用户明白发生了什么。下面是CSDEMO在数据库表切换时的处理办法:procedure TFrmViewDemo.ShowTable( ATable: string );beginScreen.Cursor := crHourglass; { 向用户提示当前操作状态 }VaryingTable.DisableControls; { 隐藏数据变化 }VaryingTable.Active := FALSE; { 关闭原来的数据库表 }VaryingTable.TableName := ATable; { 更新数据库表名 }VaryingTable.Open; { 打开数据库表 }VaryingTable.EnableControls; { 显示所作的修改 }Screen.Cursor := crDefault; { 重新设置光标形状 }end;crHourglass型光标表明正在执行SQL查询。DisableControls和EnableControls的作用是隐藏和显示数据变化。18.4.2.3 InterBase触发器(Trigger)的应用在CSDEMO应用程序中,演示触发器应用的窗体是TFromTriggerDemo;在该窗体中包含两个TDBGrid对象。DBGrid1显示EmployeeTable中的数据,DBGrid2显示SalaryHistoryTable中的数据。它们的主要属性及属性值如下:表18.16 EmlpoyeeTable部件主要属性的取值 ━━━━━━━━━━━━━━━━━━━━━ 属 性 属 性 值 ───────────────────── DatabaseName EmployeeDemoDBIndexFieldName Emp_NoTableName EMPLOYEE━━━━━━━━━━━━━━━━━━━━━表18.17 SalaryHistoryTable部件主要属性的取值 ━━━━━━━━━━━━━━━━━━━━━ 属 性 属 性 表 ───────────────────── DatabaseName EmployeeDemoDBIndexFieldName Emp_No MasterFields Emp_NoMasterSource EmployeeSourceTableName SALARY_HISTORY ━━━━━━━━━━━━━━━━━━━━━这两个表之间存在两种关系:● 连接关系EmployeeTable的记录变化时,SalaryHistoryTable的数据要作相应的变化。这种连接关系是通过索引来实现的。● 数据一致性对EmployeeTable中的Salary字段的值作修改必须反映到SalaryHistoryTable中,SalaryHistoryTable维护的是Salary变化的历史信息。这种数据一致性要求在本程序中是通过触发器实现的。触发器是在SQL服务器端执行的一段程序,它在服务器端被触发执行完成一定的数据计算任务。下面是InterBase服务器上与Employee表相关的触发器程序:Triggers on Table EMPLOYEE:SAVE_SALARY_CHANGE, Sequence: 0, Type: AFTER UPDATE, Active ASBEGINIF (old.salary <> new.salary) THENINSERT INTO salary_history(emp_no, change_date, updater_id, old_salary, percent_change)VALUES (old.emp_no,"now",user,old.salary,(new.salary - old.salary) * 100 / old.salary);END因为触发器是相应于EMPLOYEE表上的数据修改由服务器自动触发执行的,所以在客户应用程序上没有显式的调用。在客户端有打开并显示数据库表内容的程序和当SALARY_HISTORY表中数据变化时的更新显示的操作。procedure TFrmTriggerDemo.FormShow(Sender: TObject);beginDmEmployee.EmployeeTable.Open;DmEmployee.SalaryHistoryTable.Open;end;procedure TDmEmployee.EmployeeTableAfterPost(DataSet: TDataSet);begin{ 一个雇员的薪水变化将触发薪水调整历史记录的变化,因此,如果SalaryHistory打开的话,就需要更新显示 }with SalaryHistoryTable do if Active then Refresh;end;