摘要:
本文從VB數據庫體系結構的角度出發,結合一個具體實例,闡述了在VB中兼容非Access格式數據庫的具體方法和技巧。
關鍵詞:VB、非Access數據庫、數據存取對象
一個完整的數據庫管理系統(DBMS)應是能兼容市面上各種較流行數據格式的系統,它充分考慮了不同用戶的實際要求。鑒於目前市面上有多種數據庫格式(如Foxpro、DBase、Paradox等)流行,因而在VB數據庫應用程序中兼容非Access數據庫就顯得尤為重要了。
作為一種流行的開發平台,VB提供了強大的數據庫功能。主要有以下三種:數據控件法:使用數據存取對象(Data Access Object )法;直接調用ODBC 2.0 API接口函數法。其中調用數據存取對象的方法相對其它兩種方法具有方便靈活、功能強大的突出優點。本文即從調用數據庫存取對象的方法出發,實現了非Access格式數據庫(以FoxPro數據庫為例)的建新庫、拷貝數據庫結構、動態調入等操作,闡述了從編程技巧上彌補VB對這些外來數據庫支持不足的可行性。
一、VB數據庫的體系結構
VB數據庫的核心結構是所謂的MicroSoft JET數據庫引擎,它為VB與數據庫的接口提供了基本的方法和手段。JET引擎被Visual Basic、Microsoft Access和其它Microsoft產品所共享。因而在VB中Access數據庫格式是一種標准的內置格式,所有的非Access數據庫都被稱為外來數據庫。
JET引擎的作用就像是一塊“面板”,在其上可以插入多種索引順序存取方法(即ISAM)數據驅動程序。這就是VB對非Access數據庫具有豐富支持的真正原因。VB專業版中提供了FoxPro、DBase(或Xbase)、Paradox、BtrIEve等數據庫的ISAM驅動程序,這就使得VB能支持這些數據庫格式。另外,其他的許多兼容ISAM的驅動程序也可以通過從廠商的售後服務得到。因而從理論上說,VB能支持所有兼容ISAM的數據庫格式(前提是只需獲得這些數據庫的ISAM驅動接口程序)。
二、使用非Access數據庫時的參數設置及配置文件的參數讀取
值得注意的是,大多數的程序員都不注重數據庫配置文件的使用,殊不知這是極為重要的。
如果在VB的程序中使用了數據庫的操作,將應用程序生成EXE文件或打包生成安裝程序後,則必須提供一個數據庫配置(.INI)文件,在INI文件中可以對不同類型的數據庫進行設置。如果找不到這個INI文件,將會導致不能訪問數據庫。通常情況下,INI文件的文件名和應用程序的名稱相同,所以如果沒有指明,VB的程序會在Windows子目錄中去找和應用程序同名的INI文件。可以使用VB中的SetDataAccessOptions語句來設置INI文件。
SetDataAccessOptions語句的用法如下:
SetDataAccessOptions 1,IniFileName
其中IniFileName參數指明的是INI文件的帶路徑的文件名。
值得注意的是,當應用程序找不到這個INI文件時,或在調用OpenDataBase函數時對其Connect參數值沒有設定為VB規定的標准值,如對FoxPro 2.5格式設定為了“FoxPro;”(應為“FoxPro 2.5;”),或者沒有安裝相應的ISAM驅動程序,則此時VB會顯示一條錯誤信息“Not Found Installable ISAM”。
通常,INI文件在應用程序分發出去以前已經生成,或者在安裝時動態生成,也可以在應用程序中自己生成。通常這種INI文件中有“[Options]”、“[ISAM]”、“[Installed ISAMs]”、“[FoxPro ISAM]”、“[DBase ISAM]”、“[Paradox ISAM]”等設置段,對於一個完整的應用程序則還應有一個屬於應用程序自己的設置段如“[MyDB]”。可在其中設置DataType、Server、DataBase、OpenOnStartup、DisplaySQL、QueryTimeOut等較為重要的數據庫參數,並以此限定應用程序一般的運行環境。
Windows API接口函數在Win95系統提供的動態鏈接庫中提供了一個OSWritePrivateProfileString函數,此函數能按Windows下配置文件(.INI)的書寫格式寫入信息。
在通常情況下,應用程序還需要在運行時讀取配置文件內相關項的參數。比如PageTimeOut(頁加鎖超時時限)、MaxBufferSize(緩沖區大小)、LockRetry(加鎖失敗時重試次數)等參數,通過對這些參數的讀取對應用程序運行環境的設定、潛在錯誤的捕獲等均會有很大的改善。
設此應用程序的數據庫配置文件為MyDB.INI,則具體過程如下:
Funtion GetINIString$( Byval Fname$,Byval szItem$,Byval szDeFault$ )
’此自定義子函數實現INI文件內設置段內參數的讀取
Dim Tmp As String, x As Integer
Tmp = String( 2048,32 )
x = OSGetPrivateProfileString( Fname$,szItem$,szDefault$,Tmp,Len(Tmp),“MyDB.INI”)
GetINIString = Mid$( Tmp,1,x )
End Function
通過此函數就能實現對各種數據庫格式的讀取。
三、調用數據存取對象對非Access數據庫編程的方法及其實例
VB專業版中使用數據庫存取對象變量(DAO)的方法最具有功能強大、編程靈活的特點。它能夠在程序中存取ODBC 2.0的管理函數;可以控制多種記錄集類型:Dynaset,Snapshot及Table記錄集合對象;可以存儲過程和查詢動作;可以存取數據庫集合對象,例如TableDefs,FIElds,Indexes及QueryDefs;具有真正的事物處理能力。這種方法對數據庫處理的大多數情況都非常適用。
從VB的程序代碼的角度來看,提供給VB程序員的記錄集對象(RecordSet)同所使用的數據庫格式及類型是相互獨立的。即對FoxPro等數據庫仍然可以使用眾多的數據庫存取對象變量,這就為非Access數據庫的訪問提供了最重要的前提和方法。
在VB中從一種數據庫類型轉化為另一種數據庫類型幾乎不需要或只需要很少的代碼修改。而且,盡管DBase、Paradox本身的DDL(Data Definition Language,即數據定義語言)和DML(Data Manipulation Language,即數據操縱語言)是非結構化查詢的,但它們仍然可以使用VB的SQL語句和JET引擎來操縱。
因而對FoxPro等非Access數據庫而言,調用數據庫存取對象的方法同樣也是一種最佳的選擇。
(一)非Access數據庫的新建及庫結構的修改
VB專業版中的數據庫存取對象變量可以分為兩類,一類用於數據庫結構的維護和管理,另一類用於數據的存取。其中表示數據庫結構時可以使用下面的對象:DataBase、TableDef、Field、Index,以及三個集合(Collection):TableDefs、FIElds和Indexes。每一個集合都是由若干個對象組成的,這些數據對象的集合可以完全看作是一個數組,並按數組的方法來調用。
一旦數據庫對象建立後,就可以用它對數據庫的結構進行修改和數據處理。
對於非Access數據庫,大部分都是對應於一個目錄,所以可以使用VB的MkDir語句先生成一個目錄,亦即新建一個數據庫。而每一個非Access數據庫文件可看作是此目錄下的一個數據表(Table),但實際上它們是互相獨立的。
下面是新建一個FoxPro 2.5格式數據庫的程序實例。
Sub CreateNew ( )
Dim Db1 As database, Td As TableDefs
Dim T1 As New Tabledef,F1 As New Field, F2 As New Field, F3 As New FIEld
Dim Ix1 As New Index
Dim Path As String
Const DB_TEXT = 10,DB_INTEGER = 3
ChDir "\"
Path$ = InputBox( "請輸入新路徑名:", "輸入對話框" )
MkDir Path$ ’新建一個子目錄
Set Db1 = OpenDatabase(Path$, True, False, "FoxPro 2.5;")
Set Td = Db1.TableDefs
T1.Name = "MyDB" ’新建一個數據表,數據表名為MyDB
F1.Name = "Name", F1.Type = DB_TEXT, F1.Size = 20
F2.Name = "Class", F2.Type = DB_TEXT, F2.Size = 20
F3.Name = "Grade", F3.Type = DB_INTEGER
T1.FIElds.Append F1 ’向數據表中添加這些字段
T1.FIElds.Append F2
T1.FIElds.Append F3
Ix1.Name = "Name" ,Ix1.FIElds = "Name", Ix1.Primary = True ’新建索引
T1.Indexes.Append Ix1 ’向數據庫的Indexes集合中添加新的索引
Td.Append T1 ’向TableDefs集合中添加新表
Db1.Close ’必須先關閉數據庫對象再退出
End Sub
在此段程序中值得注意的是,對非Access數據庫的新建不用CreateDatabase函數,而是用OpenDatabase函數,這點與Access數據庫大不一樣,但也僅僅是針對非Access數據庫而言才能用OpenDatabase函數來新建一個數據庫對象。
在VB中,外來數據庫的不同格式只在OpenDatabase函數的最後一個參數Connect中有所體現,不同格式的外來數據庫其Connect參數值也不同,除此以外,在VB專業版中其編程的方法和步驟及技巧是基本相同的。
新建子目錄後,不能用ChDir語句進入它,否則會出現“‘MyDB’ is not a valid path”的錯誤。同時,對F1、F2、F3等新建字段對象的定義也必須分別定義,否則會出現“Element not defined”(變量未定義)的錯誤。
通過一定的編程技巧還可以實現非Access數據庫的庫結構的拷貝,下面是一段相應的程序。
Function GetPos( TFname$ ) ’此自定義函數完成對帶路徑文件名中最後一個“\”符號的定位
Dim I As Integer,Tmp As String
Tmp$ = TFname$
For I = 0 To 255
Pos% = Pos% + InStr( 1, Tmp$, "\" )
E1% = InStr( 1, Tmp$, "\" )
Tmp$ = Right$( Tmp$, Len(TFname$) - Pos% )
If E1% = 0 Then ’找到最後一個“\”符號的位置,並記下來
GetPos = Pos%
Exit For
End If
Next I
End Funtion
Sub CopyStruc( )
Dim Db1 As database, Ds1 As Dynaset,Td As TableDefs, Fld As FIElds
Dim Fname,SourceF,DestF,Path As String,Pos1 As Integer
CMD1.Filter = "FoxPro數據庫文件(*.DBF)|*.DBF|所有文件|*.*" ’CMD1為一個對話框的控制名
CMD1.DialogTitle = "調入Ms FoxPro數據庫文件"
CMD1.FilterIndex = 1
CMD1.Action = 1
DestF$ = InputBox$( "請輸入目標文件名:", "輸入對話框" )
If CMD1.FileName = “ ”Or DestF$ = " " Then
MsgBox "源文件或目標文件名為空"
Exit Sub
Else
SourceF$ = CMD1.Filename
End If
FileCopy SourceF$, DestF$
Pos1% = GetPos( SourceF$ )
Path$ = Left$( SourceF$, Pos1% ) ’獲得源文件所在的路徑名
Fn$ = Left$( DestF$, InStr(1, DestF$, ".") - 1 ) ’獲得新文件的數據庫名
’Fn$為實際的Foxpro數據庫名,也即CreateDynaset函數內的source屬性值
Set Db1 = OpenDatabase( Path$, True, False, "FoxPro 2.5;" )
Set Ds1 = Db1.CreateDynaset( Fn$ )
If Ds1.EOF And Ds1.BOF Then ’數據庫內的無記錄則退出
TotalNum% = 0
MsgBox "此數據表為空表!"
Exit Sub
End If
’刪除記錄,保留庫結構
Ds1.MoveFirst
Do
Ds1.Delete
Ds1.MoveNext
Loop Until Ds1.EOF
End Sub
可見,拷貝庫結構的方法在於把一個已存在的數據庫拷貝到一個新文件中,然後再刪除新文件內的所有記錄,保留其庫結構,得到的就是一個新建的庫結構完整的空庫。
(二)非Access數據庫的動態調入
在實際應用的很多情況下,經常需要在對一些事先並不知道其具體庫結構的數據庫進行調入、顯示及打印其記錄。因而實現未知格式數據庫的動態調入也是評價VB數據庫應用程序兼容性的一個重要標志。
在VB中,網格控件非常適合用於浏覽數據庫中的數據,只需把數據放入網格即可。
在使用網格時動態調入的關鍵在於記錄(Colume)內容和字段(Row)內容(包括字段的名稱、類型、值等)的讀取,因而生成一個可以對應於一個或多個數據表中的全部或部分記錄的Dynaset對象是非常必要的。Dynaset對象還可以是一個動態查詢的結果,能進行記錄的增加、刪除和修改等操作。
下面是一段用網格顯示FoxPro數據庫的程序。
Sub DBLoad( )
Dim Db1 As database, Ds1 As Dynaset,Td As TableDefs,Fld As FIElds
Dim Fname,Tmp,Path ToTalNum As String,I,J,Pos1 As Integer
Dim MyNum ’定義一個變體型數據
CMD1.Filter = "FoxPro數據庫文件(*.DBF)|*.DBF|所有文件|*.*"
CMD1.DialogTitle = "調入Ms FoxPro數據庫文件"
CMD1.FilterIndex = 1
CMD1.Action = 1
Fname$ = CMD1.Filename
Pos1% = GetPos( Fname$ )
Path$ = Left$( Fname$, Pos1% )
Tmp$ = Right$( Fname$, Len(Fname$)-Pos1)
Fn$ = Left$( Tmp$, Instr( 1,Tmp$,“.”) - 1 )
Set Db1 = OpenDatabase( Path$, True, False, "FoxPro 2.5;" )
Set Ds1 = Db1.CreateDynaset( Fn$ )
If Ds1.EOF And Ds1.BOF Then '數據庫表內無記錄則退出
TotalNum = 0
MsgBox "此數據表為空表!"
Exit Sub
Else '顯示數據庫表內的實際記錄數
Ds1.MoveLast
TotalNum = Ds1.RecordCount
Grid1.Rows = TotalNum + 1 ’置網格的實際行數
Total.Caption = Str$(TotalNum)
End If
'置網格的實際列數並置每列的寬度
Set Td = Db1.TableDefs
Set Fld = Td( Fn$ ).FIElds
Grid1.Cols = Fld.Count + 1
Grid1.ColWidth(0) = 600
For I = 1 To Fld.Count
Grid1.ColWidth(I) = 1500
Next I
'在網格的第一行內填入字段名
Grid1.Row = 0, Grid1.Col = 0
Grid1.Text = "序號"
For I = 1 To Fld.Count
Grid1.Col = I
Grid1.Text = Fld(I - 1).Name
Next I
'在網格中填入相應的數據
Ds1.MoveFirst
I = 1
Do While Not Ds1.EOF
Grid1.RowHeight(I) = 300
Grid1.Row = I
Grid1.Col = 0
Grid1.Text = I
For J = 1 To Fld.Count
Grid1.Col = J
MyNum = Ds1.FIElds(J - 1).Value
'對記錄的數據類型進行判斷後做相應的處理
If IsNumeric( MyNum ) Or IsDate( MyNum ) Then
Grid1.Text = Str$( Ds1.FIElds(J - 1).Value )
Else If VarType( MyNum ) = 8 Then
Grid1.Text = Ds1.FIElds(J - 1).Value
Else If VarType( MyNum ) = 0 Or VarType( MyNum ) = 1 Then
Grid1.Text = " "
End If
On Error Resume Next
Next J
Ds1.MoveNext
I = I + 1
Loop
Ds1.Close
Db1.Close
Exit Sub
最後應記住,在VB的數據庫應用程序運行之前,一定要在AUTOEXEC.BAT文件中加入一句SHARE.EXE /L:500。
以上所有程序均在Pentium/166機、中文Windows95下用VB4調試通過。
四、結束語
對非Access數據庫的兼容是VB數據庫編程中不可分割的重要部分。因而熟練掌握使用DAO方法對非Access數據庫的編程是極為重要的。並且,一定編程技巧的應用也有助於彌補VB對外來數據庫支持的不足。