MaxCompute memperkenalkan user-defined types (UDT) berdasarkan mesin SQL generasi baru. UDT memungkinkan Anda mereferensikan kelas atau objek dari bahasa pemrograman pihak ketiga dalam pernyataan SQL untuk memanggil metode atau mengambil data.
Kapan menggunakan UDT dibandingkan UDF
Baik UDT maupun user-defined functions (UDF) memperluas MaxCompute SQL dengan logika kustom. Pilih pendekatan berdasarkan alur kerja Anda:
| Situasi | Pendekatan yang direkomendasikan |
|---|---|
Memanggil metode kelas Java bawaan secara langsung (misalnya, Integer.MAX_VALUE) | UDT — tidak memerlukan definisi fungsi |
| Menggunakan kembali pustaka pihak ketiga secara langsung dalam ekspresi SQL | UDT — mereferensikan kelas secara inline tanpa pembungkus |
| Menyertakan objek bahasa sumber terkompilasi di berbagai tahap pekerjaan | UDT — secara otomatis mengenkapsulasi status JVM lintas tahap |
| Menerapkan logika bisnis yang dapat digunakan ulang di beberapa proyek | UDF — pendaftaran fungsi eksplisit memungkinkan penggunaan bersama |
Kasus penggunaan
Memanggil metode pustaka standar Java tanpa mendefinisikan fungsi. Ketika suatu tugas memerlukan metode kelas Java bawaan yang tidak diekspos secara native oleh MaxCompute SQL, UDT memungkinkan Anda memanggilnya langsung dalam ekspresi.
Mereferensikan pustaka pihak ketiga secara inline. Alih-alih membungkus fungsi pustaka pihak ketiga di dalam UDF, referensikan kelas tersebut langsung dalam pernyataan SQL.
Menyematkan kode sumber terkompilasi dalam SQL. Untuk bahasa seperti Java yang memerlukan kompilasi, UDT memungkinkan Anda mereferensikan objek dan kelas dalam ekspresi SQL tanpa langkah pendaftaran terpisah. Lihat SELECT TRANSFORM untuk alternatif berbasis skrip.
Prasyarat
Sebelum menggunakan UDT, pastikan bahwa:
JDK 1.8 tersedia di lingkungan Anda. Versi yang lebih baru dari JDK 1.8 mungkin tidak didukung.
Tipe data baru diaktifkan jika Anda menggunakan tipe seperti INT:
set odps.sql.type.system.odps2=true;
Cara kerja
Berbeda dengan UDT pada mesin SQL lainnya (yang biasanya mendefinisikan alias tipe mirip tipe STRUCT), UDT MaxCompute bekerja seperti pernyataan CREATE TYPE — mencakup bidang dan metode, serta Anda mereferensikannya langsung dalam SQL tanpa menulis DDL.
Contoh berikut menggambarkan perbedaannya. Untuk mengakses Integer.MAX_VALUE dari paket java.lang Java:
Menggunakan UDT (referensi langsung):
-- Aktifkan tipe data baru (diperlukan untuk tipe seperti INTEGER).
set odps.sql.type.system.odps2=true;
SELECT java.lang.Integer.MAX_VALUE;Karena java.lang diimpor otomatis (seperti di Java), ini setara dengan:
set odps.sql.type.system.odps2=true;
SELECT Integer.MAX_VALUE;Hasil:
+-----------+
| max_value |
+-----------+
| 2147483647 |
+-----------+Menggunakan UDF (untuk perbandingan):
Tulis kelas UDF:
package com.aliyun.odps.test; public class IntegerMaxValue extends com.aliyun.odps.udf.UDF { public Integer evaluate() { return Integer.MAX_VALUE; } }Kompilasi, unggah, dan daftarkan:
add jar odps-test.jar; create function integer_max_value as 'com.aliyun.odps.test.IntegerMaxValue' using 'odps-test.jar';Panggil:
select integer_max_value();
UDT menyederhanakan proses ini menjadi satu pernyataan SQL.
Eksekusi multi-tahap
Objek UDT mengalir secara alami melalui tahap MapReduce. Contoh berikut melakukan join dua kolom BigInteger yang dihitung dari sumber data berbeda:
-- Data contoh.
@table1 := select * from values ('100000000000000000000') as t(x);
@table2 := select * from values (100L) as t(y);
-- Buat objek dengan metode baru.
@a := select new java.math.BigInteger(x) x from @table1;
-- Panggil metode statis.
@b := select java.math.BigInteger.valueOf(y) y from @table2;
-- Panggil metode instans melalui join.
select /*+mapjoin(b)*/ x.add(y).toString() from @a a join @b b;
-- Output:
100000000000000000100Pekerjaan ini berjalan melalui tiga tahap (M1, R2, J3). new java.math.BigInteger(x) dijalankan di M1; java.math.BigInteger.valueOf(y) dan x.add(y).toString() dijalankan di J3 pada proses dan mesin fisik yang berbeda. UDT mengenkapsulasi hal ini sehingga semua tahap berperilaku seolah-olah berjalan pada Java Virtual Machine (JVM) yang sama.

Kolom x dari variabel a bertipe java.math.BigInteger, bukan tipe bawaan. Nilai UDT ini dapat diteruskan ke operator lain dan digunakan dalam pengacakan data.
Mereferensikan paket JAR dan mengatur impor Java
Semua SDK untuk kelas Java tersedia untuk UDT secara default. Untuk mereferensikan paket JAR tambahan atau mengatur jalur impor default, gunakan flag sesi berikut.
Mereferensikan paket JAR:
set odps.sql.type.system.odps2=true;
set odps.sql.session.resources=odps-test.jar;
-- JAR harus diunggah ke proyek terlebih dahulu.
select new com.aliyun.odps.test.IntegerMaxValue().evaluate();Anda dapat menentukan beberapa sumber daya yang dipisahkan koma: set odps.sql.session.resources=foo.sh,bar.txt;
odps.sql.session.resources mengontrol baik UDT maupun SELECT TRANSFORM. JAR yang ditetapkan di sini tersedia untuk kedua fitur tersebut.Mengatur jalur impor Java default:
set odps.sql.type.system.odps2=true;
set odps.sql.session.resources=odps-test.jar;
set odps.sql.session.java.imports=com.aliyun.odps.test.*;
-- Dengan impor yang ditetapkan, Anda dapat menghilangkan awalan paket lengkap.
select new IntegerMaxValue().evaluate();odps.sql.session.java.imports menerima classpath (misalnya, java.math.BigInteger) atau wildcard (*). Impor statis tidak didukung.
Operasi yang didukung
UDT mendukung operasi berikut dalam ekspresi SQL:
Membuat objek menggunakan
new— contoh:new java.math.BigInteger('123')Membuat array menggunakan
newdengan daftar inisialisasi — contoh:new Integer[] { 1, 2, 3 }Memanggil metode instans dan metode statis
Mengakses bidang instans publik dan bidang statis publik
Hanya metode publik dan bidang publik yang dapat diakses. Semua pengenal (nama paket, nama kelas, nama metode, nama bidang) bersifat case-sensitive. Kelas anonim dan ekspresi lambda tidak didukung. Fungsi yang tidak mengembalikan nilai tidak dapat dipanggil dalam ekspresi.
Tipe data
Pemetaan tipe
Tipe data Java dipetakan ke tipe bawaan MaxCompute. Pemetaan yang sama yang digunakan dalam UDF Java berlaku untuk UDT.
Panggil metode tipe bawaan secara langsung:
'123'.length(),1L.hashCode()Gunakan UDT dalam fungsi bawaan:
chr(Long.valueOf('100'))—Long.valueOfmengembalikanjava.lang.Long, yang dipetakan ke tipe bawaan BIGINTTipe primitif Java secara otomatis dikonversi ke tipe boxing-nya
Untuk tipe data bawaan baru, tambahkan set odps.sql.type.system.odps2=true; sebelum menjalankan kueri.
Konversi tipe
Konversi tipe SQL didukung:
cast(1 as java.lang.Object)Cast bergaya Java tidak didukung:
(Object)1Objek UDT dapat dikonversi secara implisit ke objek kelas dasar
Objek UDT dapat dikonversi secara eksplisit (cast) ke objek kelas dasar atau subkelas
Konversi antara dua tipe yang tidak terkait mengikuti aturan yang sama dengan konversi tipe bawaan. Misalnya, mengonversi
java.lang.Longkejava.lang.Integermenerapkan aturan yang sama seperti mengonversi BIGINT ke INT, yang dapat menyebabkan kehilangan data.
Objek UDT tidak dapat disimpan ke disk dan tidak dapat dimasukkan langsung ke tabel (DDL tidak mendukung UDT sebagai tipe kolom). Jika nilai UDT dapat dikonversi secara implisit ke tipe bawaan, nilai tersebut dapat ditulis ke tabel. BINARY mendukung serialisasi otomatis — arraybyte[]dapat disimpan dan dideserialisasi. Untuk menyimpan UDT secara persisten, konversikan ke BINARY menggunakan metode serialisasi dan deserialisasi. Nilai UDT tidak dapat muncul langsung dalam output akhir. PanggiltoString()untuk mengonversi UDT apa pun kejava.lang.Stringagar dapat ditampilkan. Untuk mengonversi semua output UDT ke string secara otomatis selama debugging, gunakan: Flag ini hanya berlaku untuk pernyataan PRINT, bukan pernyataan INSERT.
set odps.sql.udt.display.tostring=true;Generik
UDT mendukung generik Java. Kompilator melakukan inferensi parameter tipe dari argumen:
-- Mengembalikan java.util.List<java.math.BigInteger>
java.util.Arrays.asList(new java.math.BigInteger('1'))Tentukan parameter tipe secara eksplisit dalam pemanggilan konstruktor atau gunakan java.lang.Object:
-- ArrayList<Object>
new java.util.ArrayList(java.util.Arrays.asList('1', '2'))
-- ArrayList<String>
new java.util.ArrayList<String>(java.util.Arrays.asList('1', '2'))Semantik operator
Semua operator mengikuti semantik MaxCompute SQL, bukan semantik Java:
Konkatenasi string:
String.valueOf(1) + String.valueOf(2)mengembalikan3(kedua string secara implisit di-cast ke DOUBLE dan dijumlahkan). Untuk menggabungkan sebagai string, gunakan fungsi konkatenasi string.Kesetaraan: Operator
=adalah operator perbandingan SQL, bukan kesetaraan referensi Java. Gunakan metodeequalsuntuk memeriksa apakah dua objek setara.
Kesetaraan objek dan pengacakan data
UDT tidak memiliki definisi yang jelas mengenai kesetaraan objek. Selama pengacakan data, objek dapat ditransmisikan melalui proses dan mesin fisik, menyebabkan satu objek tampak sebagai dua referensi berbeda. Selalu gunakan metode equals — bukan = — untuk membandingkan objek UDT.
Objek dalam baris atau kolom yang sama saling berkorelasi, tetapi korelasi antar baris atau kolom tidak dijamin.
Batasan
UDT tidak dapat digunakan sebagai kunci pengacakan dalam klausa JOIN, GROUP BY, DISTRIBUTE BY, SORT BY, ORDER BY, atau CLUSTER BY. UDT valid dalam ekspresi pada tahap-tahap tersebut, tetapi tidak dapat menjadi output. Contohnya:
group by new java.math.BigInteger('123')— tidak didukunggroup by new java.math.BigInteger('123').hashCode()— didukung, karenahashCode()mengembalikanint.class, yang dipetakan ke tipe bawaan INT
UDF, user-defined aggregate functions (UDAF), dan UDT tidak dapat membaca data dari jenis tabel berikut:
Tabel yang menjalani evolusi skema
Tabel yang berisi tipe data kompleks
Tabel yang berisi tipe data JSON
Tabel transaksional
Mengakses sumber daya
Dalam MaxCompute SQL, panggil metode statis com.aliyun.odps.udf.impl.UDTExecutionContext.get() untuk mendapatkan objek ExecutionContext. Gunakan objek ini untuk mengakses konteks eksekusi saat ini, termasuk file dan tabel yang didaftarkan sebagai sumber daya.
Pertimbangan kinerja
Kinerja UDT mirip dengan kinerja UDF. Mesin komputasi yang dioptimalkan memberikan peningkatan tambahan dalam skenario tertentu:
Tidak ada overhead serialisasi untuk operasi lokal. Ketika objek UDT digunakan dalam proses yang sama (tidak memerlukan pengacakan data, seperti pada tahap JOIN atau AGGREGATE), serialisasi dan deserialisasi dilewati.
Runtime berbasis Codegen. UDT dijalankan melalui Codegen, bukan refleksi, sehingga tidak ada overhead refleksi. Beberapa pemanggilan UDT di-batch menjadi satu pemanggilan fungsi — misalnya,
values[x].add(values[y]).divide(java.math.BigInteger.valueOf(2))dipanggil sekali, menghindari overhead antarmuka per pemanggilan.
Keamanan
UDT tunduk pada model sandbox Java yang sama seperti UDF. Untuk melakukan operasi yang dibatasi oleh sandbox, batalkan isolasi sandbox untuk operasi tersebut atau ajukan permohonan untuk bergabung dalam daftar putih sandbox.
Fitur yang akan ditingkatkan
Fitur berikut direncanakan untuk versi mendatang:
Memanggil fungsi yang tidak mengembalikan nilai, dan fungsi yang langsung menggunakan data yang ditransfer (di mana nilai kembali diabaikan, seperti metode
addpada antarmuka List).Menggunakan kelas anonim dan ekspresi lambda.
Menggunakan UDT sebagai kunci pengacakan.
Mendukung lebih banyak bahasa pemrograman, seperti Python.
Langkah selanjutnya
UDF Java — tabel pemetaan tipe data dan referensi implementasi UDF
Tipe data bawaan baru — tipe yang memerlukan
set odps.sql.type.system.odps2=true;SELECT TRANSFORM — menyematkan skrip dalam pernyataan SQL
COLLECT_SET dan fungsi agregat lainnya — gunakan dengan UDT untuk menerapkan perilaku fungsi agregat dan fungsi bernilai tabel
Sandbox Java — model sandbox dan permohonan daftar putih