本文为您介绍如何通过Java语言编写UDTF。

UDTF代码结构

您可以通过IntelliJ IDEA(Maven)或MaxCompute Studio工具使用Java语言编写UDTF代码,代码中需要包含如下信息:
  • Java包(Package):可选。

    您可以将定义的Java类打包,为后续查找和使用类提供方便。

  • 继承UDTF类:必选。

    必需携带的UDTF类为com.aliyun.odps.udf.UDTFcom.aliyun.odps.udf.annotation.Resolve(对应@Resolve注解)和com.aliyun.odps.udf.UDFException(对应实现Java类的方法)。当您需要使用其他UDTF类或者需要用到复杂数据类型时,请根据MaxCompute SDK添加需要的类。

  • 自定义Java类:必选。

    UDTF代码的组织单位,定义了实现业务需求的变量及方法。

  • @Resolve注解:必选。

    格式为@Resolve(<signature>)signature为函数签名,用于定义函数的输入参数和返回值的数据类型。UDTF无法通过反射分析获取函数签名,只能通过@Resolve注解方式获取函数签名,例如@Resolve("smallint->varchar(10)")。更多@Resolve注解信息,请参见@Resolve注解

  • 实现Java类的方法:必选。
    Java类实现包含如下4个方法,您可以根据实际需要进行选择。
    接口定义 描述
    public void setup(ExecutionContext ctx) throws UDFException 初始化方法,在UDTF处理输入的数据前,MaxCompute会调用用户自定义的初始化行为。在每个Worker内setup会被先调用一次。
    public void process(Object[] args) throws UDFException SQL中每一条记录都会对应调用一次processprocess的参数为SQL语句中指定的UDTF输入参数。输入参数以Object[]的形式传入,输出结果通过forward函数输出。您需要在process函数内自行调用forward,以决定输出数据。
    public void close() throws UDFException UDTF的结束方法。只会被调用一次,即在处理完最后一条记录之后被调用。
    public void forward(Object …o) throws UDFException 调用forward方法输出数据,每调用一次forward代表输出一条记录。在SQL查询语句中调用UDTF时,可以通过as子句将forward输出的结果进行重命名。

    编写Java UDTF时可以使用Java Type或Java Writable Type,MaxCompute项目支持处理的数据类型与Java数据类型的详细映射关系,请参见数据类型

UDTF代码示例如下。

//将定义的Java类组织在org.alidata.odps.udtf.examples包中。
package org.alidata.odps.udtf.examples;
//继承UDTF类。
import com.aliyun.odps.udf.UDTF;
import com.aliyun.odps.udf.UDTFCollector;
import com.aliyun.odps.udf.annotation.Resolve;
import com.aliyun.odps.udf.UDFException;
//自定义Java类。  
//@Resolve注解。
@Resolve("string,bigint->string,bigint")
public class MyUDTF extends UDTF {     
     //实现Java类的方法。
     @Override
     public void process(Object[] args) throws UDFException {
         String a = (String) args[0];
         Long b = (Long) args[1];
         for (String t: a.split("\\s+")) {
         forward(t, b);
       }
     }
   }

使用限制

  • 不支持通过自定义函数访问外网。如果您需要通过自定义函数访问外网,请根据业务情况填写并提交网络连接申请表单,MaxCompute技术支持团队会及时联系您完成网络开通操作。表单填写指导,请参见网络开通流程
  • select语句中使用UDTF时,不允许存在其他列或表达式。错误示例如下。
    --查询语句中同时携带了UDTF和其他列。
    select value, user_udtf(key) as mycol ...
  • UDTF不能嵌套使用。错误示例如下。
    --user_udtf1嵌套了user_udtf2,不允许嵌套。
    select user_udtf1(user_udtf2(key)) as mycol...;
  • 不支持在同一个select子句中与group bydistribute bysort by联用。错误示例如下。
    --UDTF不能与group by联用。
    select user_udtf(key) as mycol ... group by mycol;

注意事项

在编写Java UDTF时,您需要注意:
  • 不同UDTF JAR包中不建议存在类名相同但实现逻辑不一样的类。例如UDTF1、UDTF2分别对应资源JAR包udtf1.jar、udtf2.jar,两个JAR包里都包含名称为com.aliyun.UserFunction.class的类但实现逻辑不一样,当同一条SQL语句中同时调用UDTF1和UDTF2时,MaxCompute会随机加载其中一个类,此时会导致UDTF执行结果不符合预期甚至编译失败。
  • Java UDTF中输入或返回值的数据类型是对象,数据类型首字母必须大写,例如String。
  • SQL中的NULL值通过Java中的NULL表示。Java Primitive Type无法表示SQL中的NULL值,不允许使用。

@Resolve注解

@Resolve注解格式如下。
@Resolve(<signature>)
signature为函数签名字符串,用于标识输入参数和返回值的数据类型。执行UDTF时,UDTF函数的输入参数和返回值类型要与函数签名指定的类型一致。查询语义解析阶段会检查不符合函数签名定义的用法,检查到类型不匹配时会报错。具体格式如下。
'arg_type_list -> type_list'
其中:
  • type_list:表示返回值的数据类型。UDTF可以返回多列。支持的数据类型为:BIGINT、STRING、DOUBLE、BOOLEAN、DATETIME、DECIMAL、FLOAT、BINARY、DATE、DECIMAL(precision,scale)、复杂数据类型(ARRAY、MAP、STRUCT)或复杂数据类型嵌套。
  • arg_type_list:表示输入参数的数据类型。输入参数可以为多个,用英文逗号(,)分隔。支持的数据类型为BIGINT、STRING、DOUBLE、BOOLEAN、DATETIME、DECIMAL、FLOAT、BINARY、DATE、DECIMAL(precision,scale)、CHAR、VARCHAR、复杂数据类型(ARRAY、MAP、STRUCT)或复杂数据类型嵌套。
    arg_type_list还支持星号(*)或为空(''):
    • arg_type_list为星号(*)时,表示输入参数为任意个数。
    • arg_type_list为空('')时,表示无输入参数。

合法@Resolve注解示例如下。

@Resolve注解示例 说明
@Resolve('bigint,boolean->string,datetime') 输入参数类型为BIGINT、BOOLEAN,返回值类型为STRING、DATETIME。
@Resolve('*->string, datetime') 输入任意个参数,返回值类型为STRING、DATETIME。
@Resolve('->double, bigint, string') 无输入参数,返回值类型为DOUBLE、BIGINT、STRING。
@Resolve("array<string>,struct<a1:bigint,b1:string>,string->map<string,bigint>,struct<b1:bigint>") 输入参数类型为ARRAY、STRUCT、MAP,返回值类型为MAP、STRUCT。

数据类型

在MaxCompute中不同数据类型版本支持的数据类型不同。从MaxCompute 2.0版本开始,扩展了更多的新数据类型,同时还支持ARRAY、MAP、STRUCT等复杂类型。更多MaxCompute数据类型版本信息,请参见数据类型版本说明

为确保编写Java UDTF过程中使用的数据类型与MaxCompute支持的数据类型保持一致,您需要关注二者间的数据类型映射关系。具体映射关系如下。

MaxCompute Type Java Type Java Writable Type
TINYINT java.lang.Byte ByteWritable
SMALLINT java.lang.Short ShortWritable
INT java.lang.Integer IntWritable
BIGINT java.lang.Long LongWritable
FLOAT java.lang.Float FloatWritable
DOUBLE java.lang.Double DoubleWritable
DECIMAL java.math.BigDecimal BigDecimalWritable
BOOLEAN java.lang.Boolean BooleanWritable
STRING java.lang.String Text
VARCHAR com.aliyun.odps.data.Varchar VarcharWritable
BINARY com.aliyun.odps.data.Binary BytesWritable
DATETIME java.util.Date DatetimeWritable
TIMESTAMP java.sql.Timestamp TimestampWritable
INTERVAL_YEAR_MONTH 不涉及 IntervalYearMonthWritable
INTERVAL_DAY_TIME 不涉及 IntervalDayTimeWritable
ARRAY java.util.List 不涉及
MAP java.util.Map 不涉及
STRUCT com.aliyun.odps.data.Struct 不涉及
说明 当MaxCompute项目采用MaxCompute 2.0数据类型版本时,UDTF的输入或返回值才可以使用Java Writable Type。

使用说明

按照开发流程,完成Java UDTF开发后,您即可通过MaxCompute SQL调用Java UDTF。调用方法如下:
  • 在归属MaxCompute项目中使用自定义函数:使用方法与内建函数类似,您可以参照内建函数的使用方法使用自定义函数。
  • 跨项目使用自定义函数:即在项目A中使用项目B的自定义函数,跨项目分享语句示例:select B:udf_in_other_project(arg0, arg1) as res from table_t;。更多跨项目分享信息,请参见基于Package的跨项目空间资源访问

使用MaxCompute Studio完整开发及调用Java UDTF的操作,请参见使用示例

使用示例

以通过MaxCompute Studio开发UDTF为例,开发并调用Java UDTF的操作步骤如下:

  1. 在IntelliJ IDEA上完成如下准备工作:
    1. 安装MaxCompute Studio
    2. 创建MaxCompute项目连接
    3. 创建MaxCompute Java Module
  2. 编写UDTF代码。
    1. Project区域,右键单击Module的源码目录(即src > main > java),选择new > MaxCompute Java新建Java Class
    2. Create new MaxCompute java class对话框,单击UDTF并填写Name后,按Enter键。例如Java Class名称为MyUDTF。选择类型并填写名称

      Name为创建的MaxCompute Java Class名称。如果还没有创建Package,在此处填写packagename.classname,会自动生成Package。

    3. 在代码编写区域写入如下代码。编写UDTF代码UDTF代码示例如下。
      package org.alidata.odps.udtf.examples;
      import com.aliyun.odps.udf.UDTF;
      import com.aliyun.odps.udf.UDTFCollector;
      import com.aliyun.odps.udf.annotation.Resolve;
      import com.aliyun.odps.udf.UDFException;
      // TODO define input and output types, e.g., "string,string->string,bigint".
         @Resolve("string,bigint->string,bigint")
         public class MyUDTF extends UDTF {
           @Override
           public void process(Object[] args) throws UDFException {
             String a = (String) args[0];
             Long b = (Long) args[1];
             for (String t: a.split("\\s+")) {
               forward(t, b);
             }
           }
         }
  3. 在本地运行调试UDTF,确保代码可以运行成功。

    更多调试操作,请参见通过本地运行调试UDF

    本地调试UDTF
    说明 运行参数可参照图示数据填写。
  4. 将创建的UDTF打包为JAR包,上传至MaxCompute项目并注册函数。例如函数名称为user_udtf

    更多打包操作,请参见打包

    注册函数
  5. 在MaxCompute Studio的左侧导航栏,单击Project Explorer,在目标MaxCompute项目上单击右键,启动MaxCompute客户端,并执行SQL命令调用新创建的UDTF。
    假设待查询目标表my_table的数据结构如下。
    +------------+------------+
    | col0       | col1       |
    +------------+------------+
    | A B        | 1          |
    | C D        | 2          |
    +------------+------------+
    执行如下SQL命令调用UDTF。
    select user_udtf(col0, col1) as (c0, c1) from my_table;
    返回结果如下。
    +----+------------+
    | c0 | c1         |
    +----+------------+
    | A  | 1          |
    | B  | 1          |
    | C  | 2          |
    | D  | 2          |
    +----+------------+