admin 管理员组

文章数量: 887021


2023年12月16日发(作者:feign调用走网关吗)

’‘ … ’’’●‘ ‘ ’’ ‘‘…‘ …‘ ’… 。 ‘‘… ‘…● ……~… 实用第一 智慧密集 … … . .。. … … . , 基于Delphi XE10实现数据库的实时备份和恢复 张存平,罗郁霞,吴铁堤,梁宁,卢英杰 (广州市殡葬服务中心电脑部,广州510507) 摘要:提出一种用程序在客户端实现数据库的实时备份和恢复的编程方法,方法独特、新颖,且不仅不增加客户 本方法只是基于两层架构,当然也很容易改为三层架构,编程难度不大、易 端编程量,反而大大缩减了编程量懂、易于实现,稍懂数据库编程的人都能学会,不失为解决数据库安全的好办法 . 关键词:Delphi XEIO语言;Oracle数据库;MySQL语言;数据库实时备份;数据库恢复;客户端快速直连数据库 现在大多数的软件系统都会利用数据库存储数据,随着 业务的不断进行。数据库的数据一直都连续地发生变化。如 何实时备份数据库。是广大系统维护人员最为关心的问题。 数据是软件的处理结果,也是软件所在单位的财富,特别是 大数据的背景之下,数据的重要性更加突出。 传统的实时备份是一些大型商业数据库的核心技术,往 往需要购买高级的T具,还要复杂的配置才能实现。并且对 程序来说,这一切一点都不透明,这多少让程序员有点力不 从心的感觉。作为一个长期编写客户端程序的人来说,经过 深入思考和编程试验,找到了一种用程序来实现数据库的实 时备份和恢复的方法.原理如下: 在Delphi XE编程环境里,对数据库的操作都是以事务为 单位向数据库提交数据改变请求的。而每一个事务又是一系 列SQL语句和参数组成.如果能把这些语句和参数在执行事 务提交的同时,保存下来。不是就可以对数据库进行备份了 吗?似乎有点相当于增量备份。 保存SQL语句没有任何闲难.关键是参数的保存和以后 的复用有点难,冈为通常在这种提交方式下,我们采用的方 法都是根据数据段类型逐一对Query控件的参数动态传值,很 散乱,如果编程的时候郁先保存再执行,编程量太大了,也 无法复用。所以,首先必须改变这种一个个参数的赋值方法, 把所有参数的值和类型合成一个字符串,然后调用一个函数 动态解析并逐一赋值给Query控件.所以设计出一个数据库事 务调用函数是解决问题的关键。下面用一个例子来详细叙述 实现过程。 l建立例子程序的环境 安装好一个Oracle数据库.建立一个用户test,密码123, 两张表: tab1,含有以 字段:Sn员工编号、name姓名、addr住 址、depafl—sn部门编号、l_date髓记日期。 Tab2,含有以下字段:depa ̄一Sll部门编号,depart—name 部门名称 打开Delphi XE10.新建一个VCL的应用项目.主窗口设 为frm—main,编译一下,然后增加一个数据模块DM,在DM 的窗口ffm—dnl星放5个控件:FDPhvs0rackD riverLink1, 2017.01 ]==I 蕊 堆 FDGUlxWaitCursorl,FDConnection 1,DataSource 1,FDQuery l, J"巴oracle的4个连接库文件0ci.dll,oraoeci 1 1.dl1.oraociei 1 1. dll,orasql11.dll拷贝在编译后的运行文件所在的目录下。选中 FDPhvs0ra( IeDriverLink l控件,设置其VendorLib指向(,ci.dll, 选中FDConnectionl,在其Params属性里输入以下内容: DriverlD=0ra Database=(DESCRIPTION=(ADDRESS LIST=(AD— DRESS:(PROTOCOL=TCP)(HOST=172.16.1.95){PORT= 1 521)))(CONNECT_DATA=(SERVER=DEDICATED){SER- VICE—NAME=orc1))) User_Name=test Password=1 23 CharacterSet=UTF8 注意:Database行中不能有分行符。通过FDC连接数据 库,不需要安装客户端,只要有相应的库就行了。 设女 后,选择其connected屙l生为true,连接成功。 将FDQuery l的connection属性指定为FDConnection l, DataSourceI的DataSet属性指定为FDQuery1.并在FDQuery1 的SQL属性编辑框里输入select from tabl语句 用其他连接 Oracle数据库的工具,比如说PL SQL连接数据库,在tabl输 入两条记录.在主窗口放DBGrid1控件,其DataSource设为 DM巾的DataSourceI,将FDQueryl的connected属性置为 true,数据库的记录就会显示在DBGrid1中,到此,环境设置 完毕。 2全新数据库事务提交函数设计 (1)编写一个字符串这分割函数。 function SplitStrZcp(source_str:string;split_str:string): TstringList; Var i:integer; Splitted:TArray<S ng>: begin 作者简介:张存平(1969一),男,硕士,研究方向:软件 设计 收稿日期:2016-09—05 

 .…. D^T^BASE&IHf0B啊^TION MANAGE- ∥的日期格式 para—L::SplitStrZcp(para, : ) ,/将字符串分割为数组 Resuft:=TstringList.Create(); Split(split—str); Splitted:=sourcestr。_for i:=Low(Splitted)to High(Splitted)do Result.Add(SpNtted[i]); end; type:=TStringList.Create;∥提取字段类型 ziduandata:=TS ngList.Create;∥提取字段值 ziduan—_——for i:=0 to paraL.Count一1 dO —begin (2)编写一个从SQL语句ff1提取字段名称的函数,供后 if{i mod 2)=0 then 面使用。 function get_zi_duan(sql—s:stmg):Tst gList: Var i,len,po:integer; s:string; begin //b,SQL语句中提取字段 Resu Jt:=Tst ringList.Create; len:=sq1.—S.Length; pO:=0: for i:=0 to len一1 do begin s:=sql_s.Substring(i,1): if sql—S.Substring(i.1)= : then p0:=i: if po>0then begin if((sql_s.Substring(i,1)- )or(sqLs.Substring(i,1): , 1Or (sql—S.Substring(i,1)= ) ))then begin Result.Add(sql—S.Substring(po+1,i-po-1)): pO:=0: end; if((i=len一1)and《po>0))then begin Result.Add(sql—S.Subst ring(po+1,i-po)): pO:=O: end; end; end; end; (3)将受赋值的参数,按照SQI 句中JI{现的 序,先值后类型 朋“:”号连接为一个字符串,和SQL 句一起 传送给以下过程,实现对每一个Query赋值 procedure Tfrm—dm.zi—duan—deal(sql—s:str1ng:para:string:qu:TFDQuery); Var para——L,ziduan—name。zi——duantype.ziduandata:_—_TStringList; i:integer; AFormat:TFormatSettings; begin AFormat.ShortDateFormat:= YWY—mm—dd : AFormat.DateSeDaratOr:= 一 AFormat.LongTimeFormat:= hh:mm:ss :Noracle ziduandata,Add(para__L[il.Replace( [maol , :,))//处理含有“:” else zi—duan_type Add(para一 】.Replace( [mao] , : )): end; ziduanname:=get—zi———duan(sql—s);f获取字段名称 for i:=0 to zi duan name.Count一1 do begin//根据字段类型给FDQuery参数动态赋值 if zi—duantype[i]= int then begin qu.parambyname(zi—duan—name[i]).Aslnteger:= strtoint(zi—duan—data[i]) end else if zi—duan_type…= str then begin qu.parambyname(zi——duan——name[i]).AsString:= zi_duan—data[i]; end else if zi—duan_type[i]= date then begin qu.parambyname(zi——duan——nameli]).AsDate:= strtodate(zi_duan——data[i].AFormat); end else if zi—duan_type【il= float then begin qu.parambyname(zi——duan——name[i]).AsFIoat:= StrToFloat(zi_duan—datali]); end else if zi—duan_type[i]= datetime then begin qu.parambyname(zi——duan——nameli])AsDateTime:= StrToDateTime(zi——duan——data[i],AFormat); end; end; end; 此过程.,尢埘参数分解为字段类型千"字段flI{_,然后 用 get_zidu (sql一 函数,捩收 段 ,最后依次埘Qumy赋值。 (4)考虑刮一般情况卜,每次事务总足包 数个QueLw提 交,还要每虑事务的容错、『lII滚,所以,这样 丫L次处娌并小 能很好地使用、昕以对事务的iJ; ̄l J-tj小能 接itj这个过 ,还 要设计一个事务处理函数来il,qJll这个过程: function Tfrm—dm.dm—db—transact on(sql—sn:integer; dm—param—L:TstnngList):stnng; var i,n:integer; 61     

’ … … ‘ … ^’。 …‘… … 一…, 实用第一 智慧密集 一., . .….  ,. . . tem.—FDQuery:TFDQuery; s1.pl:string; dm——sql——L:TStringList; begin dm—sql—L:=get_sql(sql—sn):IIZ数据库里提取SQL语句 try tern——FDQuery:=TFDQuery.Create(self); tem—FDQuery.COnnectiOn:=FDCOnnectiOn1: FDCOnnectiOn1.StartTransaction; try for j:=O to dm—sql—L.Count一1 do begin sl:=din—sql—LIj】: pl:=din—param—L【_】= with tem—FDQuery do begin close; sq1.Clear; sq1.Add(s1): -f pl<> 【blank] then zi_duan_deal(sl,p1.tern—FD— Query); prepare; ExecSQL: end; end; FDConnection1.COmm.t: Result:= success : except on E:Exception do//数据库出错处理 begin FDCOnnectiOn1.Rollback; Result:= error +E Message; end; end; finally tem—FDQuery.Destroy; end; end; 为SQL语句在程序巾都是同定的,变化的只是事务的 参数,所以为r程序看起来清爽和以后的事务恢复,这里把 SQL语句作为事务的参数存在数据库里,相关表sql_lab:sn 事务编号,naIllt ̄事务 称,SQL_sn语句编号,sqLtx【语句内 容 相父 数: function Tfrmdm.get—_sql(sn:integer):TStringList; begin Result:=TstringList.Create(); with qu tem do begin close; sq1.Clear; sq1.Add( select sql_txt from sql_tab where sn= 62 " -iI■垣醢_柏巧与鼍 写鼍 i_ :sn order by sqlsn —parambyname( sn,).AsInteger:=sn: prepare; open; end; while not qu tem.Eof do begin Result.Add(qu tem.Fields[O].asstring); qu—tem.Next; end; end; 实际的调用例子如下: procedure Tfrm—main.Buttonl Click(Sender:TObject); Var para—L:TStringList; s:string; begin para—L:=TStringList.Create; para—L.add( 4:int:测试2:str:广州市:str:l:int:2016—11—23: date ) para_L:Add( 1:int:电脑部:str ): s:=frm—dm.dm—db_transaction(1,para—L): if s= success then begin showmessage( 成功执行事务 end else showmessage( 出错了: +s): end; 此段程序对tabI,tab2两个表插入各插入一条记录,代码 是如此的简洁.如果是多字段,更 示ff{这种方法是多么节 省代码。对于不需要参数的,para—L 加入【blank]。 3事务参数的保存 备份事务的信息的保存足在两个库里.一个是在外部的 备份库,用来备份,另一份在主数据库里,用来对比,两个 表的内容差不多,但事务序号足完全一致的,在执行事务前, 先在备份库中保存事务的SQL语句和参数信息。得到一个事 务序号,然后在主数据库的事务里嵌入日志记录过程,和主 数据库事务同时执行。在此备份库选择的是MySQL。 (1)新建两张表。 Back_log表:Log__sn事务序号.1ogin_sn登录者T号, ddate时间点。 Backlog_—para表:Log_sn事务序号,item—SB项目序号, para_txt参数文本,sql—txt SQ[ 语句。连接上MySQL数据库的 步骤如下: 将libmySQL.dll这个库文件拷『j1到执行文件所在的目录, 在frm—dm中放控件FDPhysMySQI DriverLinkI,并把其Ven— dorLib指向该文件。再放一个连接数据库控件FDConnection2, (下转第71页) 

Cells(65536,1+2)=Cells(65536,I+2)4-Cells(65536.m) Next Application.CommandBars(“Standard”).Visible:True 显示“工具栏” Application CommandBars(”Formatting”).Visible=True Cells(65536,t 4-2)=Cells(65536,j+2)/(}一i1一i2) Celts(h,I十2)=Celts(65536,I+2) For m:2 To h 显示“格式栏” Application.DisplayFormulaBar=True 显示“编辑栏” Cells(m,I+3):”=RANK(RC【一1】,R2C[一1】:R &h&“Ci一 1 ActiveWindow.DisplayWorkbookTabs=True 显示“工作表标签” Cells(m,I+3):Cells(m l 4-3) ActiveWindow.DisplayHeadings=True Next 显示“行列标” Worksheets(1).Protect Password:=”zyz2978“ 密码保护 Application.ScreenUpdating=True l灰复屏显 End Sub 显示菜单栏各项 Dim i As Long For i=1 To Application.CommandBa rs(1).Controls.Count Application.CommandBars(1).Controls(i).Visible=True Next 4.8 【模块4一关闭程序】中的代码 Sub关闭系统0 Application.ScreenUpdating:False 屏蔽屏显 Application.WindowState=xlMaximized 应用程序窗口处 于“最大化”状态。 Application.EnableEvents=False 禁用应用程序事件 Application.Quit 关闭 ActiveWorkbook.Close savechanges:=True 保存 Application.EnableEvents=True。I灰复应用程序事件 Application.ScreenUpdating=True 恢复屏显 End Sub —S屯—i也.址—“—“ 止j止.址.il上—S啦.址—S —S上—址—址—^l上—S止—址 —址 ActiveWindow.WindowState:xlMaximized 文件窗口处 于“最大化”状态。 Application.CommandBars(1).Enabled=True 显示“菜单栏” 上-. 止—S止—S止—址.址 —S —S止—址—址.址.S — —il上—址 L.S上— L—S上—址—S —S —S也— 上—址—址(上接第62页) 诈住其param lll输入以下义奉:DriverlD=MySQI Server=172.16.1.95 Port=3306 Database=test CharacterSet=utf8 User Name=root Password=xingLzcp exit; end; trv… (3)主数据库备份过程,只是一个Query-提交,下面只给 m定义,过程免 procedure Tfrmdm.1ogbackupin(sql—sn,log—sn:integer; ———para——dm:TStringList;fqu:TFDQuery); ——去掉其I ̄)ginl rf,rnpt属性,连接成功 、 (2)添加一个qu—ba k的FI)Queu控件 数.返【『1]¨志序 function Tfrmdm.1ogbackupout(sql—dm:TStringList; —__sq1sn为SQL语句编号,l(1gh 为事务执行序号 (-onneetion指 f a—thn为参数数绀,fqu一为指向Query控件。 务删用部分修改代码如下: end; logbackupin(sql—sn,logsn,dmparam—L,temFDQuery) —定为FDConnc‘-tion2.编写 个保存事务参数到MySQ[ 的 para—dm:TStringList):stdng; _———限于篇幅,这个 数不列…了。在主数据库执行事务前, 受凋川此函数,如果…错,就返回错误,不执行主数据库雷 务、修改后的代百l5_如下: functi on Tfrmdm.dmdbtransaction(sql—sn:integer; ——∥内部备份目志记录 FDCOnnectiOn1.COmmit: Result:= success : —dmparam—L:TstringList):string: —4实际布置策略及恢复步骤 果数据库的数据城大,建c义每周作一次全备,清空备 份库平¨丰数据库的口志后开始运行.一旦主数据库崩溃.就 Var i.n:integer; tern——FDQuery:TFDQuery; —用一周 的备份恢复数据库,然后编一段程序,从备份库里 读…所 信息,渊川前而的事务过程执行一遍.就可以把数 据库恢复到崩溃前的一刻。如果数据量不大,一一个J1全备一 次也行的、 s1,pl,logsn:string; dmsqlL:TString List; ————begin dmsqlL:=getsqt(sql—sn): ———logsn:=frmdm.1ogbackupout(dm—sql—L,dm—param—L): ————if togsn.Substring(1,5): error then begin Result:=logsn; —-|一番2覆0日17与.0覃1 'i '71


本文标签: 数据库 事务 备份 参数 数据