用户定义的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