Escribir una función de tabla definida por el usuario (UDTF) en Java es una forma eficaz de gestionar tareas complejas de procesamiento de datos e implementar lógica personalizada. Al aprovechar las funciones del lenguaje Java, puede satisfacer mejor requisitos específicos de procesamiento de datos, lo que mejora tanto la eficiencia del desarrollo como el rendimiento del procesamiento. Este tema describe la estructura del código, el uso y ejemplos de las UDTF de Java.
Estructura del código de UDTF
Puede escribir código de UDTF en Java mediante IntelliJ IDEA (Maven) o MaxCompute Studio. El código debe incluir los siguientes componentes:
-
Paquete de Java: opcional.
Puede organizar sus clases de Java en un paquete para facilitar su búsqueda y uso.
-
Extender la clase UDTF: obligatorio.
Las clases requeridas son
com.aliyun.odps.udf.UDTF,com.aliyun.odps.udf.annotation.Resolve(para la anotación@Resolve) ycom.aliyun.odps.udf.UDFException(para los métodos en la clase de Java). Si necesita usar otras clases UDTF o tipos de datos complejos, agregue las clases necesarias según MaxCompute UDF overview. -
Clase de Java personalizada: obligatoria.
Esta es la unidad organizativa del código de la UDTF. Define las variables y los métodos que implementan su lógica de negocio.
-
Anotación
@Resolve: obligatoria.El formato es
@Resolve(<signature>).signaturees una firma de función que define los tipos de datos de los parámetros de entrada y del valor de retorno. Una UDTF no puede obtener la firma de la función mediante reflexión y debe usar la anotación@Resolvepara especificarla; por ejemplo,@Resolve("smallint->varchar(10)"). Para obtener más información sobre la anotación@Resolve, consulte @Resolve annotation. -
Implementar métodos en la clase de Java: obligatorio.
La implementación de la clase de Java incluye los siguientes cuatro métodos. Puede elegir implementarlos según sus necesidades.
API
Descripción
public void setup(ExecutionContext ctx) throws UDFExceptionMétodo de inicialización. MaxCompute invoca su lógica de inicialización personalizada antes de que la UDTF comience a procesar los datos de entrada. El método
setupse llama una vez por cada worker.public void process(Object[] args) throws UDFExceptionLa función
processse invoca una vez por cada registro en una consulta SQL. Los parámetros de la funciónprocessson los parámetros de entrada especificados para la UDTF en la instrucción SQL. Los parámetros de entrada se pasan como una matrizObject[], y la salida se genera mediante la funciónforward. Debe llamar a la funciónforwarddentro de la funciónprocesspara determinar la salida.NotaSi no se llama a
forwarddesde el métodoprocesso close, se pueden perder datos. Por ejemplo, si un hilo en segundo plano ejecuta una llamada aforward, debe asegurarse de que el métodoprocessno finalice hasta que la llamada aforwardse complete, a fin de evitar la pérdida de datos.public void close() throws UDFExceptionMétodo de finalización de la UDTF. Se invoca solo una vez, después de procesar el último registro.
public void forward(Object …o) throws UDFExceptionLlame al método
forwardpara generar datos de salida; cada llamada aforwardproduce un registro. Cuando invoca una UDTF en una consulta SQL, puede usar la cláusulaaspara cambiar el nombre de la salida deforward.Al escribir una UDTF de Java, puede usar tipos de Java o tipos Writable de Java. Para consultar la asignación detallada entre los tipos de datos compatibles con MaxCompute y los tipos de datos de Java, consulte Data types.
A continuación se muestra un ejemplo de 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);
}
}
}
Limitaciones
Acceso a Internet: Las UDF no tienen acceso a Internet de forma predeterminada. Para habilitar el acceso a Internet, complete el formulario de solicitud de conexión de red. Tras la aprobación, el equipo de soporte técnico de MaxCompute se pondrá en contacto con usted para establecer la conexión. Para obtener más detalles, consulte Network connection process.
-
No se permiten otras columnas en la misma instrucción
SELECT: Una instrucciónSELECTque invoque una UDTF no puede hacer referencia a otras columnas ni expresiones. La siguiente instrucción no es válida:-- Invalid: mixes a UDTF with another column select value, user_udtf(key) as mycol ... -
No se permite anidamiento: Las UDTF no se pueden anidar dentro de otras UDTF. La siguiente instrucción no es válida:
-- Invalid: user_udtf2 is nested inside user_udtf1 select user_udtf1(user_udtf2(key)) as mycol...; -
Incompatibilidad con
GROUP BY,DISTRIBUTE BYySORT BY: Una UDTF no puede aparecer en la misma instrucciónSELECTque estas cláusulas. La siguiente instrucción no es válida:-- Invalid: UDTF used with GROUP BY select user_udtf(key) as mycol ... group by mycol;
Consideraciones
Al escribir una UDTF de Java, tenga en cuenta lo siguiente:
Evite definir clases con el mismo nombre pero diferente lógica de implementación en distintos paquetes JAR de UDTF. Por ejemplo, suponga que UDTF1 y UDTF2 corresponden a los recursos de paquetes JAR udtf1.jar y udtf2.jar, respectivamente. Si ambos paquetes JAR contienen una clase llamada
com.aliyun.UserFunction.classcon lógicas diferentes, MaxCompute cargará aleatoriamente una de las clases cuando se invoquen UDTF1 y UDTF2 en la misma instrucción SQL. Esto puede provocar resultados inesperados o fallos de compilación.Los parámetros de entrada y los valores de retorno deben usar tipos de objeto de Java, como
StringyLong, en lugar de tipos primitivos.Esto se debe a que los valores
NULLde SQL se pasan comonullde Java, lo cual no puede representarse con tipos primitivos.
Anotación @Resolve
El formato de la anotación @Resolve es el siguiente.
@Resolve(<signature>)
La cadena signature especifica los tipos de datos de los parámetros de entrada y los valores de retorno de la UDTF. Durante el análisis de la consulta, MaxCompute valida las invocaciones con base en esta firma y reporta un error si detecta una discrepancia de tipos. El formato es el siguiente.
'arg_type_list -> type_list'
Donde:
type_list: representa los tipos de datos de los valores de retorno. Una UDTF puede devolver varias columnas. Los tipos de datos compatibles son BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), tipos de datos complejos (ARRAY, MAP, STRUCT) y tipos de datos complejos anidados.-
arg_type_list: representa los tipos de datos de los parámetros de entrada. Se pueden especificar varios parámetros de entrada separados por comas (,). Los tipos de datos compatibles son BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, VARCHAR, tipos de datos complejos (ARRAY, MAP, STRUCT) y tipos de datos complejos anidados.arg_type_listtambién admite un asterisco (*) o una cadena vacía ('').Si
arg_type_listes un asterisco (*), indica que la función acepta cualquier cantidad de parámetros de entrada.Si
arg_type_listes una cadena vacía (''), indica que la función no tiene parámetros de entrada.
Para obtener más información sobre la sintaxis extendida de la anotación Resolve, consulte Dynamic parameters for UDAFs and UDTFs.
La siguiente tabla muestra ejemplos de anotaciones @Resolve válidas.
|
Ejemplo |
Descripción |
|
|
Los tipos de los parámetros de entrada son BIGINT y BOOLEAN. Los tipos de los valores de retorno son STRING y DATETIME. |
|
|
La función acepta cualquier cantidad de parámetros de entrada. Los tipos de los valores de retorno son STRING y DATETIME. |
|
|
La función no tiene parámetros de entrada. Los tipos de los valores de retorno son DOUBLE, BIGINT y STRING. |
|
|
Los tipos de los parámetros de entrada son ARRAY, STRUCT y STRING. Los tipos de los valores de retorno son MAP y STRUCT. |
Tipos de datos
Los tipos de datos compatibles con MaxCompute varían según la edición de tipos de datos. A partir de MaxCompute 2.0, hay tipos de datos adicionales disponibles, incluidos tipos complejos como ARRAY, MAP y STRUCT. Para obtener más información, consulte Data type editions.
Al escribir una UDTF de Java, asegúrese de que los tipos de datos que utilice se asignen correctamente a los compatibles con MaxCompute. La siguiente tabla describe estas asignaciones.
|
Tipo de MaxCompute |
Tipo de Java |
Tipo Writable de Java |
|
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 |
Para usar tipos Writable de Java en los valores de entrada o retorno de la UDTF, su proyecto de MaxCompute debe utilizar la edición de tipos de datos de MaxCompute 2.0.
Uso
Después de desarrollar una UDTF de Java siguiendo development process, puede invocarla en MaxCompute SQL.
Usar una UDF en un proyecto de MaxCompute: el método es similar al de usar built-in functions. Puede utilizar una función definida por el usuario de la misma manera que usa una función integrada.
Usar una UDF entre proyectos: utilice una UDF del Proyecto B en el Proyecto A. La siguiente instrucción muestra un ejemplo:
select B:udf_in_other_project(arg0, arg1) as res from table_t;. Para obtener más información sobre el uso compartido entre proyectos, consulte Cross-project resource access based on packages.
Para obtener un tutorial completo sobre cómo desarrollar e invocar una UDTF de Java mediante MaxCompute Studio, consulte Usage example.
Ejemplo de uso
El siguiente procedimiento le guía paso a paso para desarrollar e invocar una UDTF de Java mediante MaxCompute Studio:
-
Prepare el entorno.
Antes de poder desarrollar y depurar una UDF en MaxCompute Studio, instale MaxCompute Studio y conéctelo a un proyecto de MaxCompute. Para obtener más información, consulte los siguientes temas:
-
Escriba el código de la UDTF.
En el explorador Project, haga clic con el botón derecho en el directorio de código fuente del módulo () y seleccione .
-
En el cuadro de diálogo Create new MaxCompute java class, haga clic en UDTF, introduzca un Name y presione Enter. Por ejemplo, asigne el nombre MyUDTF a la clase de Java.
Name es el nombre de la clase de Java de MaxCompute que se va a crear. Si aún no se ha creado un paquete, puede introducir packagename.classname aquí para generar automáticamente un paquete.
-
En el editor de código, introduzca el siguiente código. Este es un ejemplo de código de 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); } } }
-
Ejecute y depure la UDTF localmente para asegurar que el código funcione como se espera.
Para obtener más información sobre la depuración, consulte Run and debug a UDF locally.
Haga clic con el botón derecho en el archivo MyUDTF en el árbol del proyecto y seleccione Run 'MyUDTF.main()'. En el cuadro de diálogo Run/Debug Configurations, establezca MaxCompute project en
local, MaxCompute table enwc_in2, Table partition enp2=1,p1=2, Table columns encolc,colb, Download Record limit en100y Data Column Separator en|. Luego, haga clic en OK.NotaPuede usar los parámetros anteriores para la ejecución de ejemplo.
-
Empaquete la UDTF en un paquete JAR, cárguelo en su proyecto de MaxCompute y registre la función. Para este ejemplo, asigne el nombre
user_udtfa la función.Para obtener más información sobre el empaquetado, consulte Procedure.
En el árbol del proyecto de IntelliJ IDEA, haga clic con el botón derecho en el archivo Java de la UDTF (por ejemplo, MyUDTF) y seleccione Deploy to server.... En el cuadro de diálogo Package a jar, submit resource and register function, seleccione el MaxCompute project de destino, confirme la ruta del Resource file, establezca Main class en la clase UDTF correspondiente (por ejemplo,
org.alidata.odps.udtf.examples.MyUDTF), introduzcauser_udtfen Function name, seleccione Force update if already exists y haga clic en OK para completar el despliegue. -
En el panel de navegación izquierdo de MaxCompute Studio, haga clic en Project Explorer. Haga clic con el botón derecho en el proyecto de MaxCompute de destino, inicie el cliente de MaxCompute y ejecute un comando SQL para invocar la UDTF recién creada.
Suponga que la tabla de destino, my_table, contiene los siguientes datos:
+------------+------------+ | col0 | col1 | +------------+------------+ | A B | 1 | | C D | 2 | +------------+------------+Ejecute el siguiente comando SQL para invocar la UDTF.
select user_udtf(col0, col1) as (c0, c1) from my_table;Se devuelve el siguiente resultado.
+----+------------+ | c0 | c1 | +----+------------+ | A | 1 | | B | 1 | | C | 2 | | D | 2 | +----+------------+
Documentación relacionada
Para obtener más ejemplos de uso de UDTF de Java, consulte Java UDTF examples.