Lotus Domino開發心得(一)


  

—- Lotus Domino 是當今辦公自動化系統的主流開發平台之一,目前大部分企業和機構都在使用Lotus Domino 開發自己的無紙辦公系統。在開發過程中,我積累了一些小技巧,現在公布出來,希望能給相關人士提供一些幫助。

 

一. Lotus Domino 與關系數據庫的互操作

 

—- 在項目開發過程中,遇到的第一個棘手的問題是:如何把原先的關系型數據庫中的內容全部導入 Domino 中?因為企業現在的所有數據都集中在一個關系型數據庫中,因此希望Notes庫能與舊的關系庫互操作,而不必在數據庫轉換上浪費更多人力。
—- 此外,由於Lotus Notes是非結構化的數據庫,而關系數據庫屬於結構化數據庫,因此要實現兩者的互操作或數據導入/導出勢必需要一個專門的轉換程序,而這個程序一般來說都屬於附加品,要么由Lotus提供,要么由數據庫廠商提供,如Lotus Notes與Oracle數據庫之間的轉換就有專門的程序(Pump)。一般在購買Lotus產品的時候Lotus公司不會提供此類產品,如果需要可以單獨購買。而對於企業來說,如果企業數據存放在不止一個關系庫中,那么就需要購買若干個此類產品,而且還不一定能買的到,因此能否通過Lotus Script編寫一個通用的數據庫轉換程序就成為本次開發的難點之一。 —- 解決這個問題用到了Lotus Domino R5 中提供的三個Lotus Notes 對象類:ODBCConnection(ODBC 連接)、ODBCQuery(ODBC 查詢)以及ODBCResultSet(ODBC結果集)。應用這三個類並輔以Lotus Script語言就能實現與關系數據庫的互操作問題。

—- 具體解決方法如下:

—- 第一步 在控制面板——>32位ODBC數據源中建立用戶數據源Test;

—- 第二步 在Domino R5中新建一個數據庫Try,並建立一個空白表單Connection,此表單沒有任何內容,然后在表單上創建一個“操作”,起名為“Read”;

—- 第三步 在“Read”操作的編程窗口中選擇編程語言為Lotus Script;

—- 第四步 在編程窗口的對象窗口中點擊“Option”事件,並寫入如下腳本:

—- Uselsx “*lsxodbc” // 使用Lotus Script 擴展對象中的ODBC類

—- 第五步 選中“Declare”事件,在其中寫入: [Copy to clipboard]CODE: Dim session As NotesSession Dim db As NotesDataBase Dim doc As NotesDocument Dim qry As ODBCQuery Dim result As ODBCResultSet Dim con As ODBCConnection

 

定義程序中使用到的各種對象。

 

第六步 選中“Click”事件,在其中寫入: [Copy to clipboard]CODE: Sub Click(Source As Button) ‘ Set New Value Set session = New NotesSession Set con = New ODBCConnection // 新建ODBCConnection對象實例 Set qry = New ODBCQuery // 新建ODBCQuery對象實例 Set result = New ODBCResultSet // 新建ODBCResultSet對象實例 ‘ 取得當前數據庫信息 Set db = session.CurrentDataBase Set doc = New NotesDocument(db) // 新建文檔 doc.form = “connection” // 新建文檔的表單指向connection Call con.Disconnect() // 保證con對象當前沒有連接其他數據源 If con.ConnectTo(”test”) Then // 如果連接成功 Set qry.connection = con // 將建立好連接的con交給query對象 qry.SQL = “SELECT * FROM Table1″ // SQL 語句 Set result.Query = qry // 將已經連接上數據源並寫好SQL 語句的query對象賦給result對象 Call result.Execute() // 執行SQL語句 Do // 循環直到結果集為空 Call result.NextRow() // 指針指向下一條記錄 For i = 1 To result.Numcolumns // Numcolumns屬性記錄關系庫中的字段個數 field = result.FieldName(i) // 根據字段的索引值得到字段的名字 value = result.GetValue(field) // 取得字段值 If Isdate(value) Then // 對日期字段的特殊處理 If value = Datevalue(”0:00:00″) Then value = “” Else value = Format(value,”mm-dd-yyyy”) End If End If Set item = doc.AppendItemValue(field,value) // 將關系庫中的值寫到Notes當前庫的當前表單中 Next Call doc.save(True,True) // 當一條記錄的所有字段都被寫入 Notes庫后保存此文檔 Set db = session.CurrentDataBase Set doc = New NotesDocument(db) // 新建文檔 doc.form = “connection” Loop Until result.IsEndOfData Call con.Disconnect() // 斷開與數據源的連接 Else Messagebox(”Could not connect to server”) End If End Sub —- 最后,保存表單並運行,用鼠標點擊Read 操作后,關系數據庫中的內容就被取到Notes的文檔型數據庫中了。 —- 下面簡要說明一下上述代碼:

—- 1. New。New 關鍵字是用來創建Notes 對象的。New 后面是要創建的Notes對象及所需的參數。使用New 關鍵字需要注意的一點是:在Declare 事件中聲明Notes對象的時候不能使用New 關鍵字,否則編譯時會出錯;但是在Click 事件中可以按 Dim db As New NotesDatabase(“”,“***.NSF”)格式書寫。

—- 2. ODBCConnection 對象是與ODBC建立連接的Notes對象,使用它的ConnectTo方法可以與ODBC用戶數據源中定義的任何一個數據源建立連接,同時返回一些相關的信息,如:表的個數,表中的字段數等。

—- 3. ODBCQuery 對象是用戶編寫標准的SQL查詢語句的對象,它的connection 屬性是指向已經與ODBC數據源建立好連接的ODBCConnection 對象的;它的SQL 屬性是存放用戶編寫的SQL 語句。

—- 4. ODBCResultSet 對象是執行SQL 查詢語句之后存放查詢結果的,它的Query 屬性是指向包含建立起連接(ODBCConnection)的並寫好SQL語句的ODBCQuery對象。

—- 5. ODBCResultSet 對象的NextRow 方法的作用是將ODBCResultSet 的記錄指針指向下一條記錄。因為在取到關系數據庫中的記錄之后,ODBCResultSet對象的指針是為空的,也就是說它並不自動指向結果集的第一條記錄,NextRow方法即實現了將記錄指針指向第一條記錄的功能,並且在以后的循環過程中,此方法還將繼續把指針逐一指向后面的記錄,直到最后一條。

—- 6. 由於原先的關系數據庫中的字段比較多,所以采用在程序中直接寫入字段名的做法並不明智,因此,本程序使用了ODBCResultSet 對象的FieldName(fieldindex)方法來通過字段的編號獲得字段名,然后再使用GetValue(fieldname)函數得到域值,最后調用Document類的AppendItemValue(fieldname,value)函數在表單中追加一個名為fieldname的域並賦值。

—- 在把上述三個對象的屬性分別設置好以后,調用ODBCResultSet 對象的Execute 方法,就可以從關系庫中讀取數據了。

—- 但是使用上面的代碼在進行實際數據庫內容轉換的時候,發現Notes 通過ODBC數據源連接關系數據庫時,無法識別中文字段名。如果關系數據庫的字段是中文名字,那么ODBCResultSet將為空,解決的辦法是將關系數據庫中的所有字段都改為英文名字。經過反復測試,發現並不是所有的中文字段名Notes都不識別,是同關系庫的驅動程序有關,由於本次開發連接的是Foxpro的DBF數據庫,使用的是Foxpro2.6數據庫驅動,因此無法通過ODBC獲取中文字段名,如果連接的關系庫是Microsoft SQL Server7.0則可以通過ODBC數據源識別中文字段名。

—- 在實際數據庫的轉換過程中同時發現的問題還有:該程序執行完一次后不能把關系型數據庫中的內容全部取出來。由於需要轉換的關系型數據庫的結構比較復雜,共有40多個字段,且包括字符、字符串、日期、數字等多種數據類型,同時庫中的記錄比較多(共有3000多條記錄),因此出現這個問題后,有兩個猜測,一是和ODBC驅動所設置的系統緩沖有關,一是和Notes開辟的內存空間有關,然而通過試驗,最終認為和Notes有關的可能性最大。這是因為首先,Notes對其所有的類都設置了緩存,這其中也包括ODBCResultSet,通過調試Lotus Script腳本並多次單步跟蹤腳本的執行情況,發現每次只要執行到同一條數據庫記錄(例如第3328條記錄)時,ODBCResultSet就認為數據集已經到頭了,下面的記錄就都丟了。於是使用關系數據庫軟件打開數據庫,並將其中的字段減少若干條后,就可以一次讀取出全部3000多條記錄。至於需要減少多少個字段才能一次讀取出全部記錄跟原先的關系型數據庫的結構有關,需要具體情況具體實驗,沒有定論。但是可以肯定的一條是Notes本身確實為每個對象類都設置了緩存;並且Notes能根據當前內存的使用情況改變緩存的大小。其次,當用戶開機后不是馬上運行Lotus Notes而是先運行一個其他的程序,然后再運行Notes時,上面提到的第3328條數據庫記錄就有所改變,可能變為第3112條記錄,也可能變為第3218條記錄,這個數字將變得不唯一,具體是多少不得而知,而是與所運行的應用程序有關。

 

—- 上述代碼實現了通過ODBC數據源讀取關系數據庫中的內容寫到Lotus Notes庫,同樣也可以使用上述方法實現在Lotus Notes 程序通過ODBC 數據源寫回到關系數據庫的功能,具體實現方法與上面的過程大同小異,因此只在此附上源代碼:

(1) Option 事件中: [Copy to clipboard]CODE: Uselsx “*lsxodbc”

(2) Clicked 事件中: [Copy to clipboard]CODE: Sub Click(Source As Button) Dim session As NotesSession Dim db As NotesDataBase Dim dc As NotesDocumentCollection 定義程序中用到的對象類 Set session = New NotesSession Set db = session.CurrentDataBase 取到當前數據庫的信息 Dim con As New ODBCConnection Dim qry As ODBCQuery Dim result As ODBCResultSet 定義ODBC對象類 Set qry = New ODBCQuery Set result = New ODBCResultSet If con.ConnectTo(”test”) Then Set qry.Connection = con Set result.Query = qry

qry.SQL = “SELECT * FROM table1″ Call result.Execute() Set dc = db.AllDocuments // 得到當前數據庫中所有文檔的文檔集 If dc.Count = 0 Then // 如果文檔集為空,則直接退出 result.Close(DB_CLOSE) con.Disconnect Exit Sub End If

For i = 1 To dc.Count // 循環, 直到所有文檔都被寫入關系庫 Set doc = dc.GetNthDocument(i) // 獲得第i條文檔 Call result.AddRow() // 在關系庫中增加一條記錄 Forall j In doc.Items Dim str_name As String Dim value As Variant str_name = j.name If (str_name < > “Form”) And (str_name < > “$UpdatedBy”) Then // Notes為每個文檔都增加了兩個特殊的 NotesItem對象用於標識系統信息 value = doc.GetItemValue(str_name) Call result.SetValue(str_name,value(0)) // 為關系庫中的字段賦值 End If End Forall result.UpdateRow // 更新關系庫 Next result.Close(DB_CLOSE) con.Disconnect Else Messagebox(”Could not connect server”) End If End Sub —- 二. Lotus Domino 中的日期處理 —- 在使用ODBC族對象類與關系數據庫進行交互的代碼里,有下面一段: [Copy to clipboard]CODE: If Isdate(value) Then If value = Datevalue(”0:00:00″) Then value = “” Else value = Format(value,”mm-dd-yyyy”) End If Set item = doc.AppendItemValue(field,value) End if

—- 這一段代碼的作用是:判斷從關系庫中讀取出來的字段是否為日期型字段,如果是,那么判斷此日期型字段的值,看它是否為空,如果為空則在寫入Notes庫的時候做處理,保證寫入的是空日期類型,如果此字段的值不為空則直接寫入Notes庫;如果此字段不是日期型字段,則不做任何處理。也許有人會問:為什么對於日期型字段要單獨做此番處理? —- 答案是:如果不做上述處理,那么通過ODBCResultSet類取出的日期型字段的值如果為空,則寫入Notes庫的時候會出現一個特殊日期:“1899年12月30日”。為什么會出現這個日期,目前尚不得而知。但是在Lotus Script中由於沒有單獨的Date數據類型,所以Notes的日期變量是以8個字節的浮點數的形式存在的,其整數部分表示公元某年某月某日,其小數部分表示小時分秒,從午夜開始計數。

—- 其日期類型可以表示的范圍從公元100年1月1日(-657434),到9999年12月31日(2958465)這一天,然后Notes會把9999年12月31日對應的整數日期以及所有超過上述日期范圍的日期都換算成1899年12月30日。Notes為什么會用這種方法處理日期型變量,以及為什么把1899年12月30日作為無法換算日期的默認值這一點目前還沒有比較好的解釋,但是作為一個數據庫轉換程序,應該保證轉換前后的兩個數據庫中內容的一致性,因此由於Notes本身沒有解決這個問題那么就只能由程序來解決。解決的思想是:如果取出來的日期型變量的值為空,那么就應該向Notes庫中寫入空日期,於是就有了上面的代碼。

—- 在代碼中,使用到了一個DateValue(string)函數,此函數的功能是將字符串參數轉換成日期數據類型,如果字符串所表示的內容找不到合法的日期數據與之對應,那么此函數將保留字符串的值不變,而僅僅把其數據類型轉換成日期型,即實現了強制類型轉換函數的功能。而代碼中的“0:00:00”字符串是個特殊字符串,在通過ODBC數據源讀取關系數據庫的記錄時,如果關系庫中的日期型字段值為空,那么取出的值就是“0:00:00”形式的日期(這也許與Microsot公司的ODBC驅動及相關程序有關,因為不只是Notes取出空日期為此值,其他軟件甚至包括微軟自己的MS Query組件通過ODBC讀取同一關系數據庫中的日期型變量時也會出現同樣的問題)。使用DateValue(“0:00:00”)語句就可以判斷取出的日期型變量的值是否為空,如果為空則將變量Value的值賦成空字符串,然后再寫入Notes庫中,則可避免出現1899年12月30日這個特殊日期。

—- 雖然Lotus Script中沒有提供專門的日期數據類型,但是Lotus卻提供了NotesDateTime對象類,其中包括了日期數據的相關處理,如果開發者必須要在程序中處理日期數據而又覺得Lotus Script提供的函數不夠的話,可以考慮使用NotesDateTime對象。

—- 三. Notes中視圖與表單的關聯

—- 在開發過程中,由於一次錯誤意外地發現了Notes中視圖與表單的關聯技巧。Notes為每個表單都設置了名稱與別名兩個屬性,這兩個名字都能標識表單。在開發過程中,所開發的庫有兩個表單,分別命名為Form1和Form2,其各自的別名由於疏忽都設成了Document,而每個表單都有一個視圖與之對應,在設計Form2的視圖時,雖然指定視圖中的列都與Form2中的域相關聯,但是在運行的時候從視圖中雙擊某條文檔欲對其進行編輯時,切換到的表單卻是Form1的,多方查找均不能解決這個問題,於是報着試試看的心理改變了表單的別名,分別為Document1和Document2,問題解決。但是又從出現Form1的視圖無法切換到表單的情況,於是又將Form1的別名改回Document,兩個視圖均能正確切換到自己的表單上。

—- 由此發現,Notes在建立視圖與表單的關聯的時候,首先看表單是否具有別名,如果有,則用別名進行關聯,一旦對表單的別名進行了改變就得重新設計視圖,如果不想重新做視圖就不能改變表單的別名。當然同時也可以應用這一技巧實現從視圖中切換到不同表單的功能。如果表單沒有別名,則使用名字進行關聯


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM