用戶定義的Java類
您可以使用“用戶定義的Java類”步驟輸入自己的Java類,以驅動完整步驟的功能。您可以將自己的插件編程爲一個步驟,但是此步驟的目標不是在一個步驟中進行全面的Java開發。可以使用一個完整的插件系統來幫助完成該部分(請參閱嵌入和擴展PDI功能)。您的目標是隻定義Java方法和邏輯。對於此步驟,Janino項目庫用於在運行時以類的形式編譯Java代碼。
非100%Java
Janino和此步驟不需要完整的Java類。它僅需要類主體(例如導入,構造函數和方法)。該步驟不需要完整的類聲明。在整個類的定義中,使用此方法設計了此步驟,以隱藏技術細節和方法以易於使用。
您將主代碼輸入到Processor中,它定義了processRow()方法。在PDI中,以下導入已經是處理器代碼的一部分:
- org.pentaho.di.trans.steps.userdefinedjavaclass.*
- org.pentaho.di.trans.step.*
- org.pentaho.di.core.row.*
- org.pentaho.di.core.*
- org.pentaho.di.core.exception.*
上面列出的導入僅是處理器代碼的一部分。它們不是您可以在其他“ 類代碼”選項卡中輸入的任何代碼塊的一部分。
如果需要在處理器代碼中添加其他導入,請在要爲此步驟創建的代碼的最頂部包括它們,如以下示例所示:
import java.util.*;
Janino本質上是Java編譯器,僅支持Java 1.8.x規範的子集。要查看功能和限制的完整列表,請參閱Janino主頁。
總體視圖
在轉換步驟名稱字段中輸入以下信息。
步驟名稱:在畫布上指定轉換步驟的唯一名稱。默認情況下,步驟名稱設置爲“用戶定義的Java類”。
使用“ 類代碼”面板和選項選項卡輸入定義的Java類。指定Java類後,單擊“ 測試類”進行測試。
類代碼
直接在“類代碼”面板的“ 處理器”選項卡中添加定義的Java代碼。可以通過右鍵單擊並選擇Add new來爲更多代碼塊創建其他選項卡。此菜單還包括用於複製選項卡,設置轉換類或刪除類類型的選項。
處理行(Process Rows)
所述處理器代碼定義processRow()方法,這是步驟的心臟。此方法在緊密循環中由轉換調用,並將一直持續到返回false爲止。
必須在第一個get(Fieds.in,FIELD_NAME)之前調用getRow()方法,這有助於避免在上一步獲得的數據(例如Mapping輸入規範)中出現意外字段排序的情況。
以下示例處理器代碼塊中顯示了一個非常簡單的示例,該示例計算firstname +“” + lastname並將其存儲到nameField中:
String firstnameField;
String lastnameField;
String nameField;
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
{
// Let's look up parameters only once for performance reason.
//
if (first) {
firstnameField = getParameter("FIRSTNAME_FIELD");
lastnameField = getParameter("LASTNAME_FIELD");
nameField = getParameter("NAME_FIELD");
first=false;
}
// First, get a row from the default input hop
//
Object[] r = getRow();
// If the row object is null, we are done processing.
//
if (r == null) {
setOutputDone();
return false;
}
// It is always safest to call createOutputRow() to ensure that your output row's Object[] is large
// enough to handle any new fields you are creating in this step.
//
Object[] outputRow = createOutputRow(r, data.outputRowMeta.size());
String firstname = get(Fields.In, firstnameField).getString(r);
String lastname = get(Fields.In, lastnameField).getString(r);
// Set the value in the output field
//
String name = firstname+" "+lastname;
get(Fields.Out, nameField).setValue(outputRow, name);
// putRow will send the row on to the default output hop.
//
putRow(data.outputRowMeta, outputRow);
return true;
}
錯誤處理(Error Handling)
如果希望PDI處理在轉換中運行類時可能發生的錯誤,則必須實現自己的錯誤處理代碼。在添加任何錯誤處理代碼之前,右鍵單擊PDI客戶端畫布中的“用戶定義的Java類”步驟,然後在出現的菜單中選擇“ 錯誤處理 ”。出現的“ 步驟錯誤處理設置”對話框包含用於指定錯誤目標步驟的選項以及將用於在定義的代碼中實現錯誤處理的關聯字段名稱。
data-intregation / samples / transformations目錄中的“ 用戶定義的Java類– Lambda Examples.ktr”中的以下try代碼塊包含此類錯誤處理的示例:
try {
Object numList = strsList.stream()
.map( new ToInteger() )
.sorted( new ReverseCase() )
.collect( Collectors.toList() );
get( Fields.Out, "reverseOrder" ).setValue( row, numList.toString() );
} catch (NumberFormatException ex) {
// Number List contains a value that cannot be converteds to an Integer.
rowInError = true;
errMsg = ex.getMessage();
errCnt = errCnt + 1;
}
if ( !rowInError ) {
putRow( data.outputRowMeta, row );
} else {
// Output errors to the error hop. Right click on step and choose "Error Handling..."
putError(data.outputRowMeta, row, errCnt, errMsg, "Not allowed", "DEC_0");
}
上面的代碼示例中的try測試以查看numList是否包含有效數字。如果列表包含無效數字,則使用putError處理錯誤並將其定向到樣本轉換中的wlog:ErrorPath步驟。還可以在“用戶定義Java類”步驟的“ 目標步驟”選項卡中指定ErrorPath 步驟。
日誌(Logging)
如果希望PDI記錄類中的數據操作(例如讀取,寫入,輸出或更新數據),則需要在定義的步驟中實現記錄。以下代碼是如何實現日誌記錄的示例:
putRow( data.outputMeta, r );
if ( checkFeedback( getLinesOutput() ) ) {
if ( log.isBasic() ) {
logBasic( "Have I got rows for you! " + getLinesOutput() );
}
}
類和代碼片段(Class and Code Fragments)
您可以在“ 類和代碼片段”面板中瀏覽定義的類以及相關的代碼片段和字段。您可以右鍵單擊此樹中的任何項目以刪除,重命名或顯示示例。
類(Classes)
“ 類”文件夾指示“ 類代碼”面板中哪些類具有相應的代碼塊選項卡。
代碼片段(Code Snippits)
代碼片段文件夾示出了與用戶定義的Java類工序中的內部PDI代碼。這些摘錄顯示爲您類代碼的參考。
輸入字段(Input Fields)
“ 輸入字段”文件夾包含您在代碼中定義的所有輸入字段。在使用定義的代碼時,您將處理輸入和輸出字段。存在許多處理輸入字段的方法。例如,首先,檢查輸入行的以下描述:
RowMetaInterface inputRowMeta = getInputRowMeta();
所述inputRowMeta對象包含輸入行的元數據。它包括所有字段,它們的數據類型,長度,名稱,格式掩碼等。您可以使用此對象來查找輸入字段。例如,如果要查找名爲customer的字段,則可以使用以下代碼:
ValueMetaInterface customer = inputRowMeta.searchValueMeta("year");
因爲如果需要對經過轉換的每一行都執行操作,則查找字段名稱可能會很慢,因此可以在第一段代碼中提前查找字段名稱,如以下示例所示:
if (first) {
yearIndex = getInputRowMeta().indexOfValue(getParameter("YEAR"));
if (yearIndex<0) {
throw new KettleException("Year field not found in the input row, check parameter 'YEAR'\!");
}
}
要獲得year
字段的整數值,你可以使用下面的結構:
Object[] r = getRow();
...
Long year = inputRowMeta().getInteger(r, yearIndex);
爲了簡化此過程,可以使用以下形式的快捷方式:
Long year = get(Fields.In, "year").getInteger(r);
該方法還考慮了上述基於索引的優化。
從前面的步驟中獲得的Java數據類型始終對應於PDI數據類型,如“ PDI數據行”頁面上所述。
Info Fields
該信息字段文件夾包含您在代碼中定義的任何信息字段。在您的代碼中定義這些字段之前,它們不會出現在“ 類和代碼片段”面板中。如果您的代碼中未定義任何信息字段,則此文件夾中將不顯示任何內容。
Output Fields
您可以在“ 字段”選項卡中定義要用作步驟輸出的所有新字段。在此選項卡中設置字段將自動計算輸出行元數據的佈局,並將其存儲在data.outputRowMeta中,這使您可以創建輸出行。
如果該步驟寫入的行數等於讀取的行數,則可以調整在輸入時獲得的行的大小,如以下示例代碼所示:
Object[] outputRowData = RowDataUtil.resizeArray(r, data.outputRowMeta.size());
或以下示例代碼中:
Object[] outputRowData = createOutputRow(r, data.outputRowMeta.size());
如果要複製行,請創建單獨的副本,以防止後續步驟一次多次修改同一Object []副本,如以下示例所示:
Object[] outputRowData = RowDataUtil.createResizedCopy(r, data.outputRowMeta.size());
與訪問輸入字段一樣,可以通過輸出行中的索引來尋址輸出字段,如以下示例所示:
outputRowData[getInputRowMeta().size()] = easterDate(year.intValue());
或使用以下示例中顯示的快捷方式:
get(Fields.Out, "easter").setValue(r, easterDate(year.intValue());
傳遞給後續步驟的Java數據類型始終需要與PDI數據類型相對應,如“ PDI數據行”頁面上所述。
選項(Options)
“用戶定義的Java類”步驟具有幾個選項卡。每個選項卡描述如下。
字段(Fields Tab)
“字段”表定義了要傳遞給轉換的後續步驟的輸出字段。表中指定的任何字段將顯示在“ 類和代碼片段”面板的“輸出字段”文件夾中。
參數(Parameters Tab)
您可以使用“參數”表來避免使用硬編碼的字符串值,例如字段名(例如customer)。
另一個示例在用戶定義的Java類中-計算Easter.ktr示例轉換的日期,該日期位於data-intregation / samples / transformations目錄中。該樣本KTR的參數稱爲YEAR。YEAR參數由getParameter()方法引用,如以下示例所示:
getParameter("YEAR")
在運行時,它將返回年份字符串值。
類成員變量和getVariable函數(Class Member Variables and the getVariable Function)
當獲取指向轉換參數的參數時,“用戶定義的Java類”步驟的行爲會有所不同,具體取決於何時調用getVariable函數。如果在init()方法中,則參數的行爲符合預期。如果初始化是對類成員變量進行的,則該變量不會被設計解析,如以下示例所示:
private final String par = getVariable("somePar"); // DOES NOT resolve correctly
private String par2 = null;
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
logBasic("Parameter value="+par+"\[MEMBER INIT\]");
logBasic("Parameter value="+par2+"\[INIT FUNCTION\]");
setOutputDone();
return false;
}
public boolean init(StepMetaInterface stepMetaInterface, StepDataInterface stepDataInterface) {
par2 = getVariable("somePar"); // WORKS FINE
return parent.initImpl(stepMetaInterface, stepDataInterface);
}
消息步驟(Info Steps Tab)
由於GetRow()方法從任何輸入流(輸入流或信息流)返回第一行,所以當rowMeta輸入和rowMeta信息不同時,將使用“信息”步驟表。
在調用getRow()方法之前,從信息流中讀取或獲取所有數據值,如以下代碼示例所示:
if (first){
first = false;
/* TODO: Your code here. (Using info fields)
FieldHelper infoField = get(Fields.Info, "info_field_name");
RowSet infoStream = findInfoRowSet("info_stream_tag");
Object[] infoRow = null;
int infoRowCount = 0;
// Read all rows from info step before calling getRow() method, which returns first row from any
// input rowset. As rowMeta for info and input steps varies getRow() can lead to errors.
while((infoRow = getRowFrom(infoStream)) != null){
// do something with info data
infoRowCount++;
}
*/
}
Object[] r = getRow();
if (r == null) {
setOutputDone();
return false;
}
目標步驟(Target Steps Tab)
您可以使用“目標步驟”表將用戶定義的Java類的輸出定向到轉換中的特定步驟。
示例
data-integration/samples/transformations目錄包含下面的示例KTRs展示瞭如何使用此步驟:
示例KTR文件 | 描述 |
---|---|
用戶定義的Java類 - Calculate the date of Easter.ktr | 開發一個類來計算復活節的日期。 |
用戶定義的Java類 - Concatenate firstname and lastname.ktr | 開發一個類,將名字和姓氏組合成一個全名。 |
用戶定義的Java類 - Query User Defined Java Classdatabase catalog.ktr | 顯示用戶定義的類如何訪問數據庫。 |
用戶定義的Java類 - Real-time search on Twitter.ktr | 顯示如何在實時系統中使用用戶定義的類。 |
用戶定義的Java類 - LambdaExamples.ktr | 顯示如何在用戶定義的Java類步驟中使用Java流API。 |
我們建議從用戶定義的Java類開始-計算Easter.ktr示例轉換的日期。
元數據注入支持
此步驟的所有字段都支持元數據注入。您可以將此步驟與ETL元數據注入 一起使用,以在運行時將元數據傳遞給您的轉換。
元數據注入支持
此步驟的所有字段都支持元數據注入。您可以將此步驟與ETL元數據注入 一起使用,以在運行時將元數據傳遞給您的轉換。
原文
https://help.pentaho.com/Documentation/8.2/Products/Data_Integration/Transformation_Step_Reference/User_Defined_Java_Class