FIBPlus:使用独特的主细表机制访问InterBase

 在谈论技术细节前首先说说FIBPlus.这是Delphi,BCB和Kylix的原生组件库,帮助开发者通过InterBase API与InterBase高效率的交互,其同样适用于FireBird和Yaffil.这意味着使用FIBPlus开发者可以使用所有Interbase的功能:完全的事务控制,最高的速度,特殊的InterBase特性(如数组字段类型)等等.除了上面提到的,FIBPlus控件绝对与数据感知控件兼容.后面将通过范例阐述.

在关系数据库中使用主细表连接是应用开发者常遇到的任务.事实上数据库会优化这种主从关系从而建立一种更深层的主从连接.因此依赖于数据库的优化,开发者应该尽量简化用户界面,将更多的主从关系处理交给数据库.

我们将使用InterBase开发包的EMPLOYEE.GDB数据库中的DEPARTMENT 和EMPLOYEE表建立一个主从关系范例.

CREATE TABLE DEPARTMENT (

    DEPT_NO DEPTNO NOT NULL,

    DEPARTMENT VARCHAR (25) NOT NULL,

    HEAD_DEPT DEPTNO,

    MNGR_NO EMPNO,

    BUDGET BUDGET,

    LOCATION VARCHAR (15),

    PHONE_NO PHONENUMBER  DEFAULT '555-1234',

    DEPT_NO1 CUSTNO);

ALTER TABLE DEPARTMENT ADD PRIMARY KEY (DEPT_NO);

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

ALTER TABLE EMPLOYEE ADD PRIMARY KEY (EMP_NO);

ALTER TABLE EMPLOYEE ADD FOREIGN KEY (DEPT_NO) REFERENCES DEPARTMENT (DEPT_NO);

DEPARTMENT是主表,EMPLOYEE是从表.主从关系为:

DEPARTMENT.DEPT_NO <- EMPLOYEE.DEPT_NO

对每个部门都可以存储相对应的多个雇员.

步骤1.从数据表中获取数据并显示在TDBGrid中.

我们从头开始分析这个应用程序的每个过程.这可以帮助我们实现部门和雇员数据的编辑.首先向窗体拖放一个主要控件--连接到InterBase数据库的TpFIBDatabase,并编辑组件.

图2.

为连接到数据库必须设置一个路径(本例的本地路径),用户名和密码.设置好参数后点击”Test”按钮检查设置是否正确.也可以在运行期设置数据库连接参数.

图3.

procedure TForm1.Button1Click(Sender: TObject);

begin

  pFIBDatabase1.DBName := DBNameE.Text;

  pFIBDatabase1.ConnectParams.UserName := UserNameE.Text;

  pFIBDatabase1.ConnectParams.Password := PasswordE.Text;

  pFIBDatabase1.Open;

end;

下一步从数据库获取数据.需要在窗体上放置两个组件: TpFIBDataSet和TpFIBTransaction. TpFIBDataSet从TDataSet继承并可以与所有标准可视组件兼容(DataSource1可以连接到pFIBDataSet1). TpFIBTransaction用来控制事务.

图4.

在TPBMode属性中设置事务隔离级别,并将pFIBTransaction1连接到pFIBDatabase1.

图5.

同样设置pFIBDataSet1的Database和Transaction属性.

图6-7.

现在可以设置必要的数据库查询语句获取数据,并可修改数据表.使用SelectSQL工具写查询数据的SQL命令.本例查询很简单:

SELECT * FROM DEPARTMENT

现在做少量的代码修改:

procedure TForm1.Button1Click(Sender: TObject);

begin

  pFIBDatabase1.DBName := DBNameE.Text;

  pFIBDatabase1.ConnectParams.UserName := UserNameE.Text;

  pFIBDatabase1.ConnectParams.Password := PasswordE.Text;

  pFIBDatabase1.Open;

  pFIBDataSet1.Open;

end;

在打开数据集前不必显示的启动事务因为pFIBDataSet1的poStartTransaction属性默认值指定数据集将自动开启事务.启动程序打开数据库可见如下界面:

图8.

下载代码example.

步骤2.创建实时查询.使用生成器获取主键值.

如果尝试在DBGrid1中修改值将会发现数据是只读的.为了使我们的查询可以编辑(有活力的)还需要写其他一些SQL命令,在编辑DBGrid1中数据是可由pFIBDataSet1自动调用.需要设置如下属性:

图9.

FIBPlus包括特殊的设计器编辑器用来编辑和生成修改命令.可右击pFIBDataSet1组件在弹出菜单中点击” SQL Generator”项.

图10.

生成修改命令前首先需要在列表中选择要修改的表.如果查询中只有一个表,将会自动选择.点击“Get Table Fields”按钮填充“Key Fields”和“Update Fields”列表.在第一个列表中选择要生成修改命令的WHERE子句的列.在“Update Fields”列表中选择需要修改的列.默认DEPARTMENT表的所有列都被选中.现在点击“Generate SQLs”按钮将自动生成所有修改命令,可从SQLs属性中查看.本例我们得到如下InsertSQL属性:

INSERT INTO DEPARTMENT(

    DEPT_NO,  DEPARTMENT,  HEAD_DEPT,  MNGR_NO,  BUDGET,  LOCATION,

    PHONE_NO

)

VALUES(

    ?DEPT_NO, ?DEPARTMENT,  ?HEAD_DEPT, ?MNGR_NO, ?BUDGET, ?LOCATION,

    ?PHONE_NO

)

注意RefreshSQL查询命令.这也是一个查询语句,但其必须只返回当前记录.创建了所有SQL命令后启动查询,在DBGrid1中新建一个记录,pFIBDataSet1将自动使用用户设置的相应字段值填充?DEPT_NO, ?DEPARTMENT等参数的值.注意DEPT_NO字段值.这个整数字段是表的主键必须填充一个唯一值. InterBase 提供了一个叫“generators”的特殊特性来生成这样的值,并可确保值的唯一性.在执行插入命令前我们使用这个特性生成一个新的生成器值.为什么需要这样做?当执行任意修改操作后(出删除外), pFIBDataSet1都自动执行RefreshSQL,在子句中设置当前参数值.如果我们不提前获取主键值而是使用触发器生成,将无法在RefreshSQL查询中设置DEPT_NO的值,也就无法重读被修改的记录.因此如果一下记录字段在触发器中被修改,则在完全重新打开数据集前看不到这些修改的值.但如果首先获取到生成器的值,然后随其他参数插入到数据库,即可使用这个值来更新当前记录,获取到所有当前记录字段值,而不必重新打开数据集!

TpFIBDataSet允许使用生成器自动获取主键值.需要设置AutoUpdateOptions属性:

图11.

  首先设置DEPARTMENT表名,DEPT_NO主键字段和DEPT_NO_GEN生成器. WhenGetGenID属性可设置如下值:

wgOnNewRecord –缓冲区准备创建新纪录后获取生成器的值.

wgBeforePost – 向服务器发送新记录前获取生成器的值.

wgNever – 不使用生成主键值的机制.

本例设置为wgOnNewRecord,查看DBGrid1的DEPT_NO字段值,都是冲服务器上获取的.启动程序,编辑记录并插入新记录.

图12.

上图可见自动获取了DEPT_NO的值为22.注意还会自动获取到BUDGET和PHONE_NO字段的默认值.

范例代码 example

步骤3. AutoCommit模式.启用两个事务上下文防止死锁

Step 3. AutoCommit Mode. Work in the context of two transactions for avoiding DEADLOCK.

如果设置了pFIBDataSet的AutoCommit属性为True则会自动提交数据的修改.调用Post方法后, pFIBDataSet1将自动调用pFIBTransaction1.CommitRetaining,提交修改但不会关闭数据集.这样可减少死锁的可能,死锁很容易在长事务中修改数据时出现.

FIBPlus提供了另一个能力,将死锁的可能性几乎降低为0. TpFIBDataSet可以工作在两个事务上下文中,一个长事务负责读取数据,一个短事务用来提交数据的修改.

  将pFIBTransaction1重命名为ReadTransaction,添加一个新的事务组件WriteTransaction. 设置pFIBDataSet1的UpdateTransaction属性为WriteTransaction.这样pFIBDataSet1同时连接到了两个事务组件:

图13.

现在调用pFIBDataSet1的Post方法后将执行WriteTransaction的提交方法.但是考虑到读取数据的是不同的事务(ReadTransaction),将不会关闭pFIBDataSet1.因此使用FIBPluswomen有了一个真正的AutoCommit模式,减少死锁可能性而不会阻碍查看实际记录值.而在BDE中使用AutoCommit模式则不能获取到实时的记录值除非重新打开数据集.

查看范例代码example

步骤4.主从连接.使用特殊的参数名称前缀"MAS_"

现在可以创建主从连接了.向窗体拖放如下组件:

    DBGrid2: TDBGrid;

    DataSource2: TDataSource;

    pFIBDataSet2: TpFIBDataSet;

    ReadTransaction2: TpFIBTransaction;

    WriteTransaction2: TpFIBTransaction;

  将其按第一组组件同样方式进行关联.设置pFIBDataSet2的SelectSQL属性:

SELECT * FROM EMPLOYEE

WHERE DEPT_NO = ?DEPT_NO

很明显细表查询只想获取工作在当前部门的雇员.参数值?DEPT_NO必须从DEPARTMENT表的DEPT_NO字段中获取.为自动实现这个效果,设置pFIBDataSet2.DataSource= DataSource1.现在采用与pFIBDataSet1同样方式生成修改SQL命令.

自动生成命令后需要稍作修改.特别在InsertSQL中:

INSERT INTO EMPLOYEE(

    EMP_NO,  FIRST_NAME,  LAST_NAME,   PHONE_EXT,  HIRE_DATE,

    DEPT_NO,

    JOB_CODE,  JOB_GRADE, JOB_COUNTRY,  SALARY

)

VALUES(

    ?EMP_NO,  ?FIRST_NAME,  ?LAST_NAME, ?PHONE_EXT, ?HIRE_DATE,

    ?DEPT_NO,

    ?JOB_CODE,  ?JOB_GRADE, ?JOB_COUNTRY, ?SALARY

)

很明显增加一个新雇员后需要将其?DEPT_NO参数这只为DEPARTMENT表当前记录DEPT_NO字段值. FIBPlus允许通过添加前缀MAS_来自动实现.:

  INSERT INTO EMPLOYEE(

    EMP_NO,  FIRST_NAME,  LAST_NAME,   PHONE_EXT,  HIRE_DATE,

    DEPT_NO,

    JOB_CODE,  JOB_GRADE, JOB_COUNTRY,  SALARY

)

VALUES(

    ?EMP_NO,  ?FIRST_NAME,  ?LAST_NAME, ?PHONE_EXT, ?HIRE_DATE,

    ?MAS_DEPT_NO,

    ?JOB_CODE,  ?JOB_GRADE, ?JOB_COUNTRY, ?SALARY

)

现在可以明确的看到EMPLOYEE.DEPT_NO的值是取自DEPARMENT表(EMPLOYEE对应的主表)当前记录的DEPT_NO字段值.同样修改UpdateSQL:

   UPDATE EMPLOYEE SET

    FIRST_NAME = ?FIRST_NAME,

    LAST_NAME = ?LAST_NAME,

    PHONE_EXT = ?PHONE_EXT,

    HIRE_DATE = ?HIRE_DATE,

    DEPT_NO = ?MAS_DEPT_NO,

    JOB_CODE = ?JOB_CODE,

    JOB_GRADE = ?JOB_GRADE,

    JOB_COUNTRY = ?JOB_COUNTRY,

    SALARY = ?SALARY

 WHERE    

            EMP_NO = ?OLD_EMP_NO

在代码中稍作修改:

procedure TForm1.Button1Click(Sender: TObject);

begin

  pFIBDatabase1.DBName := DBNameE.Text;

  pFIBDatabase1.ConnectParams.UserName := UserNameE.Text;

  pFIBDatabase1.ConnectParams.Password := PasswordE.Text;

  pFIBDatabase1.Open;

 

  pFIBDataSet1.Open;

  pFIBDataSet2.Open;

end;

启动应用程序.在DBGrid1中选择记录,DBGrid2中的内容自动变化.注册表功能完成:

图14.

下载范例代码example

步骤5.制定主从机制

FIBPlus也允许设置一些独特的主从机制.还记得上面范例需要手动打开细表(pFIBDataSet2)吗.对于简单连接很简单,但对多个主从关系或多层主从关系(master-detail-subdetail),手动操作打开所有数据集会引起错误.必须在主表打开时启动从表.为避免不必要的代码TpFIBDataSet包括一个特殊的DetailOptions属性:

图15.

如果设置dcForceOpen可以完全确保在主表打开时从表自动打开.现在可以删除pFIBDataSet2.Open这句代码.无论多复杂的主从连接,所有的查询都会按顺序开启.

第二个重要的选项使dcWaitEndMasterScroll.假设用户在滚动DBGrid1试图查找一个部门.每一次DBGrid1的当前记录变化, pFIBDataSet2都自动重新开启查询.明显滚动主表记录时会引起一系列的复杂查询,增加了网络流量.事实上只需要用户最后在DBGrid1上选择了一个部门后才需要重新打开细表查询. FIBPlus帮助避免这些冗余的查询. 将dcWaitEndMasterScroll键值加入到DetailConditions,细表只会在指定间隔后才重新打开(间隔时间由WaitEndMasterInterval设置).

因此当用户简单的切换DBGrid1当前记录,当前记录变化将激活一个时间触发器重新打开细表重新.如果指定时间内又切换到其他记录,时间触发器将重新开始计时.直到用户找到了必要的记录位置,细表查询才会重新查询,这意味着pFIBDataSet2只执行了一次而不是很多次.

结论

现在已经知道如何使用FIBPlus的主从连接,如果连接到数据库,执行查询,生成修改命令,使用两个事务上下文,及连接主从数据集.但描述的很简单建议彻底的学习一下范例代码. 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值