Datax-HdfsWriter如何实现支持Map,List,Set等符合类型数据写入

本文介绍了如何将DataX升级到3.0版本,并针对Hadoop 2.7.3和Hive 2.3.2环境,实现对HDFS写入Map类型数据的支持。通过改造HdfsWriter,新增对Map数据类型的处理,包括在SupportHiveDataType中添加MAP类型,修改HdfsHelper以处理Map数据。测试结果显示,Map数据能成功写入Hive的Map字段,对于Text格式的数据,需要额外处理并指定字段分隔符。最后提供了测试案例及代码地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题背景

      之前分析hdfswriter时,看到过api支持map,list,set类型的数据写入,因此,想将其合入到datax里,使得源数据端有类似json,array等这种数据可以直接导入到hive中。

二. 环境准备

Datax版本:3.0

Hadoop版本:2.7.3

Hive版本:2.3.2

三. 源码

       hdfs写入时,最重要的是获取字段对应类型的ObjectInspector,因此直接从ObjectInspectorFactory类入手看:

ObjectInspectorFactory:

private static ObjectInspector getReflectionObjectInspectorNoCache(Type t, ObjectInspectorFactory.ObjectInspectorOptions options) {
...
if (t instanceof ParameterizedType) {
	ParameterizedType pt = (ParameterizedType)t;
	if (List.class.isAssignableFrom((Class)pt.getRawType()) || Set.class.isAssignableFrom((Class)pt.getRawType())) {
	    return getStandardListObjectInspector(getReflectionObjectInspector(pt.getActualTypeArguments()[0], options));
	}

	if (Map.class.isAssignableFrom((Class)pt.getRawType())) {
	    return getStandardMapObjectInspector(getReflectionObjectInspector(pt.getActualTypeArguments()[0], options), getReflectionObjectInspector(pt.getActualTypeArguments()[1], options));
	}

	t = pt.getRawType();
}
...
}

可以看出,如果类型是Map,List,Set类型的,实际获取的inspector都有对应的方法去获取。以下就拿其中的Map类型做例子来进行讲解:

ObjectInspectorFactory:

public static StandardMapObjectInspector getStandardMapObjectInspector(ObjectInspector mapKeyObjectInspector, ObjectInspector mapValueObjectInspector) {
    ArrayList<ObjectInspector> signature = new ArrayList(2);
    signature.add(mapKeyObjectInspector);
    signature.add(mapValueObjectInspector);
    StandardMapObjectInspector result = (StandardMapObjectInspector)cachedStandardMapObjectInspector.get(signature);
    if (result == null) {
        result = new StandardMapObjectInspector(mapKeyObjectInspector, mapValueObjectInspector);
        StandardMapObjectInspector prev = (StandardMapObjectInspector)cachedStandardMapObjectInspector.putIfAbsent(signature, result);
        if (prev != null) {
            result = prev;
        }
    }

    return result;
}

 从这个方法中,可以得知,要想获取map类型的inspector,需要知道对应的Key和Value的对应类型inspector,而hive中又不存在object这个类型,所以一般我们都是直接用string类型来接所有的map值,而且一般源数据读取的都是json格式的字符串类型,以下就以Key和Value都是String来对hdfswriter进行改造:

1、首先还是在SupportHiveDataType中新增一个MAP的类型

2、在HdfsHelper中修改2处地方,一个是源数据源的数据进行map类型转换,一个是获取Map对应的ObjectInspector

HdfsHelper:

public List<ObjectInspector>  getColumnTypeInspectors(List<Configuration> columns){
...
case MAP:
	ObjectInspector stringInspector = ObjectInspectorFactory.getReflectionObjectInspector(String.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
	objectInspector = ObjectInspectorFactory.getStandardMapObjectInspector(stringInspector, stringInspector);
break;
...
}

public static MutablePair<List<Object>, Boolean> transportOneRecord(
            Record record,List<Configuration> columnsConfiguration,
            TaskPluginCollector taskPluginCollector){
...
case MAP:
    // 获取Map
	recordList.add(JSON.parseObject(column.asString(), Map.class));
break;
...
}

四、测试结果

Mysql端数据:

写入ORC后查询结果:

但是对于Text类型的数据,还是需要进行额外的改动,具体改造思路可参考java往hive 的map类型字段写数据

public static MutablePair<List<Object>, Boolean> transportOneRecord(
            Record record,List<Configuration> columnsConfiguration,
            TaskPluginCollector taskPluginCollector,
            // 新增一个参数,区分text还是orc
            String fileType){
...
case MAP:
    Map map = JSON.parseObject(column.asString(), Map.class);
    if(fileType.equalsIgnoreCase("TEXT")){
        // text类型特殊处理
        recordList.add(map.toString().replaceAll("\\{|}|\\s",""));
    }else {
        recordList.add(map);
    }
break;
...
}

建表时,也需要指定一下集合的分隔符:

create table test_map(
    id      int,
    student map<string,string>
)
row format delimited
fields terminated by '|'
collection items terminated by ','
map keys terminated by '='
;

Datax指定字段分割符:

"writer": {
	"name": "hdfswriter",
	"parameter": {
	"defaultFS": "hdfs://node:9000",
	"fileType": "text",
	"path": "/usr/hive/warehouse/test.db/test_map/",
	"fileName": "test",
	"column": [
	  {
	    "name": "id",
	    "type": "int"
	  },
	  {
	    "name": "student",
	    "type": "map"
	  }
	],
	"writeMode": "append",
	"fieldDelimiter": "|"
}

 

最终执行结果:

 五、代码地址

github地址

DataX是一个开源的数据传输工具,用于实现在多种数据源之间高效、稳定地进行数据迁移。关于将MongoDB的List类型数据同步到Hive中,DataX本身并不直接支持List类型,但在处理时可以先将其转换为适合存储在关系型数据库如Hive的结构。 这里提供一个简化的示例代码,假设你已经有了MongoDB的集合`myCollection`,其中包含一个嵌套数组`listField`,并且你需要将这个数组拆分成多个单独的字段以便于Hive的表结构: ```java import com.alibaba.datax.core.Engine; import com.alibaba.datax.core.job.Job; import com.alibaba.datax.plugin.formatter.JsonFormat; import com.alibaba.datax.plugin.io.mongodb.MongodbSource; import com.alibaba.datax.plugin.io.hive.HiveSink; public class DataXExample { public static void main(String[] args) { Job job = new Job(); // MongoDB配置 MongodbSource source = new MongodbSource() .setDbName("your_mongodb_db") .setCollectionName("myCollection") .setJsonPath("$.listField"); // 获取listField字段 // 将每个元素视为一个文档,假设listField元素为JSON对象 JsonFormat format = new JsonFormat().setFieldDelimiter(","); source.setFormatter(format); // Hive配置,假设listField的元素需要拆分到tableSchema中的字段1, field2等 String tableSchema = "CREATE TABLE your_hive_table (field1 string, field2 string, ...)"; HiveSink sink = new HiveSink() .setTableName("your_hive_table") .setProject(tableSchema) .setIfNotExists(true); // 如果表不存在则创建 // 连接链路 job.addSource(source).addSink(sink); // 启动任务 try { Engine.execute(job); } catch (Exception e) { e.printStackTrace(); } } } ``` 注意这只是一个简化版本的示例,实际应用中可能需要根据数据的具体结构和需求进行调整,并且可能需要添加错误处理和配置文件支持。此外,运行DataX通常通过命令行或者配置文件启动,而不是直接在代码里。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值