DataWindow的数据更新技术及应用
华铨平
摘要:
本文介绍DataWindow的数据缓冲技术,利用该技术探索一种对非数据源表数据即时更新操作的实现方法。
关键字:数据窗口、数据缓冲、即时更新。
正文:
PowerBuilder是一个强有力的数据库前端开发工具,其DataWindow(数据窗口)控件充分体现了PowerBuilder与数据库系统的紧密结合,是PowerBuilder中拥有专利技术最多、应用最为广泛的控件。它对数据的处理方法相当简洁,有关DataWindow的数据显示、编辑、有效性检验、报表打印技术方面的文章介绍的也很多。本文结合作者多年的开发经验,利用DataWindow的数据缓冲技术,探索一种对非数据源表数据即时更新操作的实现方法。
一、DataWindow的数据缓冲机制
PowerBuilder虚拟机在处理数据窗口时,为每一个DataWindow开辟了主记录缓冲区(Primary)、删除记录缓冲区(Deleted)、过滤记录缓冲区(Filter)。每个缓冲区又分两类,当前缓冲区(Current)存储当前编辑过的数据,原来缓冲区(Original)存储最初收到的数据。数据行有四种状态:NotModified、DataModified、New、NewModified,列有二种状态:NotModified、DataModified。每次对DataWindow调用Update函数时产生对应的SQL语句如下(假定在DataWindow的Update属性窗口中设置关键字修改用Update):
Primary | Deleted | Filter | |
NotModified | 不产生 | Delete语句 | 不产生 |
DataModified | Update语句 | Delete语句 | Update语句 |
New | 不产生 | 不产生 | 不产生 |
NewModified | Insert语句 | 不产生 | Insert语句 |
备注 | 用Current数据 | 用Original数据 | 用Current数据 |
DataWindow从数据库收到数据后,首先存储在主记录缓冲区,删除某个记录就将该记录移到删除记录缓冲区,将过滤的记录存储在过滤缓冲区。数据初始状态为NotModified,数据有更改后状态为DataModified,且原来缓冲区中仍然保留着原来的数据。新增记录状态为New,有数据输入后状态置为NewModified。
二、相关DataWindow函数
ModifiedCount()函数根据Primary缓冲区和Filter缓冲区判别DataWindow中已被修改的行数。DeletedCount()返回Deleted缓冲区中记录的行数,即已经删除记录的行数。这两个函数可以判断数据是否被更改。
要设置某缓冲区记录行或列的状态,可以用函数SetItemStatus ( row, column, dwbuffer, status ),其中:row是要设置状态的记录行号,column是要设置状态的记录列号,为零表示设置行状态。dwbuffer是要设置状态的数据缓冲区类型。status是要设置的状态值。同样通过函数GetItemStatus ( row, column, dwbuffer)可以获取某缓冲区记录行或列的状态。
函数GetNextModified ( row, dwbuffer )的功能是从row行开始查找下一个修
改过的记录行号。
对DataWindow单个数据项的访问,可以用这样的表达式: dwcontrol.Object.columnname{.buffer}{.datasource}[row]
默认访问的是主缓冲区的当前值,例如dw_detail.object.shuliang[2]访问dw_detail对象主缓冲区第2行shuliang列的当前值;dw_detail.object.shuliang.Delete.Original[2]访问删除缓冲区第2行shuliang的原来值。
三、应用实例
在数据库应用系统设计中,经常会碰到这样的问题,比如对客户销售单据编辑修改完成后,希望能及时更改客户库存、客户应收帐款表的本期销售的值。笔者和同事曾采用过对数据库相关表的定时或手动刷新,在数据库系统中也设计过UPDATE、INSERT触发器以达到即时更新客户库存、客户应收帐款的值。这里,笔者用DataWindow数据缓冲机制来解决这一问题。
1、问题涉及的数据库表结构
客户销售单头表,字段有单据号、日期、客户号等,是dw_master控件的主要数据源表。
客户销售单项表,字段有单据号、序号、产品号、售价、数量等,是dw_detail控件的主要数据源表。
客户收付存表,字段有月份号、客户号、产品号、期初库存数量、金额、本期收入数量、金额、本期付出数量、金额等。
客户应收帐款表,字段有月份号、客户号、期初应收、本期开票、本期收款等。
2、窗口对象的设计
销售单据编辑窗口对象主要有主从DataWIndow控件dw_master、dw_detail处理销售单头、项数据,窗口界面如图(一)。
图(一)
其中定义了在保存单据数据时调用的窗口函数wf_updatekucun,主要代码:
lu_update = CREATE u_updatekucun_xs//建立自定义的对象
IF lu_update.of_init_bqfc(dw_master) < 0 THEN RETURN 1//初始化调用
li_ret = lu_update.of_updatesfcdetail_bqfc(dw_detail)//更改客户收付存表
3、设计自定义不可视对象
窗口函数调用了一个PowerBuilder的自定义不可视对象u_updatekucun_xs 来处理库存、应收帐款的即时更新,该对象主要实例变量及函数说明如下:
1)主要实例变量及说明
datawindow dw_detail,dw_master //存放传递来的主从DataWindow控件对象
string ls_kh_cur,ls_kh_org //存放主dw_master中当前、原来客户号
Dec ld_je_cur,ld_je_org//存放从dw_detail中当前、原来销售金额
long ll_sl_cur,ll_sl_org//存放从dw_detail中当前、原来销售数量
string ls_cp_cur,ls_cp_org//存放从dw_detail中当前、原来产品号
Boolean ib_update = TRUE //初始操作后,是否要继续修改库存
2)主要函数及说明
函数of_init_bqfc是进行初始操作,传递销售单编辑窗口中的主DataWindow控件对象,获取该对象中当前、原来客户号。
integer of_init_bqfc (ref datawindow adw_master);
ls_yuefen = f_getbenqi_yuefen()//取本期帐册月份号
dw_master = adw_master//该DataWindow控件对象至多有1行记录。
IF dw_master.GetRow() = 0 THEN //记录已删除
IF dw_master.DeletedCount() = 0 THEN //删除行是新行
ib_update = FALSE
Return 1
END IF
//删除行是旧行,则
ls_kh_org = dw_master.object.kehu_id.Delete.Original[1]//取原来的客户号
ls_kh_cur = ls_kh_org//该行无实际意义,此时不会有dw_dettail主缓冲区记录
ELSE //主dw_master有记录,可能是新的,也可能是旧的
dwItemStatus Rowstatus
Rowstatus=dw_master.GetItemStatus(1,0, Primary!)//当前行状态
CHOOSE CASE Rowstatus //根据当前行状态作不同处理
CASE NewModified!, NotModified! //是新行且已更改或是旧行且没有更改
ls_kh_cur= dw_master.object.kehu_id[1]
ls_kh_org= ls_kh_cur
CASE DataModified! //是旧行且已更改
ls_kh_cur= dw_master.object.kehu_id[1]
ls_kh_org= dw_master.object.kehu_id.Original[1]
END CHOOSE
END IF
RETURN 1
函数of_updatesfcdetail_bqfc则根据销售项数据修改客户的相应值。
integer of_updatesfcdetail_bqfc (ref datawindow adw_detail);
dw_detail = adw_detail
IF ib_update = FALSE THEN RETURN 1
//处理主缓冲区记录
long Rows, row = 0, count = 0,i
dwItemStatus Rowstatus,status
Rows = dw_detail.RowCount( )
//如果改变客户,则每行dw_detail未修改的记录都认为已修改
IF ls_kh_cur <> ls_kh_org THEN
FOR i = 1 to dw_detail.RowCount()
Rowstatus=dw_detail.GetItemStatus(i,0, Primary!)
IF Rowstatus = NotModified! THEN
dw_detail.SetItemStatus(i,0, Primary!,DataModified!)
END IF
NEXT
END IF
DO WHILE row <= Rows
row = dw_detail.GetNextModified(row, Primary!)//取第一个修改过的记录行号
IF row <= 0 THEN EXIT
Rowstatus=dw_detail.GetItemStatus(row,0, Primary!)//当前行状态
CHOOSE CASE Rowstatus
CASE NewModified! //是新行且已更改,用当前值更改
of_GetRow_cur(row,"Primary") //取Primary缓冲区row行当前值
//当前客户库存的本期付出加上当前值
IF of_update_bqfc(ls_kh_cur,ls_cp_cur,ll_sl_cur,ld_je_cur) < 0 THEN
Return -1
END IF
CASE DataModified! //是旧行且已更改
//原来客户库存的本期付出减去原来值
of_GetRow_org(row,"Primary")
IF of_update_bqfc(ls_kh_org,ls_cp_org,- ll_sl_org, - ld_je_org) < 0 THEN
Return -1
END IF
//当前客户库存的本期付出加上当前值
of_GetRow_cur(row,"Primary")
IF of_update_bqfc(ls_kh_cur,ls_cp_cur,ll_sl_cur,ld_je_cur) < 0 THEN
Return -1
END IF
END CHOOSE
LOOP
//处理Delete缓冲区区记录
Rows = dw_detail.DeletedCount( )
FOR row = 1 to Rows //原来客户库存的本期付出减去原来值
of_GetRow_Org(row,"Delete")
IF of_update_bqfc(ls_kh_org,ls_cp_org, - ll_sl_org, - ld_je_org) < 0 THEN
Return -1
END IF
NEXT
RETURN 1
函数of_getrow_cur获取指定缓冲区数据行的当前值。
integer of_getrow_cur (integer row, string buffer);
Dec ld_shoujia
CHOOSE CASE buffer
CASE "Primary"
ls_cp_cur= dw_detail.object.chanpin_id[row]
ll_sl_cur = dw_detail.object.shuliang[row]
ld_je_cur = ll_sl_cur * dw_detail.object.shoujia[row]
CASE "Delete"
ls_cp_cur = dw_detail.object.chanpin_id.Delete[row]
ll_sl_cur = dw_detail.object.shuliang.Delete[row]
ld_je_cur = ll_sl_cur * dw_detail.object.shoujia .Delete[row]
CASE "Filter"
ls_cp_cur = dw_detail.object.chanpin_id.Filter[row]
ll_sl_cur = dw_detail.object.shuliang.Filter[row]
ld_je_cur = ll_sl_cur * dw_detail.object.shoujia.Filter[row]
END CHOOSE
RETURN 1
函数of_getrow_org获取指定缓冲区指定数据行原来的产品号、数量、金额赋值给实例变量ls_cp_org,ll_sl_org,ld_je_org,代码同函数of_getrow_cur类似,只是访问时在对应列对象后加”. Original”以表明访问的是缓冲区的原来值。如:
ls_cp_org = dw_detail.object.chanpin_id.Original[row]
函数of_update_bqfc的功能是修改客户库存收付存表的本期付出值、应收帐款表的本期销售值。
integer of_update_bqfc (string ls_id, string ls_cp_id, long ll_sl, decimal ld_je);
UPDATE bsc_sfcdetail
SET bqfc = bqfc + :ll_sl , bqfc_j = bqfc_j + :ld_je
WHERE chanpin_id = :ls_cp_id AND id = :ls_id;
IF NOT sqlca.SQLNRows > 0 THEN//是新记录,则
INSERT INTO bsc_sfcdetail (yuefen,id, chanpin_id,bqfc,bqfc_j)
VALUES (:ls_yuefen,:ls_id,:ls_cp_id,:ll_sl, :ld_je) ;
END IF
f_checkTransOk(SQLCA) //自定义函数,检查数据库事务对象执行状况
......
更改应收帐款表cw_sfcdetail本期销售值的方法与更改客户收付存表本期付出值的方法类似。
RETURN 1
四、结束语
在笔者参与开发的分销管理系统中对发货单、入库单、调拨单等数据编辑窗口也采用了DataWindow的数据更新技术即时更新相关数据表,使用户在这些单据的编辑过程中能及时看到最新库存、应收帐款等敏感信息。数据窗口控件是PowerBuilder中最令人激动的控件,深入了解数据窗口的处理机制,灵活运用数据窗口技术,一定会使程序员获益非浅。在笔者开发过程中对每个单据又分为”未处理”、”在处理”、”已处理”,根据不同状态间的变化确定不同的更新策略。在宁波维科销售有限公司、兴洋浙东毛毯有限公司等单位具体应用,取得较好的效果。