當MaxCompute提供的內建函數無法支撐您的業務實現時, 您可以根據本文提供的開發流程,使用開發工具(例如IntelliJ IDEA(Maven)或MaxCompute Studio)自行編寫代碼邏輯創建自定義函數(UDF),并在MaxCompute中進行調用,以滿足多樣化業務需求。本文為您介紹如何通過Java語言編寫UDF。
使用限制
訪問外網
MaxCompute默認不支持通過自定義函數訪問外網。如果您需要通過自定義函數訪問外網,請根據業務情況填寫并提交網絡連接申請表單,MaxCompute技術支持團隊會及時聯系您完成網絡開通操作。表單填寫指導,請參見網絡開通流程。
訪問VPC網絡
MaxCompute默認不支持通過UDF訪問VPC網絡。如果您的UDF涉及訪問VPC網絡中的資源時,需要先創建MaxCompute與目標VPC網絡間的網絡連接,才可以直接通過UDF訪問VPC網絡中的資源,操作詳情請參見通過UDF訪問VPC網絡資源。
讀取表數據
目前版本不支持使用UDF/UDAF/UDTF讀取以下場景的表數據:
做過表結構修改(Schema Evolution)的表數據。
包含復雜數據類型的表數據。
包含JSON數據類型的表數據。
Transactional表的表數據。
注意事項
在編寫Java UDF前,您需要先了解UDF代碼結構,以及Java UDF使用的數據類型與MaxCompute支持的數據類型間的映射關系,二者之間的映射關系請參見附錄:數據類型。
在編寫Java UDF時,您需要注意:
不同UDF JAR包中不建議存在類名相同但實現邏輯不一樣的類。例如UDF1、UDF2分別對應資源JAR包udf1.jar、udf2.jar,兩個JAR包里都包含名稱為
com.aliyun.UserFunction.class
的類但實現邏輯不一樣,當同一條SQL語句中同時調用UDF1和UDF2時,MaxCompute會隨機加載其中一個類,此時會導致UDF執行結果不符合預期甚至編譯失敗。Java UDF中輸入或返回值的數據類型是對象,數據類型首字母必須大寫,例如String。
SQL中的NULL值通過Java中的NULL表示。Java Primitive Type無法表示SQL中的NULL值,不允許使用。
UDF開發流程
開發UDF時通常需進行準備工作、編寫UDF代碼、上傳并注冊UDF、調試UDF這幾個步驟。同時MaxCompute支持多種工具,以下以常見的MaxCompute Studio、DataWorks、odpscmd三種工具為例,以一個具體的示例為您介紹UDF開發的通用流程。
使用MaxCompute Studio
以下以開發一個字符小寫轉換功能的UDF為例,為您介紹使用MaxCompute Studio開發并調用Java UDF的操作步驟如下。
準備工作。
使用MaxCompute Studio開發調試UDF時,您需要先安裝MaxCompute Studio并連接MaxCompute項目,做好UDF開發前準備工作。操作詳情請參見:
編寫UDF代碼。
在Project區域,右鍵單擊Module的源碼目錄(即
),選擇 。在Create new MaxCompute java class對話框,單擊UDF并填寫Name后,按Enter鍵。
Name為創建的MaxCompute Java Class名稱。如果還沒有創建Package,在此處填寫packagename.classname,會自動生成Package。本示例創建的Java Class名稱為Lower。
在代碼編寫區域開始開發UDF代碼。UDF代碼示例如下。
package com.aliyun.odps.udf.example; import com.aliyun.odps.udf.UDF; public final class Lower extends UDF { public String evaluate(String s) { if (s == null) { return null; } return s.toLowerCase(); } }
說明如果需要本地調試Java UDF,請參見開發和調試UDF。
上傳并注冊UDF。
在UDF Java文件上單擊右鍵,選擇Deploy to server...,在Package a jar, submit resource and register function對話框中配置如下參數后,單擊OK。
MaxCompute project:UDF所在的MaxCompute項目名稱。由于UDF本身是在連接的MaxCompute項目下編寫的,此處保持默認值即可。
Resource file:UDF依賴的資源文件路徑。此處保持默認值即可。
Resource name:UDF依賴的資源。此處保持默認值即可。
Function name:注冊的函數名稱,即后續SQL中調用的UDF名稱。例如Lower_test。
調試UDF。
在左側導航欄單擊Project Explore,在目標MaxCompute項目上單擊右鍵,選擇Open Console并在Console區域輸入調用UDF的SQL語句,按Enter鍵運行即可。SQL語句示例如下。
select lower_test('ABC');
返回結果如下。
+-----+ | _c0 | +-----+ | abc | +-----+
使用DataWorks
準備工作。
使用DataWorks開發調試UDF時,您需要先開通DataWorks并綁定MaxCompute項目,做好UDF開發前準備工作。操作詳情請參見使用DataWorks連接。
編寫UDF代碼。
您可以在任意Java開發工具中開發UDF代碼并打包為一個JAR包。您可以使用以下UDF代碼示例。
package com.aliyun.odps.udf.example; import com.aliyun.odps.udf.UDF; public final class Lower extends UDF { public String evaluate(String s) { if (s == null) { return null; } return s.toLowerCase(); } }
上傳并注冊UDF。
您可以將已打包好的代碼包通過DataWorks上傳并完成UDF注冊,操作詳情請參見:
調試UDF。
注冊完成UDF后,您可以創建一個ODPS SQL節點,在節點中編寫并創建SQL命令來調試UDF。創建ODPS SQL節點的操作請參見創建ODPS SQL節點,調試命令示例如下。
select lower_test('ABC');
使用odpscmd
準備工作。
使用odpscmd開發調試UDF時,您需要先下載安裝odpscmd工具,并配置config文件連接MaxCompute項目,做好UDF開發前準備工作。操作詳情請參見使用客戶端(odpscmd)連接。
編寫UDF代碼。
您可以在任意Java開發工具中開發UDF代碼并打包為一個JAR包。您可以使用以下UDF代碼示例。
package com.aliyun.odps.udf.example; import com.aliyun.odps.udf.UDF; public final class Lower extends UDF { public String evaluate(String s) { if (s == null) { return null; } return s.toLowerCase(); } }
上傳并注冊UDF。
您可以將已打包好的代碼包通過odpscmd上傳并完成UDF注冊,操作詳情請參見:
調試UDF。
注冊完成UDF后,您可以編寫并創建SQL命令來調試UDF。調試命令示例如下。
select lower_test('ABC');
UDF開發完成后:UDF調用說明
按照上述UDF開發流程,完成Java UDF開發后,您即可在odpscmd中通過MaxCompute SQL調用Java UDF。調用方法如下:
在歸屬MaxCompute項目中使用自定義函數:使用方法與內建函數類似,您可以參照內建函數的使用方法使用自定義函數。
跨項目使用自定義函數:即在項目A中使用項目B的自定義函數,跨項目分享語句示例:
select B:udf_in_other_project(arg0, arg1) as res from table_t;
。更多跨項目分享信息,請參見基于Package跨項目訪問資源。
UDF示例demo
附錄:UDF代碼結構
您可以使用Java語言編寫UDF代碼,代碼中需要包含如下信息:
Java包(Package):可選。
您可以將定義的Java類打包,為后續查找和使用類提供方便。
繼承UDF類:必選。
必需攜帶的UDF類為
com.aliyun.odps.udf.UDF
。當您需要使用其他UDF類或者需要用到復雜數據類型時,請根據MaxCompute SDK添加需要的類。例如STRUCT數據類型對應的UDF類為com.aliyun.odps.data.Struct
。@Resolve
注解:可選。格式為
@Resolve(<signature>)
,signature
用于定義函數的輸入參數和返回值的數據類型。當您需要在UDF中使用STRUCT數據類型時,無法基于com.aliyun.odps.data.Struct
反射分析得到Field Name和Field Type,所以需要用@Resolve
注解來輔助獲取。即如果您需要在UDF中使用STRUCT,請在UDF Class中加上@Resolve
注解,注解只會影響參數或返回值中包含com.aliyun.odps.data.Struct的重載。例如@Resolve("struct<a:string>,string->string")
。詳細使用示例,請參見復雜數據類型示例。自定義Java類:必選。
UDF代碼的組織單位,定義了實現業務需求的變量及方法。
evaluate
方法:必選。非靜態的Public方法,位于自定義的Java類中。
evaluate
方法的輸入參數和返回值的數據類型將作為SQL語句中UDF的函數簽名Signature(定義UDF的輸入與輸出數據類型)。您可以在UDF中實現多個
evaluate
方法,在調用UDF時,MaxCompute會依據UDF調用的參數類型匹配正確的evaluate
方法。編寫Java UDF時可以使用Java Type或Java Writable Type,MaxCompute項目支持處理的數據類型與Java數據類型的詳細映射關系,請參見附錄:數據類型。
UDF初始化或結束代碼:可選。您可以通過
void setup(ExecutionContext ctx)
和void close()
分別實現UDF初始化和結束。void setup(ExecutionContext ctx)
方法會在evaluate
方法前調用且僅會調用一次,可以用來初始化一些計算所需要的資源或類的成員對象。void close()
方法會在evaluate
方法結束后調用,可以用來執行一些清理工作,例如關閉文件。
UDF代碼示例如下。
使用Java Type類型
//將定義的Java類組織在org.alidata.odps.udf.examples包中。 package org.alidata.odps.udf.examples; //繼承UDF類。 import com.aliyun.odps.udf.UDF; //自定義Java類。 public final class Lower extends UDF { //evaluate方法。其中:String標識輸入參數的數據類型,return標識返回值。 public String evaluate(String s) { if (s == null) { return null; } return s.toLowerCase(); } }
使用Java Writable Type類型
//將定義的Java類組織在com.aliyun.odps.udf.example包中。 package com.aliyun.odps.udf.example; //添加Java Writable Type類型必需的類。 import com.aliyun.odps.io.Text; //繼承UDF類。 import com.aliyun.odps.udf.UDF; //自定義Java類。 public class MyConcat extends UDF { private Text ret = new Text(); //evaluate方法。其中:Text標識輸入參數的數據類型,return標識返回值。 public Text evaluate(Text a, Text b) { if (a == null || b == null) { return null; } ret.clear(); ret.append(a.getBytes(), 0, a.getLength()); ret.append(b.getBytes(), 0, b.getLength()); return ret; } }
MaxCompute還支持直接使用在其兼容的Hive版本上開發的UDF,請參見Hive兼容數據類型。
附錄:數據類型
數據類型映射
為確保編寫Java UDF過程中使用的數據類型與MaxCompute支持的數據類型保持一致,您需要關注二者間的數據類型映射關系。具體映射關系如下。
在MaxCompute中不同數據類型版本支持的數據類型不同。從MaxCompute 2.0版本開始,擴展了更多的新數據類型,同時還支持ARRAY、MAP、STRUCT等復雜類型。更多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 |
DATE | java.sql.Date | DateWritable |
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 | 不涉及 |
Hive兼容數據類型
當MaxCompute項目采用2.0數據類型版本時,支持Hive風格的UDF,您可以直接使用在MaxCompute兼容的Hive版本上開發的Hive UDF。
MaxCompute兼容的Hive版本為2.1.0,對應Hadoop版本為2.7.2。如果UDF是在其他版本的Hive或Hadoop上開發的,您需要使用兼容的Hive或Hadoop版本重新編譯UDF JAR包。
在MaxCompute上使用Hive UDF的具體案例,請參見兼容Hive Java UDF示例。