RowListener
目录
前言
本次目的是监控数据,然后能操作数据,同时继承TableInput,TableInputMeta和TableOutput,TableOutputMeta类,能实现其中主要的方法--processRow()。
首先来看监控数据,也就是BaseStep类中的addRowListener()方法,当然其中还要很多其他方法。
这个方法里面需要一个RowListener接口,里面有三个方法,如下所示,注解很清晰的说明了每个方法被调用的情况,此处我用的是RowAdapter类,实现了这个RowListener接口。
public interface RowListener {
/**
* This method is called when a row is read from another step
*
* @param rowMeta
* the metadata of the row
* @param row
* the data of the row
* @throws KettleStepException
* an exception that can be thrown to hard stop the step
*/
public void rowReadEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException;
/**
* This method is called when a row is written to another step (even if there is no next step)
*
* @param rowMeta
* the metadata of the row
* @param row
* the data of the row
* @throws KettleStepException
* an exception that can be thrown to hard stop the step
*/
public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException;
/**
* This method is called when the error handling of a row is writing a row to the error stream.
*
* @param rowMeta
* the metadata of the row
* @param row
* the data of the row
* @throws KettleStepException
* an exception that can be thrown to hard stop the step
*/
public void errorRowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException;
}
还是上篇博客那个mysql交换,首先为了能得到error数据,走errorRowWritetenEvent方法,将dest的表name从varchar改为int。
CREATE TABLE `user_info_dest` (
`id` varchar(36) NOT NULL,
`name` int(36) DEFAULT NULL
)
源表里面一条是数字,一条是英文,这样就有一条异常数据了,
同时我们用如下图的流程来改造一下mysql交换,完整代码贴在最后,
上图这个图片流程在博客中也有,链接在下面,
代码说明
如下图所示的结构,这些都是继承原来jar包中的类,
这里的重点是RowAdapter类,本来打算是修改processRow方法的,对其中异常进行一些包裹后处理,这样能让其他正常数据能走下去,但想了想其实也很简单,没啥意义。
TableInputRowAdapter
@Slf4j
public class TableInputRowAdapter extends RowAdapter {
private String adapterName;
public TableInputRowAdapter(String adapterName){
this.adapterName = adapterName;
}
@Override
public void rowReadEvent(RowMetaInterface rowMeta, Object[] row) throws KettleStepException {
/**
* org.pentaho.di.trans.step.BaseStep#getRow()
* org.pentaho.di.trans.step.BaseStep#handleGetRow() 此处调用 rowReadEvent
*/
// super.rowReadEvent(rowMeta, row);
PrintUtil.printlnData(adapterName+"-->rowReadEvent",rowMeta,row);
}
@Override
public void rowWrittenEvent(RowMetaInterface rowMeta, Object[] row) throws KettleStepException {
/*
org.pentaho.di.trans.step.BaseStep.putRow
org.pentaho.di.trans.step.BaseStep.handlePutRow 此处调用 rowWrittenEvent
*/
// super.rowWrittenEvent(rowMeta, row);
PrintUtil.printlnData(adapterName+"-->rowWrittenEvent",rowMeta,row);
}
@Override
public void errorRowWrittenEvent(RowMetaInterface rowMeta, Object[] row) throws KettleStepException {
//当发生错误时,将错误数据打印出来
PrintUtil.printlnData(adapterName+"-->errorRowWrittenEvent",rowMeta,row);
}
}
TableOutputRowAdapter
@Slf4j
public class TableOutputRowAdapter extends RowAdapter {
private String adapterName;
public TableOutputRowAdapter(String adapterName){
this.adapterName = adapterName;
}
@Override
public void rowReadEvent(RowMetaInterface rowMeta, Object[] row) throws KettleStepException {
/**
* org.pentaho.di.trans.step.BaseStep#getRow()
* org.pentaho.di.trans.step.BaseStep#handleGetRow() 此处调用 rowReadEvent
*/
// super.rowReadEvent(rowMeta, row);
PrintUtil.printlnData(adapterName+"-->rowReadEvent",rowMeta,row);
}
@Override
public void rowWrittenEvent(RowMetaInterface rowMeta, Object[] row) throws KettleStepException {
/*
org.pentaho.di.trans.step.BaseStep.putRow
org.pentaho.di.trans.step.BaseStep.handlePutRow 此处调用 rowWrittenEvent
*/
// super.rowWrittenEvent(rowMeta, row);
PrintUtil.printlnData(adapterName+"-->rowWrittenEvent",rowMeta,row);
}
@Override
public void errorRowWrittenEvent(RowMetaInterface rowMeta, Object[] row) throws KettleStepException {
/*
定义了错误处理时会进入此处,如下文章定义错误处理
https://blog.csdn.net/lw18751836671/article/details/121339655?spm=1001.2014.3001.5501
org.pentaho.di.trans.step.BaseStep.putError
org.pentaho.di.trans.step.BaseStep#handlePutError 此处调用 errorRowWrittenEvent
*/
//当发生错误时,将错误数据打印出来
PrintUtil.printlnData(adapterName+"-->errorRowWrittenEvent",rowMeta,row);
}
}
将RowAdapter放入监控队列中,如下所示,在构造函数处添加,
public OwnTableInput(StepMeta stepMeta, StepDataInterface stepDataInterface,
int copyNr, TransMeta transMeta, Trans trans, RowAdapter rowAdapter) {
super(stepMeta, stepDataInterface, copyNr, transMeta, trans);
//添加行监控
if (rowAdapter != null)
addRowListener(rowAdapter);
}
public OwnTableOutput(StepMeta stepMeta, StepDataInterface stepDataInterface,
int copyNr, TransMeta transMeta, Trans trans, RowAdapter rowAdapter) {
super(stepMeta, stepDataInterface, copyNr, transMeta, trans);
//添加行监控
if (rowAdapter != null)
addRowListener(rowAdapter);
}
当运行后日志打印如下,tableInput只走进了rowWritten,也就是说只进行了putRow方法,tableOutput有读数据和写数据,那么也就是调用了getRow(),putRow(),putError()方法。
其中putError方法是用来处理异常数据的,从日志打印看一条正常,一条异常,和我造的数据结果一样。
tableErrorOutput读取了异常的数据,然后将此异常数据写入到异常表。
2021/11/25 14:23:09 [INFO] tableInput-->rowWrittenEvent: id[VARCHAR]=002e7210219b49819ae5485a4d06e3c3,name[VARCHAR]=1
2021/11/25 14:23:09 [INFO] tableInput-->rowWrittenEvent: id[VARCHAR]=0092d0758f5e4cbb8a54d116e10ae5ed,name[VARCHAR]=bbb
2021/11/25 14:23:09 [INFO] tableOutput-->rowReadEvent: id[VARCHAR]=002e7210219b49819ae5485a4d06e3c3,name[VARCHAR]=1
2021/11/25 14:23:09 [INFO] tableOutput-->rowReadEvent: id[VARCHAR]=0092d0758f5e4cbb8a54d116e10ae5ed,name[VARCHAR]=bbb
2021/11/25 14:23:09 [INFO] tableOutput-->rowWrittenEvent: id[VARCHAR]=002e7210219b49819ae5485a4d06e3c3,name[VARCHAR]=1
2021/11/25 14:23:09 [INFO] tableOutput-->errorRowWrittenEvent: id[VARCHAR]=0092d0758f5e4cbb8a54d116e10ae5ed,name[VARCHAR]=bbb
2021/11/25 14:23:09 [INFO] tableErrorOutput-->rowReadEvent: id[VARCHAR]=0092d0758f5e4cbb8a54d116e10ae5ed,name[VARCHAR]=bbb
2021/11/25 14:23:09 [INFO] tableErrorOutput-->rowWrittenEvent: id[VARCHAR]=0092d0758f5e4cbb8a54d116e10ae5ed,name[VARCHAR]=bbb
其他代码段
public class OwnTableInputMeta extends TableInputMeta {
private RowAdapter rowAdapter;
@Override
public StepInterface getStep(StepMeta stepMeta, StepDataInterface stepDataInterface, int cnr,
TransMeta transMeta, Trans trans ) {
return new OwnTableInput( stepMeta, stepDataInterface, cnr, transMeta, trans,rowAdapter);
}
public void setRowAdapter(RowAdapter rowAdapter) {
this.rowAdapter = rowAdapter;
}
}
public class OwnTableOutputMeta extends TableOutputMeta {
private RowAdapter rowAdapter;
@Override
public StepInterface getStep(StepMeta stepMeta, StepDataInterface stepDataInterface, int cnr, TransMeta transMeta, Trans trans) {
return new OwnTableOutput(stepMeta, stepDataInterface, cnr, transMeta, trans,rowAdapter);
}
public void setRowAdapter(RowAdapter rowAdapter) {
this.rowAdapter = rowAdapter;
}
}
在交换代码中,使用自己继承的类,替换其中的TableInputMeta和TableOutputMeta,
OwnTableInputMeta tableInputMeta = new OwnTableInputMeta();
tableInputMeta.setRowAdapter(new TableInputRowAdapter("tableInput"));
OwnTableOutputMeta tableOutputMeta = new OwnTableOutputMeta();
tableOutputMeta.setRowAdapter(new TableOutputRowAdapter("tableOutput"));
OwnTableOutputMeta tableOutputMeta = new OwnTableOutputMeta();
tableOutputMeta.setRowAdapter(new TableOutputRowAdapter("tableErrorOutput"));
后记
本来打算再写一写关于kettle 8.2.0.0.342这个源码的,时间上来不及了,如果看源码的话这个入口可以是 org.pentaho.di.trans.Trans#execute,初略的看一下大概,后面断点调试比较好,可以从debug中看到堆栈信息。
关于kettle中博客的案例也上传gitee了,地址是下面的,当然里面代码不一定有用,因为我一开始用的是5.XXX 版本,后续改为了8.XXX 版本,所以改了版本后有些需要引入plugins中的包。
kettle简单案例: 使用kettle的简单案例https://gitee.com/king_software/simple-case-of-kettle
完整交换代码
@Slf4j
public class ExchangeWithExpandCode {
@Before
public void before() {
try {
// 初始化Kettle环境
KettleEnvironment.init();
EnvUtil.environmentInit();
} catch (KettleException e) {
e.printStackTrace();
}
}
@Test
public void exchange()throws KettleException{
TransMeta transMeta = new TransMeta();
transMeta.setName("交换");
PluginRegistry registry = PluginRegistry.getInstance();
StepMeta inputStep = getTableInputStep(transMeta,registry);
StepMeta outputStep = getTableOutputStep(transMeta,registry);
StepMeta errorOutputStep = getTableOutputErrorStep(transMeta,registry);
//错误数据处理
VariableSpace space = new Variables();
StepErrorMeta errorMeta = new StepErrorMeta(space,outputStep,errorOutputStep);
errorMeta.setEnabled(true);
outputStep.setStepErrorMeta(errorMeta);
errorOutputStep.setStepErrorMeta(errorMeta);
Trans trans = new Trans(transMeta);
transMeta.addTransHop(new TransHopMeta(inputStep, outputStep));
transMeta.addTransHop(new TransHopMeta(outputStep, errorOutputStep));
//执行转换
trans.execute(null);
//等待完成
trans.waitUntilFinished();
if (trans.getErrors() > 0) {
System.out.println("交换出错.");
return;
}
// StepMetaInterface tableOutputMeta = outputStep.getStepMetaInterface();
// tableOutputMeta.getStep(outputStep,tableOutputMeta.getStepData(),1,transMeta,trans).addRowListener();
}
/**
* 获取表输入
* @param transMeta
* @param registry
* @return
*/
public StepMeta getTableInputStep(TransMeta transMeta,PluginRegistry registry) throws KettleException{
/*
1. 源数据库连接
*/
String mysql_src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<connection>" +
"<name>src</name>" +
"<server>192.168.10.64</server>" +
"<type>MySQL</type>" +
"<access>Native</access>" +
"<database>test</database>" +
"<port>3306</port>" +
"<username>root</username>" +
"<password>root</password>" +
"</connection>";
DatabaseMeta srcDatabaseMeta = new DatabaseMeta(mysql_src);
transMeta.addDatabase(srcDatabaseMeta);
OwnTableInputMeta tableInputMeta = new OwnTableInputMeta();
tableInputMeta.setRowAdapter(new TableInputRowAdapter("tableInput"));
String tableInputPluginId = registry.getPluginId(StepPluginType.class,
tableInputMeta);
tableInputMeta.setDatabaseMeta(srcDatabaseMeta);
//设置查询条件
String selectSql = "select id ,name from user_info_src";
tableInputMeta.setSQL(selectSql);
StepMeta tableInputStep = new StepMeta(tableInputPluginId,
"tableInput", (StepMetaInterface) tableInputMeta);
//给步骤添加在spoon工具中的显示位置
tableInputStep.setDraw(true);
tableInputStep.setLocation(100, 100);
transMeta.addStep(tableInputStep);
return tableInputStep;
}
/**
* 表输出
* @param transMeta
* @param registry
* @return
* @throws KettleException
*/
public StepMeta getTableOutputStep(TransMeta transMeta,PluginRegistry registry) throws KettleException{
String mysql_dest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<connection>" +
"<name>dest</name>" +
"<server>192.168.10.64</server>" +
"<type>MySQL</type>" +
"<access>Native</access>" +
"<database>test</database>" +
"<port>3306</port>" +
"<username>root</username>" +
"<password>root</password>" +
"</connection>";
DatabaseMeta destDatabaseMeta = new DatabaseMeta(mysql_dest);
OwnTableOutputMeta tableOutputMeta = new OwnTableOutputMeta();
tableOutputMeta.setRowAdapter(new TableOutputRowAdapter("tableOutput"));
tableOutputMeta.setDatabaseMeta(destDatabaseMeta);
tableOutputMeta.setSchemaName(null);
tableOutputMeta.setTablename("user_info_dest");
String tableOutputPluginId = registry.getPluginId(StepPluginType.class, tableOutputMeta);
StepMeta tableOutputStep = new StepMeta(tableOutputPluginId, "tableOutput", (StepMetaInterface) tableOutputMeta);
//将步骤添加进去
transMeta.addStep(tableOutputStep);
//给步骤添加在spoon工具中的显示位置
tableOutputStep.setDraw(true);
tableOutputStep.setLocation(200, 200);
return tableOutputStep;
}
/**
* 表输出
* @param transMeta
* @param registry
* @return
* @throws KettleException
*/
public StepMeta getTableOutputErrorStep(TransMeta transMeta,PluginRegistry registry) throws KettleException{
String mysql_dest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<connection>" +
"<name>dest</name>" +
"<server>192.168.10.64</server>" +
"<type>MySQL</type>" +
"<access>Native</access>" +
"<database>test</database>" +
"<port>3306</port>" +
"<username>root</username>" +
"<password>root</password>" +
"</connection>";
DatabaseMeta destDatabaseMeta = new DatabaseMeta(mysql_dest);
OwnTableOutputMeta tableOutputMeta = new OwnTableOutputMeta();
tableOutputMeta.setRowAdapter(new TableOutputRowAdapter("tableErrorOutput"));
tableOutputMeta.setDatabaseMeta(destDatabaseMeta);
tableOutputMeta.setSchemaName(null);
tableOutputMeta.setTablename("user_info_dest_error");
String tableOutputPluginId = registry.getPluginId(StepPluginType.class, tableOutputMeta);
StepMeta tableOutputStep = new StepMeta(tableOutputPluginId, "tableErrorOutput", (StepMetaInterface) tableOutputMeta);
//将步骤添加进去
transMeta.addStep(tableOutputStep);
//给步骤添加在spoon工具中的显示位置
tableOutputStep.setDraw(true);
tableOutputStep.setLocation(300, 300);
return tableOutputStep;
}
}