配置CDM作业字段映射
操作场景
- 作业参数配置完成后,将进行字段映射的配置,您可以通过字段映射界面的可自定义新增字段,也可单击操作列下创建字段转换器。
- 如果是文件类数据源(FTP/SFTP/HDFS/OBS)之间相互迁移数据,且源端“文件格式”配置为“二进制格式”(即不解析文件内容直接传输),则没有字段映射这一步骤。
- 其他场景下,CDM会自动匹配源端和目的端数据表字段,需用户检查字段映射关系和时间格式是否正确,例如:源字段类型是否可以转换为目的字段类型。
- 自动创表场景下,需在目的端表中提前手动新增字段,再在字段映射里新增字段。
约束限制
- 作业源端开启“使用SQL语句”参数时不支持配置转换器。
- 如果在字段映射界面,CDM通过获取样值的方式无法获得所有列(例如从HBase/CloudTable/MongoDB导出数据时,CDM有较大概率无法获得所有列),则可以单击后选择“添加新字段”来手动增加,确保导入到目的端的数据完整。
- 关系数据库、Hive、MRS Hudi及DLI做源端时,不支持获取样值功能。
- SQLServer作为目的端数据源时,不支持timestamp类型字段的写入,需修改为其他时间类型字段写入(如datetime)。
- 当作业源端为OBS、迁移CSV文件时,并且配置“解析首行为列名”参数的场景下显示列名。
- 当使用二进制格式进行文件到文件的迁移时,没有字段映射这一步。
- 自动创表场景下,需在目的端表中提前手动新增字段,再在字段映射里新增字段。
- 添加完字段后,新增的字段在界面不显示样值,不会影响字段值的传输,CDM会将字段值直接写入目的端。
- 如果字段映射关系不正确,您可以通过拖拽字段、单击对字段批量映射两种方式来调整字段映射关系。
- 如果是导入到数据仓库服务(DWS),则还需在目的字段中选择分布列,建议按如下顺序选取分布列:
- 有主键可以使用主键作为分布列。
- 多个数据段联合做主键的场景,建议设置所有主键作为分布列。
- 在没有主键的场景下,如果没有选择分布列,DWS会默认第一列作为分布列,可能会有数据倾斜风险。
- 如CDM不支持源端迁移字段类型,请参见不支持数据类型转换规避指导将字段类型转换为CDM支持的类型。
新增字段
- 常量
常量参数即参数值是固定的参数,不需要重新配置值。例如“lable”=“friends”用来标识常量值。
- 变量
您可以使用时间宏、表名宏、版本宏等变量来标记数据库来源信息。变量的语法:${variable},其中“variable”指的是变量。例如“input_time”=“${timestamp()}”用来标识当前时间的时间戳。
- 表达式
您可以使用表达式语言根据运行环境动态生成参数值。表达式的语法:#{expr},其中“expr”指的是表达式。例如“time”=“#{DateUtil.now()}”用来标识当前日期字符串。
新建转换器
CDM可以在迁移过程中对字段进行转换,目前支持以下字段转换器:
- 脱敏
隐藏字符串中的关键信息,例如要将“12345678910”转换为“123****8910”,则配置如下:
- “起始保留长度”为“3”。
- “结尾保留长度”为“4”。
- “替换字符”为“*”。
- 去前后空格
自动去字符串前后的空值,不需要配置参数。
- 字符串反转
自动反转字符串,例如将“ABC”转换为“CBA”,不需要配置参数。
- 字符串替换
替换字符串,需要用户配置被替换的对象,以及替换后的值。
- 去换行
将字段中的换行符(\n、\r、\r\n)删除。
- 表达式转换
数据进行转换过程中,替换内容包含特殊字符时,需要先使用\将该字符转义成普通字符。
- 表达式支持以下两个环境变量:
- value:当前字段值。
- row:当前行,数组类型。
- 表达式支持的工具类用法罗列如下,未列出即表示不支持:
- 如果当前字段为字符串类型,将字符串全部转换为小写,例如将“aBC”转换为“abc”。
表达式:StringUtils.lowerCase(value)
- 将当前字段的字符串全部转为大写。
- 如果想将第1个日期字段格式从“2018-01-05 15:15:05”转换为“20180105”。
表达式:DateUtils.format(DateUtils.parseDate(row[0],"yyyy-MM-dd HH:mm:ss"),"yyyyMMdd")
- 如果想将时间戳转换成“yyyy-MM-dd hh:mm:ss”格式的日期字符串的类型,例如字段值为“1701312046588”,转换后为“2023-11-30 10:40:46”。
表达式:DateUtils.format(NumberUtils.toLong(value),"yyyy-MM-dd HH:mm:ss")
- 如果想将“yyyy-MM-dd hh:mm:ss”格式的日期字符串转换成时间戳的类型。
表达式:DateUtils.getTime(DateUtils.parseDate(value,"yyyy-MM-dd hh:mm:ss"))
- 如果当前字段值为“yyyy-MM-dd”格式的日期字符串,需要截取年,例如字段值为“2017-12-01”,转换后为“2017”。
表达式:StringUtils.substringBefore(value,"-")
- 如果当前字段值为数值类型,转换后值为当前值的两倍。
- 如果当前字段值为“true”,转换后为“Y”,其它值则转换后为“N”。
表达式:value=="true"?"Y":"N"
- 如果当前字段值为字符串类型,当为空时,转换为“Default”,否则不转换。
表达式:empty value? "Default":value
- 如果想将日期字段格式从“2018/01/05 15:15:05”转换为“2018-01-05 15:15:05”。
表达式:DateUtils.format(DateUtils.parseDate(value,"yyyy/MM/dd HH:mm:ss"),"yyyy-MM-dd HH:mm:ss")
- 获取一个36位的UUID(Universally Unique Identifier,通用唯一识别码)。
- 如果当前字段值为字符串类型,将首字母转换为大写,例如将“cat”转换为“Cat”。
表达式:StringUtils.capitalize(value)
- 如果当前字段值为字符串类型,将首字母转换为小写,例如将“Cat”转换为“cat”。
表达式:StringUtils.uncapitalize(value)
- 如果当前字段值为字符串类型,使用空格填充为指定长度,并且将字符串居中,当字符串长度不小于指定长度时不转换,例如将“ab”转换为长度为4的“ab”。
表达式:StringUtils.center(value,4)
- 删除字符串末尾的一个换行符(包括“\n”、“\r”或者“\r\n”),例如将“abc\r\n\r\n”转换为“abc\r\n”。
表达式:StringUtils.chomp(value)
- 如果字符串中包含指定的字符串,则返回布尔值true,否则返回false。例如“abc”中包含“a”,则返回true。
表达式:StringUtils.contains(value,"a")
- 如果字符串中包含指定字符串的任一字符,则返回布尔值true,否则返回false。例如“zzabyycdxx”中包含“z”或“a”任意一个,则返回true。
表达式:StringUtils.containsAny(value,"za")
- 如果字符串中不包含指定的所有字符,则返回布尔值true,包含任意一个字符则返回false。例如“abz”中包含“xyz”里的任意一个字符,则返回false。
表达式:StringUtils.containsNone(value,"xyz")
- 如果当前字符串只包含指定字符串中的字符,则返回布尔值true,包含任意一个其它字符则返回false。例如“abab”只包含“abc”中的字符,则返回true。
表达式:StringUtils.containsOnly(value,"abc")
- 如果字符串为空或null,则转换为指定的字符串,否则不转换。例如将空字符转换为null。
- 如果字符串以指定的后缀结尾(包括大小写),则返回布尔值true,否则返回false。例如“abcdef”后缀不为null,则返回false。
表达式:StringUtils.endsWith(value,null)
- 如果字符串和指定的字符串完全一样(包括大小写),则返回布尔值true,否则返回false。例如比较字符串“abc”和“ABC”,则返回false。
表达式:StringUtils.equals(value,"ABC")
- 从字符串中获取指定字符串的第一个索引,没有则返回整数-1。例如从“aabaabaa”中获取“ab”的第一个索引1。
表达式:StringUtils.indexOf(value,"ab")
- 从字符串中获取指定字符串的最后一个索引,没有则返回整数-1。例如从“aFkyk”中获取“k”的最后一个索引4。
表达式:StringUtils.lastIndexOf(value,"k")
- 从字符串中指定的位置往后查找,获取指定字符串的第一个索引,没有则转换为“-1”。例如“aabaabaa”中索引3的后面,第一个“b”的索引是5。
表达式:StringUtils.indexOf(value,"b",3)
- 从字符串获取指定字符串中任一字符的第一个索引,没有则返回整数-1。例如从“zzabyycdxx”中获取“z”或“a”的第一个索引0。
表达式:StringUtils.indexOfAny(value,"za")
- 如果字符串仅包含Unicode字符,返回布尔值true,否则返回false。例如“ab2c”中包含非Unicode字符,返回false。
表达式:StringUtils.isAlpha(value)
- 如果字符串仅包含Unicode字符或数字,返回布尔值true,否则返回false。例如“ab2c”中仅包含Unicode字符和数字,返回true。
表达式:StringUtils.isAlphanumeric(value)
- 如果字符串仅包含Unicode字符、数字或空格,返回布尔值true,否则返回false。例如“ab2c”中仅包含Unicode字符和数字,返回true。
表达式:StringUtils.isAlphanumericSpace(value)
- 如果字符串仅包含Unicode字符或空格,返回布尔值true,否则返回false。例如“ab2c”中包含Unicode字符和数字,返回false。
表达式:StringUtils.isAlphaSpace(value)
- 如果字符串仅包含ASCII可打印字符,返回布尔值true,否则返回false。例如“!ab-c~”返回true。
表达式:StringUtils.isAsciiPrintable(value)
- 如果字符串为空或null,返回布尔值true,否则返回false。
- 如果字符串中仅包含Unicode数字,返回布尔值true,否则返回false。
- 获取字符串最左端的指定长度的字符,例如获取“abc”最左端的2位字符“ab”。
表达式:StringUtils.left(value,2)
- 获取字符串最右端的指定长度的字符,例如获取“abc”最右端的2位字符“bc”。
表达式:StringUtils.right(value,2)
- 将指定字符串拼接至当前字符串的左侧,需同时指定拼接后的字符串长度,如果当前字符串长度不小于指定长度,则不转换。例如将“yz”拼接到“bat”左侧,拼接后长度为8,则转换后为“yzyzybat”。
表达式:StringUtils.leftPad(value,8,"yz")
- 将指定字符串拼接至当前字符串的右侧,需同时指定拼接后的字符串长度,如果当前字符串长度不小于指定长度,则不转换。例如将“yz”拼接到“bat”右侧,拼接后长度为8,则转换后为“batyzyzy”。
表达式:StringUtils.rightPad(value,8,"yz")
- 如果当前字段为字符串类型,获取当前字符串的长度,如果该字符串为null,则返回0。
- 如果当前字段为字符串类型,删除其中所有的指定字符串,例如从“queued”中删除“ue”,转换后为“qd”。
表达式:StringUtils.remove(value,"ue")
- 如果当前字段为字符串类型,移除当前字段末尾指定的子字符串。指定的子字符串若不在当前字段的末尾,则不转换,例如移除当前字段“www.domain.com”后的“.com”。
表达式:StringUtils.removeEnd(value,".com")
- 如果当前字段为字符串类型,移除当前字段开头指定的子字符串。指定的子字符串若不在当前字段的开头,则不转换,例如移除当前字段“www.domain.com”前的“www.”。
表达式:StringUtils.removeStart(value,"www.")
- 如果当前字段为字符串类型,替换当前字段中所有的指定字符串,例如将“aba”中的“a”用“z”替换,转换后为“zbz”。
表达式:StringUtils.replace(value,"a","z")
替换内容包含特殊字符时,需要先把该字符转义成普通字符,例如,客户想通过该表达式把字符串中 \t 去掉时,需要配置为: StringUtils.replace(value,"\\t","")(即把 \ 再次转义)。
- 如果当前字段为字符串类型,一次替换字符串中的多个字符,例如将字符串“hello”中的“h”用“j”替换,“o”用“y”替换,转换后为“jelly”。
表达式:StringUtils.replaceChars(value,"ho","jy")
- 如果字符串以指定的前缀开头(区分大小写),则返回布尔值true,否则返回false,例如当前字符串“abcdef”以“abc”开头,则返回true。
表达式:StringUtils.startsWith(value,"abc")
- 如果当前字段为字符串类型,去除字段中首、尾处所有指定的字符,例如去除“abcyx”中首尾所有的“x”、“y”、“z”和“b”,转换后为“abc”。
表达式:StringUtils.strip(value,"xyzb")
- 如果当前字段为字符串类型,去除字段末尾所有指定的字符,例如去除当前字段末尾的“abc”字符串。
- 如果当前字段为字符串类型,去除字段开头所有指定的字符,例如去除当前字段开头的所有空格。
- 如果当前字段为字符串类型,获取字符串指定位置后(索引从0开始,包括指定位置的字符)的子字符串,指定位置如果为负数,则从末尾往前计算位置,末尾第一位为-1。例如获取“abcde”索引为2的字符(即c)及之后的字符串,则转换后为“cde”。
表达式:StringUtils.substring(value,2)
- 如果当前字段为字符串类型,获取字符串指定区间(索引从0开始,区间起点包括指定位置的字符,区间终点不包含指定位置的字符)的子字符串,区间位置如果为负数,则从末尾往前计算位置,末尾第一位为-1。例如获取“abcde”第2个字符(即c)及之后、第4个字符(即e)之前的字符串,则转换后为“cd”。
表达式:StringUtils.substring(value,2,4)
- 如果当前字段为字符串类型,获取当前字段里第一个指定字符后的子字符串。例如获取“abcba”中第一个“b”之后的子字符串,转换后为“cba”。
表达式:StringUtils.substringAfter(value,"b")
- 如果当前字段为字符串类型,获取当前字段里最后一个指定字符后的子字符串。例如获取“abcba”中最后一个“b”之后的子字符串,转换后为“a”。
表达式:StringUtils.substringAfterLast(value,"b")
- 如果当前字段为字符串类型,获取当前字段里第一个指定字符前的子字符串。例如获取“abcba”中第一个“b”之前的子字符串,转换后为“a”。
表达式:StringUtils.substringBefore(value,"b")
- 如果当前字段为字符串类型,获取当前字段里最后一个指定字符前的子字符串。例如获取“abcba”中最后一个“b”之前的子字符串,转换后为“abc”。
表达式:StringUtils.substringBeforeLast(value,"b")
- 如果当前字段为字符串类型,获取嵌套在指定字符串之间的子字符串,没有匹配的则返回null。例如获取“tagabctag”中“tag”之间的子字符串,转换后为“abc”。
表达式:StringUtils.substringBetween(value,"tag")
- 如果当前字段为字符串类型,删除当前字符串两端的控制字符(char≤32),例如删除字符串前后的空格。
- 将当前字符串转换为字节,如果转换失败,则返回0。
- 将当前字符串转换为字节,如果转换失败,则返回指定值,例如指定值配置为1。
- 将当前字符串转换为Double数值,如果转换失败,则返回0.0d。
- 将当前字符串转换为Double数值,如果转换失败,则返回指定值,例如指定值配置为1.1d。
- 将当前字符串转换为Float数值,如果转换失败,则返回0.0f。
- 将当前字符串转换为Float数值,如果转换失败,则返回指定值,例如配置指定值为1.1f。
- 将当前字符串转换为Int数值,如果转换失败,则返回0。
- 将当前字符串转换为Int数值,如果转换失败,则返回指定值,例如配置指定值为1。
- 将字符串转换为Long数值,如果转换失败,则返回0。
- 将当前字符串转换为Long数值,如果转换失败,则返回指定值,例如配置指定值为1L。
- 将字符串转换为Short数值,如果转换失败,则返回0。
- 将当前字符串转换为Short数值,如果转换失败,则返回指定值,例如配置指定值为1。
- 将当前IP字符串转换为Long数值,例如将“10.78.124.0”转换为Long数值是“172915712”。
表达式:CommonUtils.ipToLong(value)
- 从网络读取一个IP与物理地址映射文件,并存放到Map集合,这里的URL是IP与地址映射文件存放地址,例如“http://10.114.205.45:21203/sqoop/IpList.csv”。
表达式:HttpsUtils.downloadMap("url")
- 将IP与地址映射对象缓存起来并指定一个key值用于检索,例如“ipList”。
表达式:CommonUtils.setCache("ipList",HttpsUtils.downloadMap("url"))
- 取出缓存的IP与地址映射对象。
- 判断是否有IP与地址映射缓存。
- 根据IP取出对应的详细地址:国家_省份_城市_运营商,例如“1xx.78.124.0”对应的地址为“中国_广东_深圳_电信”,取不到对应地址则默认“**_**_**_**”。如果需要,可通过StringUtil类表达式对地址进行进一步拆分。
表达式:CommonUtils.getMapValue(CommonUtils.ipToLong(value),CommonUtils.cacheExists("ipLis")?CommonUtils.getCache("ipLis"):CommonUtils.setCache("ipLis",HttpsUtils.downloadMap("url")))
- 根据指定的偏移类型(month/day/hour/minute/second)及偏移量(正数表示增加,负数表示减少),将指定格式的时间转换为一个新时间,例如将“2019-05-21 12:00:00”增加8个小时。
表达式:DateUtils.getCurrentTimeByZone("yyyy-MM-dd HH:mm:ss",value, "hour", 8)
- 如果value值为空或者null时,则返回字符串“aaa”,否则返回value。
- 如果当前字段为字符串类型,将字符串全部转换为小写,例如将“aBC”转换为“abc”。
- 表达式支持以下两个环境变量:
特殊链路说明
- 当源端为DLI,目的端为DWS时,DLI的tinyint类型字段映射为DWS的smallint类型字段。
- 当源端为Hudi,目的端为DWS时,Hudi的Double类型字段映射为DWS的Float类型字段。