L'écriture d'une fonction de table définie par l'utilisateur (UDTF) en Java constitue une méthode efficace pour traiter des tâches complexes de traitement des données et mettre en œuvre une logique personnalisée. En tirant parti des fonctionnalités du langage Java, vous pouvez mieux répondre à des besoins spécifiques de traitement des données, améliorant ainsi l'efficacité du développement et les performances de traitement. Cette rubrique décrit la structure du code, l'utilisation et des exemples d'UDTF Java.
Structure du code UDTF
Vous pouvez écrire le code UDTF en Java à l'aide d'IntelliJ IDEA (Maven) ou de MaxCompute Studio. Le code doit inclure les composants suivants :
-
Package Java : facultatif.
Organisez vos classes Java dans un package afin de faciliter leur recherche et leur utilisation.
-
Extension de la classe UDTF : obligatoire.
Les classes requises sont
com.aliyun.odps.udf.UDTF,com.aliyun.odps.udf.annotation.Resolve(pour l'annotation@Resolve) etcom.aliyun.odps.udf.UDFException(pour les méthodes de la classe Java). Si vous devez utiliser d'autres classes UDTF ou des types de données complexes, ajoutez les classes requises conformément à la section Présentation des UDF MaxCompute. -
Classe Java personnalisée : obligatoire.
Il s'agit de l'unité organisationnelle du code UDTF. Elle définit les variables et les méthodes qui mettent en œuvre votre logique métier.
-
Annotation
@Resolve: obligatoire.Le format est
@Resolve(<signature>). Lasignatureest une signature de fonction qui définit les types de données des paramètres d'entrée et de la valeur de retour. Une UDTF ne peut pas obtenir la signature de fonction par réflexion et doit utiliser l'annotation@Resolvepour la spécifier, par exemple@Resolve("smallint->varchar(10)"). Pour plus d'informations sur l'annotation@Resolve, consultez la section Annotation @Resolve. -
Implémentation des méthodes dans la classe Java : obligatoire.
L'implémentation de la classe Java comprend les quatre méthodes suivantes. Implémentez-les selon vos besoins.
API
Description
public void setup(ExecutionContext ctx) throws UDFExceptionLa méthode d'initialisation. MaxCompute appelle votre logique d'initialisation personnalisée avant que l'UDTF ne commence à traiter les données d'entrée. La méthode
setupest appelée une fois par worker.public void process(Object[] args) throws UDFExceptionLa fonction
processest appelée une fois pour chaque enregistrement d'une requête SQL. Les paramètres de la fonctionprocesscorrespondent aux paramètres d'entrée spécifiés pour l'UDTF dans l'instruction SQL. Les paramètres d'entrée sont transmis sous forme de tableauObject[]et la sortie est générée à l'aide de la fonctionforward. Vous devez appeler la fonctionforwardau sein de la fonctionprocesspour déterminer la sortie.RemarqueL'absence d'appel à
forwarddepuis la méthodeprocessou close peut entraîner une perte de données. Par exemple, si un thread d'arrière-plan exécute un appelforward, vous devez vous assurer que la méthodeprocessne se termine pas tant que l'appelforwardn'est pas terminé afin d'éviter toute perte de données.public void close() throws UDFExceptionLa méthode de terminaison de l'UDTF. Elle n'est appelée qu'une seule fois, après le traitement du dernier enregistrement.
public void forward(Object …o) throws UDFExceptionAppelez la méthode
forwardpour produire des données ; chaque appel àforwardgénère un enregistrement. Lorsque vous appelez une UDTF dans une requête SQL, vous pouvez utiliser la clauseaspour renommer la sortie deforward.Lors de l'écriture d'une UDTF Java, vous pouvez utiliser Java Type ou Java Writable Type. Pour un mappage détaillé entre les types de données pris en charge par MaxCompute et les types de données Java, consultez la section Types de données.
Voici un exemple d'UDTF.
// Organize the defined Java class in the org.alidata.odps.udtf.examples package.
package org.alidata.odps.udtf.examples;
// Extend the UDTF class.
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;
// Custom Java class.
//@Resolve annotation.
@Resolve("string,bigint->string,bigint")
public class MyUDTF extends UDTF {
// Implement the methods of the Java class.
@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);
}
}
}
Limitations
Accès Internet : par défaut, les UDF ne peuvent pas accéder à Internet. Pour activer l'accès Internet, remplissez le formulaire de demande de connexion réseau. Après approbation, l'équipe d'assistance technique MaxCompute vous contactera pour établir la connexion. Pour plus de détails, consultez la section Processus de connexion réseau.
-
Aucune autre colonne dans le même
SELECT: une instructionSELECTqui appelle une UDTF ne peut pas référencer d'autres colonnes ou expressions. L'instruction suivante n'est pas valide :-- Invalid: mixes a UDTF with another column select value, user_udtf(key) as mycol ... -
Pas d'imbrication : les UDTF ne peuvent pas être imbriquées dans d'autres UDTF. L'instruction suivante n'est pas valide :
-- Invalid: user_udtf2 is nested inside user_udtf1 select user_udtf1(user_udtf2(key)) as mycol...; -
Incompatible avec
GROUP BY,DISTRIBUTE BYetSORT BY: une UDTF ne peut pas apparaître dans la même instructionSELECTque ces clauses. L'instruction suivante n'est pas valide :-- Invalid: UDTF used with GROUP BY select user_udtf(key) as mycol ... group by mycol;
Considérations
Lorsque vous écrivez une UDTF Java, tenez compte des points suivants :
Évitez de définir des classes portant le même nom mais ayant une logique d'implémentation différente dans différents packages JAR UDTF. Par exemple, supposons que UDTF1 et UDTF2 correspondent respectivement aux ressources de package JAR udtf1.jar et udtf2.jar. Si les deux packages JAR contiennent une classe nommée
com.aliyun.UserFunction.classavec une logique différente, MaxCompute charge aléatoirement l'une des classes lorsque UDTF1 et UDTF2 sont appelées dans la même instruction SQL. Cela peut entraîner des résultats inattendus ou des échecs de compilation.Les paramètres d'entrée et les valeurs de retour doivent utiliser des types d'objets Java, tels que
StringetLong, plutôt que des types primitifs.En effet, les valeurs SQL
NULLsont transmises en tant quenullJava, ce que les types primitifs ne peuvent pas représenter.
Annotation @Resolve
Le format de l'annotation @Resolve est le suivant.
@Resolve(<signature>)
La chaîne signature spécifie les types de données des paramètres d'entrée et des valeurs de retour de l'UDTF. Lors de l'analyse de la requête, MaxCompute valide les appels par rapport à cette signature et signale une erreur en cas d'incompatibilité de type. Le format est le suivant.
'arg_type_list -> type_list'
Où :
type_list: représente les types de données des valeurs de retour. Une UDTF peut renvoyer plusieurs colonnes. Les types de données pris en charge sont BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), les types de données complexes (ARRAY, MAP, STRUCT) et les types de données complexes imbriqués.-
arg_type_list: représente les types de données des paramètres d'entrée. Plusieurs paramètres d'entrée peuvent être spécifiés, séparés par des virgules (,). Les types de données pris en charge sont BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, VARCHAR, les types de données complexes (ARRAY, MAP, STRUCT) et les types de données complexes imbriqués.La
arg_type_listprend également en charge un astérisque (*) ou une chaîne vide ('').Si la
arg_type_listest un astérisque (*), cela indique que la fonction accepte n'importe quel nombre de paramètres d'entrée.Si la
arg_type_listest une chaîne vide (''), cela indique que la fonction n'a aucun paramètre d'entrée.
Pour plus d'informations sur la syntaxe étendue de l'annotation Resolve, consultez la section Paramètres dynamiques pour les UDAF et les UDTF.
Le tableau suivant présente des exemples d'annotations @Resolve valides.
|
Exemple |
Description |
|
|
Les types de paramètres d'entrée sont BIGINT et BOOLEAN. Les types de valeurs de retour sont STRING et DATETIME. |
|
|
La fonction accepte n'importe quel nombre de paramètres d'entrée. Les types de valeurs de retour sont STRING et DATETIME. |
|
|
La fonction n'a aucun paramètre d'entrée. Les types de valeurs de retour sont DOUBLE, BIGINT et STRING. |
|
|
Les types de paramètres d'entrée sont ARRAY, STRUCT et STRING. Les types de valeurs de retour sont MAP et STRUCT. |
Types de données
Les types de données pris en charge par MaxCompute varient selon l'édition du type de données. À partir de MaxCompute 2.0, des types de données supplémentaires sont disponibles, notamment des types complexes tels que ARRAY, MAP et STRUCT. Pour plus d'informations, consultez la section Éditions des types de données.
Lors de l'écriture d'une UDTF Java, assurez-vous que les types de données que vous utilisez correspondent correctement à ceux pris en charge par MaxCompute. Le tableau suivant décrit ces mappages.
|
Type MaxCompute |
Type Java |
Type Java Writable |
|
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 |
N/A |
IntervalYearMonthWritable |
|
INTERVAL_DAY_TIME |
N/A |
IntervalDayTimeWritable |
|
ARRAY |
java.util.List |
N/A |
|
MAP |
java.util.Map |
N/A |
|
STRUCT |
com.aliyun.odps.data.Struct |
N/A |
Pour utiliser les types Java Writable pour les entrées ou les valeurs de retour de l'UDTF, votre projet MaxCompute doit utiliser l'édition de type de données MaxCompute 2.0.
Utilisation
Après avoir développé une UDTF Java en suivant le processus de développement, vous pouvez l'appeler dans MaxCompute SQL.
Utilisation d'une UDF dans un projet MaxCompute : la méthode est similaire à celle de l'utilisation des fonctions intégrées. Vous pouvez utiliser une fonction définie par l'utilisateur de la même manière qu'une fonction intégrée.
Utilisation d'une UDF entre projets : utilisez une UDF du projet B dans le projet A. L'instruction suivante montre un exemple :
select B:udf_in_other_project(arg0, arg1) as res from table_t;. Pour plus d'informations sur le partage interprojets, consultez la section Accès aux ressources interprojets basé sur les packages.
Pour une procédure complète de développement et d'appel d'une UDTF Java à l'aide de MaxCompute Studio, consultez la section Exemple d'utilisation.
Exemple d'utilisation
La procédure suivante vous guide pas à pas dans le développement et l'appel d'une UDTF Java à l'aide de MaxCompute Studio :
-
Préparez l'environnement.
Avant de pouvoir développer et déboguer une UDF dans MaxCompute Studio, installez MaxCompute Studio et connectez-le à un projet MaxCompute. Pour plus d'informations, consultez les rubriques suivantes :
-
Écrivez le code UDTF.
Dans l'explorateur Project, cliquez avec le bouton droit sur le répertoire source du module () et sélectionnez .
-
Dans la boîte de dialogue Create new MaxCompute java class, cliquez sur UDTF, saisissez un Name et appuyez sur Entrée. Par exemple, nommez la classe Java MyUDTF.
Name correspond au nom de la classe Java MaxCompute à créer. Si aucun package n'a été créé, vous pouvez saisir ici packagename.classname pour générer automatiquement un package.
-
Dans l'éditeur de code, saisissez le code suivant. Il s'agit d'un exemple de code 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); } } }
-
Exécutez et déboguez l'UDTF localement pour vous assurer que le code fonctionne comme prévu.
Pour plus d'informations sur le débogage, consultez la section Exécuter et déboguer une UDF localement.
Cliquez avec le bouton droit sur le fichier MyUDTF dans l'arborescence du projet et sélectionnez Run 'MyUDTF.main()'. Dans la boîte de dialogue Run/Debug Configurations, définissez MaxCompute project sur
local, MaxCompute table surwc_in2, Table partition surp2=1,p1=2, Table columns surcolc,colb, Download Record limit sur100et Data Column Separator sur|. Cliquez ensuite sur OK.RemarqueVous pouvez utiliser les paramètres précédents pour l'exécution de l'exemple.
-
Empaquetez l'UDTF dans un package JAR, téléchargez-le dans votre projet MaxCompute et enregistrez la fonction. Pour cet exemple, nommez la fonction
user_udtf.Pour plus d'informations sur l'empaquetage, consultez la section Procédure.
Dans l'arborescence du projet IntelliJ IDEA, cliquez avec le bouton droit sur le fichier Java UDTF (par exemple, MyUDTF) et sélectionnez Deploy to server.... Dans la boîte de dialogue Package a jar, submit resource and register function, sélectionnez le MaxCompute project cible, confirmez le chemin d'accès au Resource file, définissez Main class sur la classe UDTF correspondante (par exemple,
org.alidata.odps.udtf.examples.MyUDTF), saisissezuser_udtfpour Function name, sélectionnez Force update if already exists et cliquez sur OK pour terminer le déploiement. -
Dans le volet de navigation de gauche de MaxCompute Studio, cliquez sur Project Explorer. Cliquez avec le bouton droit sur le projet MaxCompute cible, démarrez le client MaxCompute et exécutez une commande SQL pour appeler l'UDTF nouvellement créée.
Supposons que la table cible, my_table, contienne les données suivantes :
+------------+------------+ | col0 | col1 | +------------+------------+ | A B | 1 | | C D | 2 | +------------+------------+Exécutez la commande SQL suivante pour appeler l'UDTF.
select user_udtf(col0, col1) as (c0, c1) from my_table;Le résultat suivant est renvoyé.
+----+------------+ | c0 | c1 | +----+------------+ | A | 1 | | B | 1 | | C | 2 | | D | 2 | +----+------------+
Documentation connexe
Pour plus d'exemples d'utilisation des UDTF Java, consultez la section Exemples de UDTF Java.