Tous les produits
Search
Centre de documentation

E-MapReduce:Fonction UDF Java

Dernière mise à jour :Aug 22, 2026

Cette rubrique explique comment développer et utiliser des fonctions définies par l'utilisateur (UDF) en Java.

Informations générales

À partir de la version 2.2.0, StarRocks prend en charge les fonctions définies par l'utilisateur (UDF) écrites en Java.

Depuis la version 3.0, StarRocks prend en charge les UDF globales. Il vous suffit d'ajouter le mot-clé GLOBAL aux instructions SQL pertinentes (CREATE, SHOW et DROP) pour les appliquer à l'échelle globale. Cela vous évite d'exécuter les instructions pour chaque base de données individuellement. Vous pouvez développer des fonctions personnalisées adaptées à vos scénarios métier afin d'étendre les capacités de StarRocks.

StarRocks prend actuellement en charge les types d'UDF suivants :

  • fonction scalaire définie par l'utilisateur (scalar UDF)

  • fonction d'agrégation définie par l'utilisateur (UDAF)

  • fonction de fenêtre définie par l'utilisateur (UDWF)

  • fonction table définie par l'utilisateur (UDTF)

Prérequis ​

Avant d'utiliser les UDF Java dans StarRocks, assurez-vous de remplir les conditions suivantes :

  • Installez Apache Maven pour créer et développer des projets Java.

  • Installez le JDK 1.8 sur votre serveur.

  • Activez la fonctionnalité UDF en définissant l'élément de configuration FE enable_udf sur TRUE dans la page Instance Configuration. Redémarrez ensuite l'instance pour appliquer la modification.

Correspondance des types de données

Type SQL

Type Java

BOOLEAN

java.lang.Boolean

TINYINT

java.lang.Byte

SMALLINT

java.lang.Short

INT

java.lang.Integer

BIGINT

java.lang.Long

FLOAT

java.lang.Float

DOUBLE

java.lang.Double

STRING/VARCHAR

java.lang.String

Développer et utiliser des UDF

Vous allez créer un projet Maven et écrire la fonction en Java.

Étape 1 : Créer un projet Maven

Créez un projet Maven avec la structure de répertoire de base suivante.

project
|--pom.xml
|--src
|  |--main
|  |  |--java
|  |  |--resources
|  |--test
|--target

Étape 2 : Ajouter les dépendances ​

Ajoutez les dépendances suivantes au fichier pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>udf</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Étape 3 : Développer la UDF ​

Développez les UDF requises en Java.

Développer une scalar UDF

Une scalar UDF traite une ligne à la fois et renvoie une seule valeur pour chaque ligne. Lorsque vous utilisez une scalar UDF dans une requête, chaque ligne apparaît dans l'ensemble de résultats. Les fonctions scalaires typiques incluent UPPER, LOWER, ROUND et ABS.

L'exemple suivant montre comment extraire des données d'un objet JSON. Dans certains scénarios métier, la valeur d'un champ dans les données JSON peut être une chaîne JSON plutôt qu'un objet JSON. Pour extraire la chaîne JSON imbriquée, vous devez utiliser un appel imbriqué à la fonction GET_JSON_STRING, tel que GET_JSON_STRING(GET_JSON_STRING('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key"), "$.k0").

Pour simplifier l'instruction SQL, vous pouvez développer une UDF qui extrait directement la chaîne JSON, par exemple MY_UDF_JSON_GET('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key.k0").

package com.starrocks.udf.sample;
import com.alibaba.fastjson.JSONPath;

public class UDFJsonGet {
    public final String evaluate(String jsonObj, String key) {
        if (obj == null || key == null) return null;
        try {
            // The JSONPath library can fully expand the path, even if a field value is a JSON-formatted string.
            return JSONPath.read(jsonObj, key).toString();
        } catch (Exception e) {
            return null;
        }
    }
}

Votre classe personnalisée doit implémenter la méthode suivante.

Remarque

Les types de données des paramètres de requête et de la valeur de retour dans la méthode doivent correspondre à ceux déclarés dans l'instruction CREATE FUNCTION de l'étape 6. La correspondance des types de données doit respecter les règles décrites dans la section Correspondance des types de données.

Méthode

Description

TYPE1 evaluate(TYPE2, ...)

La méthode evaluate est le point d'entrée de la UDF et doit être une méthode membre publique.

Développer une UDAF

Une UDAF opère sur un groupe de lignes et renvoie une seule valeur. Les fonctions d'agrégation typiques incluent SUM, COUNT, MAX et MIN. Ces fonctions agrègent les données de plusieurs lignes au sein de chaque groupe GROUP BY et produisent un seul résultat pour chaque groupe.

L'exemple suivant utilise la fonction MY_SUM_INT. Contrairement à la fonction intégrée SUM, qui renvoie une valeur BIGINT, MY_SUM_INT accepte un argument INT et renvoie un INT.

package com.starrocks.udf.sample;

public class SumInt {
    public static class State {
        int counter = 0;
        public int serializeLength() { return 4; }
    }

    public State create() {
        return new State();
    }

    public void destroy(State state) {
    }

    public final void update(State state, Integer val) {
        if (val != null) {
            state.counter+= val;
        }
    }

    public void serialize(State state, java.nio.ByteBuffer buff) {
        buff.putInt(state.counter);
    }

    public void merge(State state, java.nio.ByteBuffer buffer) {
        int val = buffer.getInt();
        state.counter += val;
    }

    public Integer finalize(State state) {
        return state.counter;
    }
}

Votre classe personnalisée doit implémenter les méthodes suivantes.

Remarque

Les types de données des paramètres de requête et de la valeur de retour dans les méthodes doivent correspondre à ceux déclarés dans l'instruction CREATE FUNCTION de l'étape 6. La correspondance des types de données doit respecter les règles décrites dans la section Correspondance des types de données.

Méthode

Description

State create()

Crée un objet d'état.

void destroy(State)

Détruit l'objet d'état.

void update(State, ...)

Met à jour l'état. Le premier argument est l'objet d'état et les arguments suivants sont les paramètres de requête spécifiés dans la déclaration de la fonction. Un ou plusieurs paramètres de requête sont pris en charge.

void serialize(State, ByteBuffer)

Sérialise l'état.

void merge(State, ByteBuffer)

Fusionne un état sérialisé dans l'état actuel.

TYPE finalize(State)

Calcule et renvoie le résultat final à partir de l'état.

Lors du développement d'une UDAF, vous devez utiliser la classe de tampon java.nio.ByteBuffer pour stocker le résultat intermédiaire et la méthode serializeLength pour spécifier la longueur du résultat intermédiaire sérialisé.

Classe et méthode

Description

java.nio.ByteBuffer()

Cette classe de tampon stocke le résultat intermédiaire. Étant donné que le résultat est sérialisé pour le transfert entre les nœuds, vous devez utiliser serializeLength() pour spécifier sa taille sérialisée.

serializeLength()

La longueur en octets du résultat intermédiaire sérialisé. La méthode serializeLength() doit renvoyer un INT. Par exemple, State { int counter = 0; public int serializeLength() { return 4; }} indique que le résultat intermédiaire est un INT et que sa longueur sérialisée est de 4 octets. Vous pouvez personnaliser cette valeur selon vos besoins. Par exemple, si le résultat intermédiaire est un LONG d'une longueur de 8 octets, vous utiliseriez State { long counter = 0; public int serializeLength() { return 8; }}.

Remarque

Tenez compte des exigences suivantes pour la sérialisation java.nio.ByteBuffer :

  • Ne vous fiez pas à la méthode remaining() de ByteBuffer pour désérialiser l'état.

  • N'appelez pas la méthode clear() sur le ByteBuffer.

  • La valeur renvoyée par serializeLength() doit correspondre à la longueur réelle des données écrites dans le tampon. Une incompatibilité entraîne des erreurs de sérialisation et de désérialisation.

Développer une UDWF

Une UDWF est un type spécial de fonction d'agrégation. Contrairement à une fonction d'agrégation classique, une fonction de fenêtre calcule une valeur sur un groupe de lignes (une fenêtre) et renvoie un résultat distinct pour chaque ligne. Généralement, une fonction de fenêtre inclut une clause OVER qui divise les lignes en partitions. La fonction calcule ensuite un résultat pour chaque ligne en fonction de la fenêtre de lignes à laquelle elle appartient.

L'exemple suivant utilise la fonction MY_WINDOW_SUM_INT. Contrairement à la fonction intégrée SUM, qui renvoie une valeur BIGINT, MY_WINDOW_SUM_INT accepte un argument INT et renvoie un INT.

package com.starrocks.udf.sample;

public class WindowSumInt {    
    public static class State {
        int counter = 0;
        public int serializeLength() { return 4; }
        @Override
        public String toString() {
            return "State{" +
                    "counter=" + counter +
                    '}';
        }
    }

    public State create() {
        return new State();
    }

    public void destroy(State state) {

    }

    public void update(State state, Integer val) {
        if (val != null) {
            state.counter+=val;
        }
    }

    public void serialize(State state, java.nio.ByteBuffer buff) {
        buff.putInt(state.counter);
    }

    public void merge(State state, java.nio.ByteBuffer buffer) {
        int val = buffer.getInt();
        state.counter += val;
    }

    public Integer finalize(State state) {
        return state.counter;
    }

    public void reset(State state) {
        state.counter = 0;
    }

    public void windowUpdate(State state,
                            int peer_group_start, int peer_group_end,
                            int frame_start, int frame_end,
                            Integer[] inputs) {
        for (int i = (int)frame_start; i < (int)frame_end; ++i) {
            state.counter += inputs[i];
        }
    }
}

Étant donné qu'une fonction de fenêtre est un type spécial de fonction d'agrégation, votre classe personnalisée doit implémenter toutes les méthodes UDAF requises, ainsi que la méthode windowUpdate().

Remarque

Les types de données des paramètres de requête et de la valeur de retour dans les méthodes doivent correspondre à ceux déclarés dans l'instruction CREATE FUNCTION de l'étape 6. La correspondance des types de données doit respecter les règles décrites dans la section Correspondance des types de données.

Méthode supplémentaire requise

Méthode

Description

void windowUpdate(State state, int, int, int , int, ...)

Met à jour les données de la fenêtre. Pour plus d'informations sur les fonctions de fenêtre, consultez la section Fonctions de fenêtre. Pour chaque ligne d'entrée, les informations de fenêtre correspondantes sont récupérées pour mettre à jour le résultat intermédiaire.

  • peer_group_start : La position de départ de la partition actuelle.

    Une partition est un groupe de lignes partageant la même valeur pour la colonne spécifiée dans la clause PARTITION BY.

  • peer_group_end : La position de fin de la partition actuelle.

  • frame_start : La position de départ du cadre de fenêtre actuel.

    Un cadre de fenêtre est un sous-ensemble de lignes au sein d'une partition qui définit l'étendue du calcul pour la ligne actuelle. Par exemple, ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING définit un cadre qui inclut la ligne actuelle, la ligne précédente et la ligne suivante.

  • frame_end : La position de fin du cadre de fenêtre actuel.

  • inputs : Les données d'entrée pour une fenêtre, fournies sous forme de tableau de classes wrapper. La classe wrapper doit correspondre au type de données d'entrée. Dans cet exemple, le type de données d'entrée est INT, donc le tableau de classes wrapper est Integer[].

Développer une UDTF

Une UDTF prend une seule ligne en entrée et produit une table de lignes en sortie. Les UDTF sont souvent utilisées pour des opérations telles que la division d'une colonne en plusieurs lignes.

Remarque

Actuellement, les UDTF ne prennent en charge que le renvoi de plusieurs lignes avec une seule colonne.

L'exemple suivant utilise la fonction MY_UDF_SPLIT. Elle divise une chaîne en utilisant un espace comme séparateur. L'argument d'entrée et la valeur de retour sont tous deux de type STRING.

package com.starrocks.udf.sample;

public class UDFSplit{
    public String[] process(String in) {
        if (in == null) return null;
        return in.split(" ");
    }
}

Votre classe personnalisée doit implémenter la méthode suivante.

Remarque

Les types de données des paramètres de requête et de la valeur de retour dans la méthode doivent correspondre à ceux déclarés dans l'instruction CREATE FUNCTION de l'étape 6. La correspondance des types de données doit respecter les règles décrites dans la section Correspondance des types de données.

Méthode

Description

TYPE[] process()

La méthode process() est le point d'entrée de la UDTF et doit renvoyer un tableau.

Étape 4 : Empaqueter le projet Java

Exécutez la commande suivante pour empaqueter le projet Java.

mvn package

Cette commande génère deux fichiers JAR dans le répertoire target : udf-1.0-SNAPSHOT.jar et udf-1.0-SNAPSHOT-jar-with-dependencies.jar.

Étape 5 : Télécharger le projet

Téléchargez le fichier udf-1.0-SNAPSHOT-jar-with-dependencies.jar dans un compartiment OSS et accordez l'autorisation public-read au fichier JAR. Pour plus d'informations, consultez les sections Téléchargement simple et ACL du compartiment.

Remarque

À l'étape 6, le FE valide le fichier JAR de la UDF et calcule une somme de contrôle. Le BE télécharge ensuite le fichier JAR et l'exécute.

Étape 6 : Créer la UDF dans StarRocks

StarRocks propose deux espaces de noms pour les UDF : au niveau de la base de données et au niveau global.

  • Si vous n'avez pas besoin d'isolation de visibilité pour vos UDF, vous pouvez créer une UDF globale. Lors de l'appel à une UDF globale, vous pouvez l'invoquer directement par son nom de fonction sans préfixe de catalogue ou de base de données, ce qui simplifie l'accès.

  • Si vous avez besoin d'isolation de visibilité ou si vous devez créer des UDF portant le même nom dans différentes bases de données, vous pouvez créer une UDF au niveau de la base de données. Si votre session actuelle se trouve dans cette base de données, vous pouvez appeler la fonction directement par son nom. Si votre session se trouve dans un catalogue ou une base de données différent, vous devez utiliser un nom complet, tel que catalog.database.function.

Remarque

La création d'une UDF globale nécessite l'autorisation CREATE GLOBAL FUNCTION au niveau du système. La création d'une UDF au niveau de la base de données nécessite l'autorisation CREATE FUNCTION sur la base de données. L'utilisation de toute UDF nécessite l'autorisation USAGE sur celle-ci. Pour savoir comment accorder des autorisations, consultez la documentation GRANT.

Après avoir téléchargé le fichier JAR, créez la UDF correspondante dans StarRocks. Pour créer une UDF globale, ajoutez simplement le mot-clé GLOBAL à l'instruction SQL.

Syntaxe

CREATE [GLOBAL][AGGREGATE | TABLE] FUNCTION function_name(arg_type [, ...])
RETURNS return_type
[PROPERTIES ("key" = "value" [, ...]) ]

Paramètres

Paramètre

Obligatoire

Description

GLOBAL

Non

Spécifie que la UDF est une UDF globale. Pris en charge depuis StarRocks v3.0.

AGGREGATE

Non

Spécifie que la fonction à créer est une UDAF ou une UDWF.

TABLE

Non

Spécifie que la fonction à créer est une UDTF.

function_name

Oui

Le nom de la fonction. Il peut inclure un nom de base de données, tel que db1.my_func. Si un function_name inclut un nom de base de données, la UDF est créée dans cette base de données. Sinon, elle est créée dans la base de données actuelle. La combinaison du nom de la fonction et des arguments doit être unique au sein de la base de données. Toutefois, vous pouvez surcharger une fonction en en créant une autre portant le même nom mais avec des types d'arguments différents.

arg_type

Oui

Le type de données d'un argument de fonction. Pour les types de données pris en charge, consultez la section Correspondance des types de données.

return_type

Oui

Le type de données de la valeur de retour de la fonction. Pour les types de données pris en charge, consultez la section Correspondance des types de données.

properties

Oui

Propriétés liées à la fonction. Différentes propriétés sont requises pour différents types d'UDF. Consultez les exemples suivants pour plus de détails.

Créer une scalar UDF

Exécutez la commande suivante pour créer la scalar UDF de l'exemple précédent dans StarRocks.

CREATE [GLOBAL] FUNCTION MY_UDF_JSON_GET(string, string) 
RETURNS string
PROPERTIES (
    "symbol" = "com.starrocks.udf.sample.UDFJsonGet", 
    "type" = "StarrocksJar",
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

Paramètre

Description

symbol

Le nom de classe complet de la UDF, au format <package_name>.<class_name>.

type

Le type de UDF. Pour les UDF Java, définissez cette valeur sur StarrocksJar.

file

Le chemin HTTP vers le fichier JAR de la UDF. Il doit s'agir de l'URL HTTP du fichier dans OSS, de préférence en utilisant un endpoint interne. Le format est http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/<jar_package_name>.

Créer une UDAF

Exécutez la commande suivante pour créer la UDAF de l'exemple précédent dans StarRocks.

CREATE [GLOBAL] AGGREGATE FUNCTION MY_SUM_INT(INT) 
RETURNS INT
PROPERTIES 
( 
    "symbol" = "com.starrocks.udf.sample.SumInt", 
    "type" = "StarrocksJar",
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

Les paramètres de la clause PROPERTIES sont identiques à ceux décrits dans la section Créer une scalar UDF.

Créer une UDWF

Exécutez la commande suivante pour créer la UDWF de l'exemple précédent dans StarRocks.

CREATE [GLOBAL] AGGREGATE FUNCTION MY_WINDOW_SUM_INT(Int)
RETURNS Int
PROPERTIES 
(
    "analytic" = "true",
    "symbol" = "com.starrocks.udf.sample.WindowSumInt", 
    "type" = "StarrocksJar", 
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

La propriété analytic identifie la fonction comme une fonction de fenêtre. Pour une UDWF, définissez cette valeur sur true. Les autres paramètres sont identiques à ceux décrits dans la section Créer une scalar UDF.

Créer une UDTF

Exécutez la commande suivante pour créer la UDTF de l'exemple précédent dans StarRocks.

CREATE [GLOBAL] TABLE FUNCTION MY_UDF_SPLIT(string)
RETURNS string
PROPERTIES 
(
    "symbol" = "com.starrocks.udf.sample.UDFSplit", 
    "type" = "StarrocksJar", 
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

Les paramètres de la clause PROPERTIES sont identiques à ceux décrits dans la section Créer une scalar UDF.

Étape 7 : Utiliser la UDF

Après avoir créé la UDF, vous pouvez la tester et l'utiliser.

Utiliser la scalar UDF

Exécutez la commande suivante pour utiliser la scalar UDF créée à l'étape 6.

SELECT MY_UDF_JSON_GET('{"key":"{\\"in\\":2}"}', '$.key.in');

Utiliser la UDAF

Exécutez la commande suivante pour utiliser la UDAF créée à l'étape 6.

SELECT MY_SUM_INT(col1);

Utiliser la UDWF

Exécutez la commande suivante pour utiliser la UDWF créée à l'étape 6.

SELECT MY_WINDOW_SUM_INT(intcol) 
            OVER (PARTITION BY intcol2
                  ORDER BY intcol3
                  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM test_basic;

Utiliser la UDTF

Exécutez la commande suivante pour utiliser la UDTF de l'exemple précédent.

-- Assume that table t1 exists with columns a, b, and c1.
SELECT t1.a,t1.b,t1.c1 FROM t1;
> output:
1,2.1,"hello world"
2,2.2,"hello UDTF."

-- Use the MY_UDF_SPLIT() function.
SELECT t1.a,t1.b, MY_UDF_SPLIT FROM t1, MY_UDF_SPLIT(t1.c1); 
> output:
1,2.1,"hello"
1,2.1,"world"
2,2.2,"hello"
2,2.2,"UDTF."
Remarque
  • Dans la liste SELECT, le premier MY_UDF_SPLIT est l'alias de colonne par défaut pour la sortie de la fonction MY_UDF_SPLIT.

  • Actuellement, vous ne pouvez pas spécifier d'alias de table ou d'alias de colonne pour la sortie de la UDTF, par exemple avec AS t2(f1).

Afficher les UDF ​

Exécutez la commande suivante pour afficher les informations sur les UDF.

SHOW [GLOBAL] FUNCTIONS;

Supprimer des UDF

Exécutez la commande suivante pour supprimer une UDF spécifiée.

DROP [GLOBAL] FUNCTION <function_name>(arg_type [, ...]);

FAQ

Q : Puis-je utiliser des variables statiques lors du développement d'une UDF ? Les variables statiques de différentes UDF interfèrent-elles entre elles ?

R : Oui, vous pouvez utiliser des variables statiques. Les variables statiques sont isolées entre différentes UDF et n'interfèrent pas entre elles, même si les UDF partagent le même nom de classe.