【DATAX自定义transformer】

业务场景

我们选取了DATAX作为数据抽取引擎,数据存储的目标库是apche doris数据库,业务需求是在将数据从源端库抽取到目标端时需要新增一个full_name字段,full_name是拼接了前面所有字段信息的大文本字段,用于全文检索。由于full_name字段建立了中文倒排索引,使用了DUPLICATE KEY数据模型,不支持数据行的修改,在综合考虑下决定在DATAX数据抽取任务过程中新增自定义transformer来实现。

实现原理

DATAX使用JAVA编写的,源码中提供了transformer模块,支持数据提取后数据加工转换能力,通过实现Transformer类来对行记录Record进行修改、新增等操作。

DATAX源码下载

DATAX源码地址,我这里直接使用的master分支。
在这里插入图片描述

DATAX源码编译

源码下载完成之后,需要对源码进行打包编译
在编译前我对根目录下pom文件的一些不需要使用的reader和writer模块进行了注释,减少编译时间。

    <modules>
        <module>common</module>
        <module>core</module>
        <module>transformer</module>

        <!-- reader -->
        <module>mysqlreader</module>
        <module>oraclereader</module>
        <module>dorisreader</module>
        <!-- writer -->
        <module>mysqlwriter</module>
        <module>oraclewriter</module>
        <module>doriswriter</module>
        <module>selectdbwriter</module>
        <module>adbmysqlwriter</module>
        <module>plugin-rdbms-util</module>
        <module>plugin-unstructured-storage-util</module>

        <module>datax-example</module>

    </modules>

执行编译命令: mvn clean package -DskipTests assembly:assembly
编译完成后在项目根目录下会有target包

自定义transformer

datax的启动入口在datax-core模块下,datax-core依赖了datx-transformer模块,我们在core模块或者transformer下自定义代码逻辑即可。
注意:内部加载的transformer注册名称必须以**dx_**开头。注册名在后面的json配置文件中使用。

package com.alibaba.datax.core.transport.transformer;

import com.alibaba.datax.common.element.Column;
import com.alibaba.datax.common.element.Record;
import com.alibaba.datax.common.element.StringColumn;
import com.alibaba.datax.transformer.Transformer;

/**
 * @projectName: DataX
 * @package: com.alibaba.datax.transformer.transformer
 * @className: ConcatTransformer
 * @author: chuanxilaomuji
 * @description: TODO
 * @date: 2024/10/22 9:57
 * @version: 1.0
 */
public class JskjConcatTransformer extends Transformer {

    public JskjConcatTransformer() {
        setTransformerName("dx_concat_transformer");
    }

    @Override
    public Record evaluate(Record record, Object... paras) {
        if (paras.length < 2) {
            throw new IllegalArgumentException("需要提供参与拼接的列和目标列名");
        }

        // 判断是否为 "concat_all",如果是,则拼接所有列
        String option = (String) paras[1];
        String separator = paras.length > 3 ? (String) paras[3] : "";  // 可选分隔符

        StringBuilder fullNameBuilder = new StringBuilder();

        // 如果传入的参数为 "concat_all",拼接所有列的内容
        if ("concat_all".equals(option)) {
            // 遍历所有列,将每个列的数据拼接起来
            int columnCount = record.getColumnNumber();
            for (int i = 0; i < columnCount; i++) {
                Column column = record.getColumn(i);
                if (column != null && column.asString() != null) {
                    if (fullNameBuilder.length() > 0) {
                        fullNameBuilder.append(separator);
                    }
                    fullNameBuilder.append(column.asString());
                }
            }
        } else {
            // 否则,按照指定的列拼接
            String[] columnsToConcatenate = option.split(",");
            for (String col : columnsToConcatenate) {
                int colIndex = Integer.parseInt(col.trim());
                Column column = record.getColumn(colIndex);
                if (column != null && column.asString() != null) {
                    if (fullNameBuilder.length() > 0) {
                        fullNameBuilder.append(separator);
                    }
                    fullNameBuilder.append(column.asString());
                }
            }
        }

        // 将拼接好的 full_name 列添加到 record 中
        record.addColumn(new StringColumn(fullNameBuilder.toString()));
        return record;
    }
}

自定义transformer类后,需要在注册器中注册。注册类位于core模块下core.transport.transformer包路径下的TransformerRegistry类中。

    static {
        /**
         * add native transformer
         * local storage and from server will be delay load.
         */

        registTransformer(new SubstrTransformer());
        registTransformer(new PadTransformer());
        registTransformer(new ReplaceTransformer());
        registTransformer(new FilterTransformer());
        registTransformer(new GroovyTransformer());
        registTransformer(new DigestTransformer());
        registTransformer(new JskjConcatTransformer());
    }

运行调试

注意:datax运行是要有python环境来执行bin目录下的.py文件的,我这里装的是python2.7.5版本,必须使用python2.6以上的版本,不要用python3。

找到core模块下的Engine类,修改main方法中的参数进行调试,这里配置的datax.home就是代码实际运行地址;本地调试standalone,jobid必须为-1,修改job_RW_YX_SJQX.json内容即可,注意datax.home配置到bin目录的上一级

 public static void main(String[] args) throws Exception {
        int exitCode = 0;
        try {
            System.setProperty("datax.home","E:\\DataX\\target\\datax\\datax");
            String[] datxArgs = {"-job", "E:\\DataX\\target\\datax\\datax\\job\\job_RW_YX_SJQX.json", "-mode", "standalone", "-jobid", "-1"};
            Engine.entry(datxArgs);
//            Engine.entry(args);
        } catch (Throwable e) {
            exitCode = 1;
            LOG.error("\n\n经DataX智能分析,该任务最可能的错误原因是:\n" + ExceptionTracker.trace(e));

            if (e instanceof DataXException) {
                DataXException tempException = (DataXException) e;
                ErrorCode errorCode = tempException.getErrorCode();
                if (errorCode instanceof FrameworkErrorCode) {
                    FrameworkErrorCode tempErrorCode = (FrameworkErrorCode) errorCode;
                    exitCode = tempErrorCode.toExitValue();
                }
            }

            System.exit(exitCode);
        }
        System.exit(exitCode);
    }

job_RW_YX_SJQX.json

{
	"job": {
		"setting": {
			"speed": {
				"channel": 1
			}
		},
		"content": [{
			"reader": {
				"name": "mysqlreader",
				"parameter": {
					"username": "username",
					"password": "password",
					"connection": [{
						"querySql": ["SELECT DWBM  , RYBM  , QXBM  FROM SJQX WHERE 1=1  "],
						"jdbcUrl": ["jdbc:dm://ip:port/database"]
					}]
				}
			},
			"transformer": [{
				"name": "dx_concat_transformer",
				"parameter": {
					"columnIndex": "1",
					"paras": ["concat_all", "full_name", ""]
				}
			}],
			"writer": {
				"name": "mysqlwriter",
				"parameter": {
					"username": "username",
					"password": "password",
					"column": ["DWBM", "RYBM", "QXBM", "full_name"],
					"connection": [{
						"jdbcUrl": "jdbc:mysql://ip:port/database",
						"table": ["SJQX"]
					}]
				}
			}
		}]
	}
}

注意:writer.parameter.column下也要新增full_name,DATAX的CommonRdbmsWriter类在构造insert语句时会校验Record的字段数和插入字段数是否一致,我刚刚在transformer添加了record,writer对应要新增一个字段。

if (record.getColumnNumber() != this.columnNumber) {

debug测试完成后引入项目

项目引入DATAX

我这边是将修改后源码打包通过本地引入的方式进行使用,推荐放入公司私服进行管理;这里jar包版本自定义修改的。
在这里插入图片描述

<dependency>
			<groupId>com.datax</groupId>
			<artifactId>datax-core</artifactId>
			<version>1.0.2</version>
			<scope>system</scope>
			<systemPath>${project.basedir}/src/lib/datax-core-1.0.2.jar</systemPath>
		</dependency>
		<dependency>
			<groupId>com.datax</groupId>
			<artifactId>datax-transformer</artifactId>
			<version>1.0.2</version>
			<scope>system</scope>
			<systemPath>${project.basedir}/src/lib/datax-transformer-1.0.2.jar</systemPath>
		</dependency>
		<dependency>
			<groupId>com.datax</groupId>
			<artifactId>datax-common</artifactId>
			<version>1.0.0</version>
			<scope>system</scope>
			<systemPath>${project.basedir}/src/lib/datax-common-1.0.0.jar</systemPath>
		</dependency>

然后在代码中构建代码逻辑,通过Engine.entry(args)来启动DATAX;对应的JSON文件也需要自己构造并自定目录,这里就不展示了。

            System.setProperty("datax.home", dataxMain);
            String[] args = {"-job", jsonFilePath, "-mode", "standalone", "-jobid", "-1"};
            Engine.entry(args);

ok,完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值