當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樣本。