天天看點

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

使用者定義的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.*

上面列出的導入僅是處理器代碼的一部分。它們不是您可以在其他“ 類代碼”頁籤中輸入的任何代碼塊的一部分。

如果需要在處理器代碼中添加其他導入,請在要為此步驟建立的代碼的最頂部包括它們,如以下示例所示:

Janino本質上是Java編譯器,僅支援Java 1.8.x規範的子集。要檢視功能和限制的完整清單,請參閱Janino首頁。

總體視圖

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

在轉換步驟名稱字段中輸入以下資訊。

步驟名稱:在畫布上指定轉換步驟的唯一名稱。預設情況下,步驟名稱設定為“使用者定義的Java類”。

使用“ 類代碼”面闆和選項頁籤輸入定義的Java類。指定Java類後,單擊“ 測試類”進行測試。

類代碼

kettle-如何在kettle中編寫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)

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

您可以在“ 類和代碼片段”面闆中浏覽定義的類以及相關的代碼片段和字段。您可以右鍵單擊此樹中的任何項目以删除,重命名或顯示示例。

類(Classes)

“ 類”檔案夾訓示“ 類代碼”面闆中哪些類具有相應的代碼塊頁籤。

代碼片段(Code Snippits)

代碼片段檔案夾示出了與使用者定義的Java類工序中的内部PDI代碼。這些摘錄顯示為您類代碼的參考。

輸入字段(Input Fields)

“ 輸入字段”檔案夾包含您在代碼中定義的所有輸入字段。在使用定義的代碼時,您将處理輸入和輸出字段。存在許多處理輸入字段的方法。例如,首先,檢查輸入行的以下描述:

所述inputRowMeta對象包含輸入行的中繼資料。它包括所有字段,它們的資料類型,長度,名稱,格式掩碼等。您可以使用此對象來查找輸入字段。例如,如果要查找名為customer的字段,則可以使用以下代碼:

因為如果需要對經過轉換的每一行都執行操作,則查找字段名稱可能會很慢,是以可以在第一段代碼中提前查找字段名稱,如以下示例所示:

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);
           

為了簡化此過程,可以使用以下形式的快捷方式:

該方法還考慮了上述基于索引的優化。

從前面的步驟中獲得的Java資料類型始終對應于PDI資料類型,如“ PDI資料行”頁面上所述。

Info Fields

該資訊字段檔案夾包含您在代碼中定義的任何資訊字段。在您的代碼中定義這些字段之前,它們不會出現在“ 類和代碼片段”面闆中。如果您的代碼中未定義任何資訊字段,則此檔案夾中将不顯示任何内容。

Output Fields

您可以在“ 字段”頁籤中定義要用作步驟輸出的所有新字段。在此頁籤中設定字段将自動計算輸出行中繼資料的布局,并将其存儲在data.outputRowMeta中,這使您可以建立輸出行。

如果該步驟寫入的行數等于讀取的行數,則可以調整在輸入時獲得的行的大小,如以下示例代碼所示:

或以下示例代碼中:

如果要複制行,請建立單獨的副本,以防止後續步驟一次多次修改同一Object []副本,如以下示例所示:

與通路輸入字段一樣,可以通過輸出行中的索引來尋址輸出字段,如以下示例所示:

或使用以下示例中顯示的快捷方式:

傳遞給後續步驟的Java資料類型始終需要與PDI資料類型相對應,如“ PDI資料行”頁面上所述。

選項(Options)

“使用者定義的Java類”步驟具有幾個頁籤。每個頁籤描述如下。

字段(Fields Tab)

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

“字段”表定義了要傳遞給轉換的後續步驟的輸出字段。表中指定的任何字段将顯示在“ 類和代碼片段”面闆的“輸出字段”檔案夾中。

參數(Parameters Tab)

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

您可以使用“參數”表來避免使用寫死的字元串值,例如字段名(例如customer)。

另一個示例在使用者定義的Java類中-計算Easter.ktr示例轉換的日期,該日期位于data-intregation / samples / transformations目錄中。該樣本KTR的參數稱為YEAR。YEAR參數由getParameter()方法引用,如以下示例所示:

在運作時,它将傳回年份字元串值。

類成員變量和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)

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

由于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)

kettle-如何在kettle中編寫java代碼使用者定義的Java類原文

您可以使用“目标步驟”表将使用者定義的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