全部产品
Search
文档中心

MaxCompute:UDF dengan Penyematan Kode

更新时间:Jul 02, 2025

Topik ini menjelaskan cara menggunakan fungsi yang ditentukan pengguna (UDF) dengan penyematan kode untuk menyematkan kode Java atau Python ke dalam skrip SQL.

Informasi latar belakang

UDF dengan penyematan kode dari MaxCompute menyelesaikan masalah berikut terkait implementasi dan pemeliharaan kode:

  • Implementasi kode yang rumit: Setelah membuat UDF dan mengembangkan kode, Anda harus mengompilasi kode dalam Java serta membuat sumber daya dan fungsi.

  • Pemeliharaan kode yang tidak nyaman: Tidak dapat langsung melihat logika implementasi UDF yang dirujuk dalam skrip SQL atau mendapatkan kode sumber paket JAR.

  • Keterbacaan kode yang buruk: Untuk mengimplementasikan fungsi pustaka Java menggunakan tipe yang ditentukan pengguna (UDT), Anda perlu mengonversi kode Java menjadi ekspresi dalam baris kode panjang. Selain itu, beberapa kode Java mungkin gagal ditulis sebagai ekspresi. Contoh:

    Foo f = new Foo();
    f.execute();
    f.getResult();

Deskripsi

UDF dengan penyematan kode memungkinkan Anda menyematkan kode Java atau Python ke dalam skrip SQL. Saat mengompilasi skrip, Janino-compiler mengidentifikasi dan mengekstraksi kode yang disematkan, mengompilasi kode dalam Java, lalu secara dinamis menghasilkan sumber daya dan membuat fungsi sementara.

Anda dapat meletakkan skrip SQL dan baris kode pihak ketiga dalam file kode sumber yang sama. Hal ini menyederhanakan penggunaan UDT atau UDF serta mempermudah pengembangan dan pemeliharaan sehari-hari.

Batasan

Anda hanya dapat menggunakan Janino-compiler untuk mengompilasi kode Java yang disematkan. Sintaks kode Java yang disematkan harus merupakan subset dari sintaks JDK standar. Berikut adalah batasan kode Java yang disematkan:

  • Ekspresi lambda tidak didukung.

  • Tidak dapat menentukan beberapa jenis pengecualian dalam satu blok catch. Sebagai contoh, catch(Pengecualian1 | Pengecualian2 e) tidak diizinkan.

  • Tidak dapat secara otomatis melakukan inferensi argumen generik. Sebagai contoh, Map map = new HashMap<>(); tidak didukung.

  • Ekspresi untuk inferensi argumen tipe diabaikan. Anda harus menggunakan ekspresi cast untuk menentukan tipe argumen, seperti (String) myMap.get(key).

  • Aserksi diaktifkan secara paksa, meskipun opsi -ea dari Java virtual machine (JVM) digunakan.

  • Kode yang diprogram dalam versi setelah Java 8 tidak didukung.

Rujuk kode tersemat dalam UDT

Contoh kode berikut menunjukkan cara merujuk kode tersemat. Anda perlu mengirimkan pernyataan SQL dalam mode skrip. Untuk informasi lebih lanjut, lihat SQL dalam Mode Skrip.

SELECT 
  s, 
  com.mypackage.Foo.extractNumber(s) 
FROM VALUES ('abc123def'),('apple') AS t(s);

#CODE ('lang'='JAVA')
package com.mypackage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Foo {
  final static Pattern compile = Pattern.compile(".*?([0-9]+).*");
  public static String extractNumber(String input) {
    final Matcher m = compile.matcher(input);
    if (m.find()) {
      return m.group(1);
    }
    return null;
  }
}
#END CODE;
  • #CODE menandai awal blok kode tersemat. #END CODE menandai akhir blok kode tersemat. Dalam contoh ini, blok kode tersemat ditempatkan di akhir skrip dan berlaku untuk seluruh skrip.

  • 'lang'='JAVA' menunjukkan bahwa kode tersemat adalah dalam Java. JAVA dapat diganti dengan PYTHON jika Anda mengompilasi kode dalam Python.

  • Anda dapat menggunakan sintaks UDT dalam skrip SQL untuk memanggil Foo.extractNumber.

Definisikan dan panggil UDF dengan penyematan kode Java

Contoh kode berikut menunjukkan cara mendefinisikan dan memanggil UDF dengan penyematan kode Java. Anda perlu mengirimkan pernyataan SQL dalam mode skrip. Untuk informasi lebih lanjut, lihat SQL dalam Mode Skrip.

CREATE TEMPORARY FUNCTION foo AS 'com.mypackage.Reverse' USING
#CODE ('lang'='JAVA')
package com.mypackage;
import com.aliyun.odps.udf.UDF;
public class Reverse extends UDF {
  public String evaluate(String input) {
    if (input == null) return null;
    StringBuilder ret = new StringBuilder();
    for (int i = input.toCharArray().length - 1; i >= 0; i--) {
      ret.append(input.toCharArray()[i]);
    }
    return ret.toString();
  }
}
#END CODE;

SELECT foo('abdc');
  • Blok kode tersemat dapat ditempatkan setelah USING atau di akhir skrip. Blok kode yang ditempatkan setelah USING hanya berlaku untuk pernyataan CREATE TEMPORARY FUNCTION.

  • Fungsi yang dibuat oleh CREATE TEMPORARY FUNCTION bersifat sementara. Fungsi sementara ini hanya berlaku selama proses eksekusi saat ini dan tidak disimpan dalam sistem metadata MaxCompute. Untuk informasi lebih lanjut tentang cara membuat fungsi permanen dan menyimpannya dalam sistem metadata MaxCompute, lihat CREATE SQL FUNCTION.

Definisikan dan panggil UDTF dengan penyematan kode Java

Contoh kode berikut menunjukkan cara mendefinisikan dan memanggil UDTF dengan penyematan kode Java. Anda perlu mengirimkan pernyataan SQL dalam mode skrip. Untuk informasi lebih lanjut, lihat SQL dalam Mode Skrip.

CREATE TEMPORARY FUNCTION foo AS 'com.mypackage.Reverse' USING 
#CODE ('lang'='JAVA', 'filename'='embedded.jar')
package com.mypackage;

import com.aliyun.odps.udf.UDTF;
import com.aliyun.odps.udf.UDFException;
import com.aliyun.odps.udf.annotation.Resolve;

@Resolve({"string->string,string"})
public class Reverse extends UDTF {
  @Override
  public void process(Object[] objects) throws UDFException {
    String str = (String) objects[0];
    String[] split = str.split(",");
    forward(split[0], split[1]);
  }
}

#END CODE;

SELECT foo('ab,dc') AS (a,b);

Nilai kembali dari @Resolve harus bertipe string[]. Namun, Janino-compiler tidak dapat mengidentifikasi "string->string,string" sebagai string[]. Untuk mengatasi masalah ini, parameter yang dikomentari oleh @Resolve harus diletakkan dalam kurung kurawal {}. Saat membuat Java UDTF menggunakan metode umum, kurung kurawal {} tidak diperlukan.

Definisikan dan panggil UDF dengan penyematan kode Python

Contoh kode berikut menunjukkan cara mendefinisikan dan memanggil UDF dengan penyematan kode Python. Anda perlu mengirimkan pernyataan SQL dalam mode skrip. Untuk informasi lebih lanjut, lihat SQL dalam Mode Skrip.

CREATE TEMPORARY FUNCTION foo AS 'embedded.UDFTest' USING
#CODE ('lang'='PYTHON', 'filename'='embedded')
from odps.udf import annotate
@annotate("bigint->bigint")
class UDFTest(object):
  def evaluate(self, a):
    return a * a
#END CODE;

SELECT foo(4);
  • Indentasi kode Python harus sesuai dengan spesifikasi bahasa Python.

  • Saat membuat UDF Python, nama kelas yang mengikuti klausa AS harus mencakup nama file kode sumber Python. Anda dapat menggunakan 'filename'='embedded' untuk menentukan nama file virtual.

  • Untuk informasi lebih lanjut tentang pengembangan dan penggunaan versi Python yang berbeda, lihat topik berikut: