Kettle變數和自訂java代碼的執行個體應用,kettlejava
1 kettle.properties參數配置資料來源串連和FTP串連
由於測試環境和生產環境中資料庫連接FTP等配置會在部署過程中變更,所以預先定義成配置項,在設定檔中修改,這樣測試和發布將會變得簡單,下面以資料庫為例說明這類配置的使用。
(1) 首先要找到設定檔,不同的作業系統路徑也不一樣,本人用win7進行開發,設定檔的路徑為“C:\Users\chenpeng\.kettle\kettle.properties”,如下:
(2) 設定檔中的具體配置如下:
還可以可視化設定:
(3) 具體使用樣本
l 下方是資料庫連接配置:
l 下方是FTP串連配置:
1.1.2 kettle.properties參數設定和路徑及與正則配合使用
(1)在kettle.properties中設定變數值
(2)kettle.properties設定如下(win7下的路徑為:C:\Users\chenpeng\.kettle)
(4) 在輸出檔案時使用參數中指定的路徑:
註:如果路徑出現錯誤,如把某個檔案夾刪除則會報錯,只要是報錯,即使把檔案夾建好了也仍然會報目錄錯誤(好像有記憶功能),這時必須重新啟動kettle才能正常運行。
l 在輸出檔案時使用參數中和Regex混合使用情境:
1.1.3 kettle.properties參數在java代碼中的應用
1.1.4 作業中變數使用並用javascript設定變數值
上面的例子都是kettle.properties中聲明的變數的應用,這些都是全域範圍內通用的,但很多時間,子作業需要有內部專用的變數參數,這時就不能使用kettle.properties中聲明的變數了,需要在流程中聲明變數,並把範圍設定為當前作業有效。以下應用情境業務如下:檔案名稱要命名成當前日期格式的,所以在作業層級定義了一個變數,但無法給它賦值,如是採用了javascript指令碼方式給該變數賦值,然後在輸出檔案名的位置應用該變數即可,後面檔案的刪除上傳都是公用部分都需要用到這個變數做為介面參數來做處理。
(1) 主流程如下:
(2) 定義變數:
(3) 子過程中調用javascript指令碼修改值:
Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; } var dateTime = new Date().Format("yyyyMMdd"); // gives back today at yyyy/MM/dd HH:mm:ss.000setVariable("curdate",dateTime,"s");
另外,如果是用上一節點的欄位值,修改變數值則更為簡單,如下:
(4) 使用變數(按常規使用):
1.1.5 Java代碼訪問變數調用jar包並產生驗證檔案
這是一個較為綜合性的樣本,首先定義了一條記錄(可能理解為很多個變數),然後通過java代碼來調用jar包,計算出記錄數、檔案大小、MD5值等,賦值給相應記錄欄位,並輸出到檔案,形成資料檔案的校正檔案。
(1) 主流程如下:
(2) 產生記錄
(3) Java處理
//匯入在eclipse中編輯好的包,主要用於計算檔案行數、MD5值import cgb.tools.KettleHelper;import java.io.File;import java.io.IOException; //kettle中已定義好的行處理方法,每行記錄都會執行一次public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException{//(1)擷取到上一個步驟的輸入行Object[] r = getRow();if (r == null) { setOutputDone(); return false;}r = createOutputRow(r, data.outputRowMeta.size());//(2)讀取出參數變數值String kettleoutputdir = getVariable("kettleoutputdir", "");String hbprovince_code = getVariable("hbprovince_code", "");String item_code = getVariable("item_code", "");String curdate = getVariable("curdate", "");String filename = "";String onlyfilename = "";String recordcount = "";String bytecount = "";String md5code = "";//(3)調用jar包計算出MD5值、行數、位元組數try {filename = kettleoutputdir + "\\" + hbprovince_code + "_" + item_code + "_day_" + curdate + ".csv";onlyfilename = hbprovince_code + "_" + item_code + "_day_" + curdate + ".csv";md5code = KettleHelper.getFileMD5String(filename);recordcount = String.valueOf(KettleHelper.getFileRecordCount(filename, true));bytecount = String.valueOf(KettleHelper.getFileByteCount(filename));} catch (IOException e) {e.printStackTrace();}//(4)把計算好的值放入到輸出記錄中get(Fields.Out, "filename").setValue(r, onlyfilename);get(Fields.Out, "recordcount").setValue(r, recordcount);get(Fields.Out, "byteCount").setValue(r, bytecount);get(Fields.Out, "md5code").setValue(r, md5code);//(5)輸出到下一個節點做處理 putRow(data.outputRowMeta, r);return true;}
(4) Jar包的開發和匯出
其中調用的jar包,可以選擇用eclipse來產生,如下:
package XXX.tools;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;import java.io.InputStream;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException; public class KettleHelper { /** * 預設的密碼字串組合,用來將位元組轉換成 16 進位表示的字元,apache校正下載的檔案的正確性用的就是預設的這個組合 */ private static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private static MessageDigest messagedigest = null; static { try { messagedigest = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException nsaex) { System.err.println("MD5Util.class.getName()" + "初始化失敗,MessageDigest不支援MD5Util。"); nsaex.printStackTrace(); } } /** * 產生字串的md5校正值 * * @param s * @return */ private static String getMD5String(String s) { return getMD5String(s.getBytes()); } /** * 判斷字串的md5校正碼是否與一個已知的md5碼相匹配 * * @param password 要校正的字串 * @param md5PwdStr 已知的md5校正碼 * @return */ public static boolean checkPassword(String password, String md5PwdStr) { String s = getMD5String(password); return s.equals(md5PwdStr); } /** * 組建檔案的md5校正值 * * @param file * @return * @throws IOException */ public static String getFileMD5String(String fileName) throws IOException { File file = new File(fileName); InputStream fis; fis = new FileInputStream(file); byte[] buffer = new byte[1024]; int numRead = 0; while ((numRead = fis.read(buffer)) > 0) { messagedigest.update(buffer, 0, numRead); } fis.close(); return bufferToHex(messagedigest.digest()); } /** * 擷取檔案的記錄條數 * * @param file * @return * @throws IOException */ public static int getFileRecordCount(String fileName,boolean hasHeadRow) throws IOException { File inFile = new File(fileName); // 讀取的CSV檔案 @SuppressWarnings("unused")String inString = ""; int count = 0; try { BufferedReader reader = new BufferedReader(new FileReader(inFile)); while((inString = reader.readLine())!= null){ count++; } reader.close(); } catch (FileNotFoundException ex) { System.out.println("沒找到檔案!"); } catch (IOException ex) { System.out.println("讀寫檔案出錯!"); } if(hasHeadRow) { count--; } return count; } /** * 組建檔案的md5校正值 * * @param file * @return * @throws IOException */ public static String getFileMD5String(File file) throws IOException { InputStream fis; fis = new FileInputStream(file); byte[] buffer = new byte[1024]; int numRead = 0; while ((numRead = fis.read(buffer)) > 0) { messagedigest.update(buffer, 0, numRead); } fis.close(); return bufferToHex(messagedigest.digest()); } private static String getMD5String(byte[] bytes) { messagedigest.update(bytes); return bufferToHex(messagedigest.digest()); } private static String bufferToHex(byte bytes[]) { return bufferToHex(bytes, 0, bytes.length); } private static String bufferToHex(byte bytes[], int m, int n) { StringBuffer stringbuffer = new StringBuffer(2 * n); int k = m + n; for (int l = m; l < k; l++) { appendHexPair(bytes[l], stringbuffer); } return stringbuffer.toString(); } private static void appendHexPair(byte bt, StringBuffer stringbuffer) { char c0 = hexDigits[(bt & 0xf0) >> 4];// 取位元組中高 4 位的數字轉換, >>> 為邏輯右移,將符號位一起右移,此處未發現兩種符號有何不同 char c1 = hexDigits[bt & 0xf];// 取位元組中低 4 位的數字轉換 stringbuffer.append(c0); stringbuffer.append(c1); } /** * 擷取文個把的位元組數 * @param fileName * @return * @throws IOException */ public static long getFileByteCount(String fileName) throws IOException { File file = new File(fileName); return file.length(); } }
匯出jar包
放入到kettle的jar包目錄,它會自載入,作業系統不同,目錄也會不同,本人使用的是win7,目錄如下:
(5) 檔案輸出
(6) 最終組建檔案的效果如下:
1.1.6 SQL中使用變數
下面的查詢語句用問號預留位置,當開始日期(第一個?號)和結束日期(第二個?號)綁定到SQL的問號預留位置,在查詢入職日期在一定期間的總統資訊:
SELECTname,took_office FROMpresidents WHEREtook_officeBETWEEN? AND?
樣本中,首先使用產生行步驟(“Generdate Rows”)產生一行帶有兩個欄位的記錄,分別按順序代替表輸入SQL語句中的預留位置。實際情境中,通常使用動態處理結果產生期望值代替產生行步驟。
接下來是表輸入步驟,其中配置SQL查詢語句,包含問號預留位置,通過在“Insert Data Step”的下拉框中選擇前一步驟,來替換問號的值。
通過傳輸不同的值多次執行查詢
如果你想迴圈執行查詢,使用不同值替換預留位置;就需要佔位符生產步驟產生多行資料,並把表輸入的選項“Execute for each row”選中。本樣本檔案名稱為placeholders_in_loop.ktr。
預留位置的局限性
雖然通過給預留位置綁定值查詢非常有效,但也有一些情境不能使用,下面一些SQL不能使用預留位置。這些樣本都非常通用,但是不能使用預留位置。
不能用預留位置代替表名詞,否則查詢將不執行。
SELECT some_fieldFROM ?
不能使用預留位置代替查詢的欄位名稱,下面的查詢可以成功綁定參數,但只是作為一個常量,而不是欄位的名稱。
SELECT ? asmy_field FROM table
不能使用預留位置綁定逗號分隔的多個清單項目值;如果你綁定“1,2,3″給下面的查詢語句,將得到意外的結果。
SELECT * FROM testWHERE id IN(?)
你期望得到的結果是:
SELECT * FROM testWHERE id IN("1,2,3")
但是啟動並執行結果卻是這樣,傳輸一個字串,卻得到三個值,而實際情況完全不確定有幾個值傳輸進來。
SELECT * FROM testWHERE id IN(1,2,3)
為瞭解決這些情境的問題,需要使用kettle的變數動態構造查詢文本,下面詳細說明。
SQL查詢中使用kettle變數
表輸入步驟支援替換查詢中的變數或參數,假設有一系列結構完全相關的表,分別是: mammals, birds, insects(動物、鳥、昆蟲),可以使用kettle變數作為表的名稱。假設我們有一個變數,名稱為:ANIMALS_TABLE,賦值為birds,我們設定“Replace Vaiables”選項選中。如果我們寫下面的查詢:
SELECT name,population FROM${ANIMALS_TABLE}
在執行一定被成功的替換成:
SELECT name,population FROM birds
如果設定變數的值為“mammals”或“insects”,則將動態查詢不同的表。當預留位置不能勝任是,使用變數技術可以協助我們解決。樣本的名稱為variables.ktr,運行時不要忘了給parameter(具名引數)賦值進行測試。
變數和預留位置一起使用
如果有必要,我們可以混合這兩種技術;本樣本中使用變數作為表名詞,同時使用預留位置作為前面步驟的輸入值。樣本檔案variables_and_placeholders.ktr。
SELECT name, population FROM${ANIMALS_TABLE}WHERE population > ?
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。