Escrever uma função de tabela definida pelo usuário (UDTF) em Java é uma forma eficaz de lidar com tarefas complexas de processamento de dados e implementar lógica personalizada. Ao aproveitar os recursos da linguagem Java, você atende melhor a requisitos específicos de processamento de dados, aumentando tanto a eficiência do desenvolvimento quanto o desempenho do processamento. Este tópico descreve a estrutura de código, o uso e exemplos de UDTFs em Java.
Estrutura de código da UDTF
É possível escrever o código da UDTF em Java usando o IntelliJ IDEA (Maven) ou MaxCompute Studio. O código deve incluir os seguintes componentes:
-
Pacote Java: Opcional.
Organize suas classes Java em um pacote para facilitar a localização e o uso.
-
Estender a classe UDTF: Obrigatório.
As classes necessárias são
com.aliyun.odps.udf.UDTF,com.aliyun.odps.udf.annotation.Resolve(para a anotação@Resolve) ecom.aliyun.odps.udf.UDFException(para métodos na classe Java). Caso precise utilizar outras classes UDTF ou tipos de dados complexos, adicione as classes necessárias conforme MaxCompute UDF overview. -
Classe Java personalizada: Obrigatório.
Esta é a unidade organizacional do código da UDTF. Ela define as variáveis e os métodos que implementam sua lógica de negócios.
-
Anotação
@Resolve: Obrigatório.O formato é
@Resolve(<signature>).signatureé uma assinatura de função que define os tipos de dados dos parâmetros de entrada e do valor de retorno. Uma UDTF não consegue obter a assinatura da função por reflexão e deve usar a anotação@Resolvepara especificá-la, por exemplo,@Resolve("smallint->varchar(10)"). Para obter mais informações sobre a anotação@Resolve, consulte @Resolve annotation. -
Implementar métodos na classe Java: Obrigatório.
A implementação da classe Java inclui os quatro métodos a seguir. Escolha implementá-los conforme suas necessidades.
API
Descrição
public void setup(ExecutionContext ctx) throws UDFExceptionMétodo de inicialização. O MaxCompute chama sua lógica de inicialização personalizada antes que a UDTF comece a processar os dados de entrada. O método
setupé chamado uma vez por worker.public void process(Object[] args) throws UDFExceptionA função
processé chamada uma vez para cada registro em uma consulta SQL. Os parâmetros da funçãoprocesscorrespondem aos parâmetros de entrada especificados para a UDTF na instrução SQL. Esses parâmetros são passados como um arrayObject[], e a saída é gerada por meio da funçãoforward. É obrigatório chamar a funçãoforwarddentro da funçãoprocesspara determinar a saída.NotaDeixar de chamar
forwardno métodoprocessou close pode causar perda de dados. Por exemplo, se uma thread em segundo plano executar uma chamadaforward, garanta que o métodoprocessnão termine até que a chamadaforwardseja concluída, evitando assim a perda de dados.public void close() throws UDFExceptionMétodo de encerramento da UDTF. Ele é chamado apenas uma vez, após o processamento do último registro.
public void forward(Object …o) throws UDFExceptionChame o método
forwardpara gerar dados de saída; cada chamada aforwardproduz um registro. Ao chamar uma UDTF em uma consulta SQL, utilize a cláusulaaspara renomear a saída deforward.Ao escrever uma UDTF em Java, utilize Java Type ou Java Writable Type. Para obter o mapeamento detalhado entre os tipos de dados suportados pelo MaxCompute e os tipos de dados Java, consulte Data types.
Abaixo está um exemplo 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);
}
}
}
Limitações
Acesso à Internet: Por padrão, as UDFs não têm acesso à Internet. Para ative esse acesso, preencha o formulário de solicitação de conexão de rede. Após a aprovação, a equipe de suporte técnico do MaxCompute entrará em contato para estabelecer a conexão. Para mais detalhes, consulte Network connection process.
-
Proibição de outras colunas no mesmo
SELECT: Uma instruçãoSELECTque chame uma UDTF não pode referenciar outras colunas ou expressões. A seguinte instrução é inválida:-- Invalid: mixes a UDTF with another column select value, user_udtf(key) as mycol ... -
Proibição de aninhamento: Não é permitido aninhar UDTFs dentro de outras UDTFs. A seguinte instrução é inválida:
-- Invalid: user_udtf2 is nested inside user_udtf1 select user_udtf1(user_udtf2(key)) as mycol...; -
Incompatibilidade com
GROUP BY,DISTRIBUTE BYeSORT BY: Uma UDTF não pode aparecer na mesma instruçãoSELECTque contenha essas cláusulas. A seguinte instrução é inválida:-- Invalid: UDTF used with GROUP BY select user_udtf(key) as mycol ... group by mycol;
Considerações
Ao escrever uma UDTF em Java, observe os pontos a seguir:
Evite definir classes com o mesmo nome, mas com lógicas de implementação diferentes, em pacotes JAR de UDTF distintos. Por exemplo, suponha que UDTF1 e UDTF2 correspondam aos recursos de pacote JAR udtf1.jar e udtf2.jar, respectivamente. Se ambos os pacotes JAR contiverem uma classe chamada
com.aliyun.UserFunction.classcom lógicas diferentes, o MaxCompute carregará aleatoriamente uma das classes quando UDTF1 e UDTF2 forem chamadas na mesma instrução SQL. Isso pode causar resultados inesperados ou falhas de compilação.Os parâmetros de entrada e os valores de retorno devem usar tipos de objeto Java, como
StringeLong, em vez de tipos primitivos.Isso ocorre porque valores SQL
NULLsão passados comonullem Java, o que tipos primitivos não conseguem representar.
Anotação @Resolve
O formato da anotação @Resolve é o seguinte:
@Resolve(<signature>)
A string signature especifica os tipos de dados dos parâmetros de entrada e dos valores de retorno da UDTF. Durante a análise da consulta, o MaxCompute valida as chamadas com base nessa assinatura e relata um erro caso encontre incompatibilidade de tipos. O formato é o seguinte:
'arg_type_list -> type_list'
Onde:
type_list: Representa os tipos de dados dos valores de retorno. Uma UDTF pode retornar várias colunas. Os tipos de dados suportados incluem BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), tipos de dados complexos (ARRAY, MAP, STRUCT) e tipos de dados complexos aninhados.-
arg_type_list: Representa os tipos de dados dos parâmetros de entrada. É possível especificar múltiplos parâmetros de entrada, separados por vírgulas (,). Os tipos de dados suportados incluem BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, VARCHAR, tipos de dados complexos (ARRAY, MAP, STRUCT) e tipos de dados complexos aninhados.arg_type_listtambém aceita um asterisco (*) ou uma string vazia ('').Se
arg_type_listfor um asterisco (*), indica que a função aceita qualquer número de parâmetros de entrada.Se
arg_type_listfor uma string vazia (''), indica que a função não possui parâmetros de entrada.
Para obter mais informações sobre a sintaxe estendida da anotação Resolve, consulte Dynamic parameters for UDAFs and UDTFs.
A tabela a seguir apresenta exemplos de anotações @Resolve válidas.
|
Exemplo |
Descrição |
|
|
Os tipos dos parâmetros de entrada são BIGINT e BOOLEAN. Os tipos dos valores de retorno são STRING e DATETIME. |
|
|
A função aceita qualquer número de parâmetros de entrada. Os tipos dos valores de retorno são STRING e DATETIME. |
|
|
A função não possui parâmetros de entrada. Os tipos dos valores de retorno são DOUBLE, BIGINT e STRING. |
|
|
Os tipos dos parâmetros de entrada são ARRAY, STRUCT e STRING. Os tipos dos valores de retorno são MAP e STRUCT. |
Tipos de dados
Os tipos de dados suportados pelo MaxCompute variam conforme a edição do tipo de dados. A partir do MaxCompute 2.0, tipos adicionais estão disponíveis, incluindo tipos complexos como ARRAY, MAP e STRUCT. Para mais informações, consulte Data type editions.
Ao escrever uma UDTF em Java, assegure-se de que os tipos de dados utilizados tenham o mapeamento correto para aqueles suportados pelo MaxCompute. A tabela a seguir descreve esses mapeamentos.
|
Tipo MaxCompute |
Tipo Java |
Tipo 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 |
Para usar Java Writable Types como entrada ou retorno de valores da UDTF, seu projeto MaxCompute deve utilizar a edição de tipos de dados do MaxCompute 2.0.
Uso
Após desenvolver uma UDTF em Java seguindo as instruções em development process, chame-a no MaxCompute SQL.
Usar uma UDF em um projeto MaxCompute: O método é semelhante ao uso de built-in functions. Utilize uma função definida pelo usuário da mesma maneira que uma função integrada.
Usar uma UDF entre projetos: Utilize uma UDF do Projeto B no Projeto A. A seguinte instrução mostra um exemplo:
select B:udf_in_other_project(arg0, arg1) as res from table_t;. Para obter mais informações sobre compartilhamento entre projetos, consulte Cross-project resource access based on packages.
Para obter um tutorial completo sobre como desenvolver e chamar uma UDTF em Java usando o MaxCompute Studio, consulte Usage example.
Exemplo de uso
O procedimento a seguir orienta você no desenvolvimento e na chamada de uma UDTF em Java usando o MaxCompute Studio:
-
Prepare o ambiente.
Antes de desenvolver e depurar uma UDF no MaxCompute Studio, instale o MaxCompute Studio e conecte-o a um projeto MaxCompute. Para mais informações, consulte os seguintes tópicos:
-
Escreva o código da UDTF.
No explorador Project, clique em com o botão direito no diretório de source do módulo () e selecione .
-
Na caixa de diálogo Create new MaxCompute java class, clique em UDTF, insira um Name e pressione Enter. Por exemplo, nomeie a classe Java como MyUDTF.
Name corresponde ao nome da classe Java do MaxCompute a ser criada. Se nenhum pacote tiver sido criado, insira packagename.classname neste campo para gerar automaticamente um pacote.
-
No editor de código, insira o código a seguir. Trata-se de um exemplo de código 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); } } }
-
Execute e depure a UDTF localmente para garantir que o código funcione conforme o esperado.
Para obter mais informações sobre depuração, consulte Run and debug a UDF locally.
Clique em com o botão direito no arquivo MyUDTF na árvore do projeto e selecione Run 'MyUDTF.main()'. Na caixa de diálogo Run/Debug Configurations, defina MaxCompute project como
local, MaxCompute table comowc_in2, Table partition comop2=1,p1=2, Table columns comocolc,colb, Download Record limit como100e Data Column Separator como|. Em seguida, clique em OK.NotaUtilize os parâmetros anteriores para a execução de exemplo.
-
Empacote a UDTF em um pacote JAR, faça o upload para o seu projeto MaxCompute e registre a função. Para este exemplo, nomeie a função como
user_udtf.Para obter mais informações sobre empacotamento, consulte Procedure.
Na árvore de projetos do IntelliJ IDEA, clique em com o botão direito no arquivo Java da UDTF (por exemplo, MyUDTF) e selecione Deploy to server.... Na caixa de diálogo Package a jar, submit resource and register function, selecione o MaxCompute project de destino, confirme o caminho do Resource file, defina Main class como a classe UDTF correspondente (por exemplo,
org.alidata.odps.udtf.examples.MyUDTF), insirauser_udtfem Function name, selecione Force update if already exists e clique em OK para concluir a implantação. -
No painel de navegação à esquerda do MaxCompute Studio, clique em Project Explorer. Clique em com o botão direito no projeto MaxCompute de destino, inicie o cliente MaxCompute e execute um comando SQL para chamar a UDTF recém-criada.
Suponha que a tabela de destino, my_table, contenha os seguintes dados:
+------------+------------+ | col0 | col1 | +------------+------------+ | A B | 1 | | C D | 2 | +------------+------------+Execute o seguinte comando SQL para chamar a UDTF.
select user_udtf(col0, col1) as (c0, c1) from my_table;O seguinte resultado é retornado.
+----+------------+ | c0 | c1 | +----+------------+ | A | 1 | | B | 1 | | C | 2 | | D | 2 | +----+------------+
Documentação relacionada
Para obter mais exemplos de uso de UDTFs em Java, consulte Java UDTF examples.