IBM Message Broker筆記系列(九)

CloudSpace發表於2008-09-11

這篇是純粹的“coding心得”,撇開MB那些囉嗦的配置不談,專門講學習ESQL的痛苦經歷,有些內容可能前面的筆記有介紹過,這裡做一個全面的彙總。雖然有些程式設計的tips已經忘記了,以後如果想起來還會繼續補充。

概述

ESQL的語法和資料庫的儲存過程語句很像,雖然我從未寫過儲存過程,但是平心而論,ESQL的基本語法和概念還是很好理解的,畢竟,ESQL沒有類、物件、多型這些OOP的東西,也沒有指標、位移操作這些C的概念;沒有C的函式指標、指標的指標、記憶體分配這種讓新手頭暈的術語,也不像Java那樣各類框架滿天飛,開足馬力都學不過來。所以,ESQL還是很好入門的。但是,切記,只是“入門”而已。你看懂那些示例的ESQL很容易,無非是邏輯樹的增刪改;訊息流也是那麼一目瞭然,訊息從一個節點出來,進入另一個節點,不知不覺一個“業務流”就完成了,so simple,naïve!我一開始也是這麼覺得的,但真正動手的時候,才發覺ESQL程式碼中,危機四伏!下面一一列舉

基本型別

數字

ESQL的基本型別很少,無非是數字、字串、邏輯、時間,還有引用。數字型別包括int、float和decimal(就是double,高精度小數),一般很少會考慮三者的差別,把它們與java的等同起來,其實不然,如果隨便亂用,會冒出很多無聊而又浪費時間的bug

  • 資料庫查詢

在使用oracle的時候,通常都會用Number型別作為主鍵id等數字型別欄位,可是你知道用select語句取Number到ESQL中是什麼嗎?是decimal。由於ESQL裡面,訊息樹中的欄位型別是隱式的、可變的(類似PHP),也就是你可以隨便賦任何值給某個訊息節點。按理說這種指令碼語言的特性可以方便程式設計,是好事。不過請先看完下面的描述。

  • 資料庫插入

這個問題是最近才發現的,在64位的linux上,MB使用64位資料來源訪問oracle,在一條insert語句上屢屢失敗,而這條insert語句之前在32位windows平臺上卻很正常的執行。丟擲的異常提示:“oracle:String data, right truncated”,在網上搜了一下大部分人都說是資料太長,只有dw論壇上有人說可能是64bit資料來源的關係,但具體原因也不清楚。請了IBM的支援來搞了半天也沒任何結果,絕望之際我乾脆用排除法,每次修改一兩個欄位為很簡單的常數(那樣總不會出問題了),在排除到最後一個欄位時,才發現把一個decimal的資料插進去會有問題,如果換成float就ok了!這個問題前後浪費了我兩天,當時忍不住說了幾句髒話,一個簡單的問題搞得這麼噁心,錯誤提示也純粹誤導使用者。天知道以後換成其他平臺會不會又這樣呢?

  • 函式呼叫

ESQL是弱型別的?是的,某種程度上是弱型別的,可是遇到函式呼叫的時候,它的型別強度簡直勝過java——你不能把一個int作為引數傳遞給宣告為double的引數,那樣會丟擲異常。問題是有時候你根本不知道某個訊息樹節點啥時變成了int或者是float或是double,就像上文說到的資料庫查詢結果。請開啟debug,慢慢找吧,總會有柳暗花明的一刻

字串型別

估計MB為了照顧比較“原始”的訊息格式,即非XML、而是CWF或者TDS格式的訊息,ESQL的字串型別不僅僅有字元型別“char”,還有位元組型別“BLOB”和位元型別“BIT”,的確是大大方便了位元流的處理。關於字串,發現的問題不多,下面列出幾點需要稍加註意之處。

  • 字串連線“||”

使用“||”連線字串時,記得每個被連線的變數都不能為null,否則連線後的結果就變成null了,可以用“變數 is not null”來判斷

  • BLOB型別

BLOB型別在ESQL中的表示方式為 X'ABCD',實際上是一種BCD碼,每個字元表示一個十六進位制,即半個位元組。注意這裡要是偶數個長度,否則在打包或者部署時會報錯

時間型別

和一般程式語言不同的是ESQL有時間型別的變數,這很大程度上是因為MB裡面支援很多XML的特性,而時間型別也是標準XML中包含的,例如xsd:dateTime等等。我沒學過高階的XML,自然對這些名目繁多的時間型別之間的差別不甚瞭解,所以簡單地列出幾點比較常用的特性:

  • 訊息定義中的時間型別

訊息定義檔案裡頭也可以宣告某個節點型別為timestamp(訊息定義檔案本質上就是XML SCHEMA檔案,自然支援xsd:名稱空間),不過在實踐中發現從MQ讀入的一個XML字串,解析為timestamp型別時,經常有一些格式上的問題而導致解析失敗,後來乾脆把timestamp全部替換為string了,IBM的技術支援也是這麼“推薦”的,估計是他們也找不出原因所在

  • 計算花費的時間

JAVA裡面很常用的就是通過計算系統時間之差,來得到一段程式碼的執行時間毫秒數,然後用時間類可以進行格式轉換,ESQL同樣可以做到,而且更加簡單。比如要計算一個訊息流的執行時間,那麼在訊息流開頭用 CURRENT_TIME得到起始時間,儲存在環境樹中的T1節點;然後在訊息流結尾,再次用 CURRENT_TIME得到結束時間T2,兩個時間值相減,再用下面這段程式碼將其轉換成毫秒數

SET OutputRoot.MRM.process_time = CAST( (T2-Environment.Variables.T1) SECOND AS FLOAT) *1000;

很遺憾的,ESQL最小隻支援“秒”的時間間隔(“時間間隔”INTERVAL也是一種時間型別),不過得到的float值通常是小數位很長的,包含了毫秒資訊,譬如0.2352436,因此乘以1000也完全夠用

全域性變數

問過IBM的人好幾次,自己也去查了不少資料,一直沒有發現ESQL中有足夠理想的全域性變數或者全域性常量。我們知道,ESQL的程式碼層次從高到低依次是:schema->module->function or procedure,越是區域性的變數,優先順序越高,這一點和普通程式語言一樣。所以,沒有變數的宣告可以超越schema這一級,包括所謂的外部變數external,因此,在不同schema的訊息流之間不能共享全域性變數的,這個限制有時候很麻煩,比如所有訊息流都要訪問同一個資料來源、或者Oracle的schema,或者是你自己定義的全域性常量,那你就只能在每個schema中設定了,還好schema不會很多,或者你在訊息流開始的時候,從檔案、資料庫等地方讀取配置引數也是一種選擇。

對於定義好的全域性變數,可以用{ }進行變數值的替換,從而實現動態功能,比如 OutputBody.{myvar}.value,花括號的值會在執行期間指定。但是這樣一來ESQL的編輯器就無法判斷這個節點是否存在,會給出“無法解析該引用”的警告,這不影響使用。

全域性函式

剛才說到的在schema作用域中定義的全域性變數,在其他schema不能引用。但是在schema中定義的全域性函式或者過程,則可以在其他schema中引用,只要在定義schema時使用PATH將其他schema匯入即可,或者在呼叫函式時指定完整路徑,如 CALL com.xxx.GLOBAL_FUNCTION。很多人一開始會對全域性函式寄予厚望,因為可以減少程式碼的重複,增加複用度,實則符合聖人們的教誨。只可惜呢,全域性函式中不能使用通常在節點中的Root、ExceptionList、Environment等邏輯樹,所以我在定義全域性函式時,第一個引數通常都是REFERENCE型別,用來把Environment等邏輯樹傳進去。

陣列、ROW和LIST

ESQL裡面有陣列型別,但你不能DECLARE一個陣列變數;有ROW型別,同時有個ROW函式專門用來產生一個ROW變數,還有個LIST函式用來產生陣列。

先談談陣列,陣列是什麼大家都知道了,在ESQL裡面,由於不能宣告一個陣列,所以我習慣把陣列儲存在Environment或者LocalEnvironment中。因為訊息樹的每個節點都可以往下增加子結點,所以每個訊息樹的中間節點其實都是陣列,我們說 set LocalEnvironment.Variables.temp = xxx ,實際上就是 set LocalEnvironment.Variables.temp[1] = xxx

至於ROW型別,《精通WMB》上說是個XML單行資料,執行這條ESQL語句:set LocalEnvironment.root = ROW('aa' AS a, 'bb' AS b),(注意root旁邊沒有方括號[ ])生成的XML片段如下:

aa

bb

然而,ROW其實是對應與資料庫的一行資料,僅此而已。上面的root節點相當於一行,a和b則是該行資料的欄位,就這麼簡單。所以,執行select語句時,返回的就是一個ROW的陣列

LIST函式是產生陣列用的,例如:set LocalEnvironment.Var.root[ ] =LIST( 'aa', 'bb'),(root旁邊這次有方括號[])產生如下XML:


aa
bb

詳細內容,請參考老陳的《精通WMB》後面的附錄P321(我也是最近才無意中翻到ROW和LIST)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14789789/viewspace-444228/,如需轉載,請註明出處,否則將追究法律責任。

相關文章