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ógicas personalizadas. Ao aproveitar os recursos da linguagem Java, você atende melhor aos requisitos específicos de processamento de dados, o que melhora tanto a eficiência do desenvolvimento quanto o desempenho do processamento. Este tópico descreve a estrutura do código, o uso e os exemplos de UDTFs em Java.
Estrutura de código da UDTF
Você pode 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.
Você pode organizar 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 usar outras classes UDTF ou tipos de dados complexos, adicione as classes necessárias de acordo com 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 a sua lógica de negócios.
-
Anotação
@Resolve: Obrigatório.O formato é
@Resolve(<signature>). Osignatureé 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 pode 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 de acordo com as suas necessidades.
API
Descrição
public void setup(ExecutionContext ctx) throws UDFExceptionO método de inicialização. O MaxCompute chama a 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çãoprocesssão os parâmetros de entrada especificados para a UDTF na instrução SQL. Os parâmetros de entrada são passados como um arrayObject[], e a saída é gerada usando a funçãoforward. Você deve chamar a funçãoforwarddentro da funçãoprocesspara determinar a saída.NotaA falha ao chamar
forwarda partir do métodoprocessou close pode causar perda de dados. Por exemplo, se uma thread em segundo plano executar uma chamadaforward, você deve garantir que o métodoprocessnão termine até que a chamadaforwardseja concluída, a fim de evitar a perda de dados.public void close() throws UDFExceptionO mé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, em que cada chamada aoforwardgera um registro. Ao chamar uma UDTF em uma consulta SQL, use a cláusulaaspara renomear a saída doforward.Ao escrever uma UDTF em Java, você pode usar Java Type ou Java Writable Type. Para obter um mapeamento detalhado entre os tipos de dados suportados pelo MaxCompute e os tipos de dados Java, consulte Data types.
A seguir, 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 podem acessar a Internet. Ative o acesso à Internet preenchendo 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 com você para estabelecer a conexão. Para obter detalhes, consulte Network connection process.
-
Nenhuma outra coluna no mesmo
SELECT: Uma instruçãoSELECTque chama uma UDTF não pode fazer referência a outras colunas ou expressões. A instrução a seguir é inválida:-- Invalid: mixes a UDTF with another column select value, user_udtf(key) as mycol ... -
Sem aninhamento: As UDTFs não podem ser aninhadas dentro de outras UDTFs. A instrução a seguir é inválida:
-- Invalid: user_udtf2 is nested inside user_udtf1 select user_udtf1(user_udtf2(key)) as mycol...; -
Incompatível com
GROUP BY,DISTRIBUTE BYeSORT BY: Uma UDTF não pode aparecer na mesma instruçãoSELECTque essas cláusulas. A instrução a seguir é 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 o seguinte:
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 a UDTF1 e a 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 os valores
NULLdo SQL são passados comonulldo Java, o que os tipos primitivos não podem 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 em relação a essa assinatura e relata um erro se encontrar uma incompatibilidade de tipo. O formato é o seguinte.
'arg_type_list -> type_list'
Em que:
type_list: Representa os tipos de dados dos valores de retorno. Uma UDTF pode retornar várias colunas. Os tipos de dados suportados são 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. Especifique vários parâmetros de entrada, separados por vírgulas (,). Os tipos de dados suportados são 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.O
arg_type_listtambém aceita um asterisco (*) ou uma string vazia ('').Se o
arg_type_listfor um asterisco (*), isso indica que a função aceita qualquer número de parâmetros de entrada.Se o
arg_type_listfor uma string vazia (''), isso 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 mostra exemplos de anotações @Resolve válidas.
|
Exemplo |
Descrição |
|
|
Os tipos de parâmetro de entrada são BIGINT e BOOLEAN. Os tipos de valor de retorno são STRING e DATETIME. |
|
|
A função aceita qualquer número de parâmetros de entrada. Os tipos de valor de retorno são STRING e DATETIME. |
|
|
A função não possui parâmetros de entrada. Os tipos de valor de retorno são DOUBLE, BIGINT e STRING. |
|
|
Os tipos de parâmetro de entrada são ARRAY, STRUCT e STRING. Os tipos de valor de retorno são MAP e STRUCT. |
Tipos de dados
Os tipos de dados suportados pelo MaxCompute variam de acordo com a edição do tipo de dados. A partir do MaxCompute 2.0, tipos de dados adicionais estão disponíveis, incluindo tipos complexos como ARRAY, MAP e STRUCT. Para obter mais informações, consulte Data type editions.
Ao escrever uma UDTF em Java, certifique-se de que os tipos de dados usados sejam mapeados corretamente para aqueles suportados pelo MaxCompute. A tabela a seguir descreve esses mapeamentos.
|
Tipo do 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 para valores de entrada ou retorno da UDTF, o seu projeto MaxCompute deve usar a edição de tipo de dados do MaxCompute 2.0.
Uso
Após desenvolver uma UDTF em Java seguindo o development process, você pode chamá-la no MaxCompute SQL.
Usar uma UDF em um projeto do MaxCompute: O método é semelhante ao uso de built-in functions. Use uma função definida pelo usuário da mesma forma que você usa uma função integrada.
Usar uma UDF entre projetos: Use uma UDF do Projeto B no Projeto A. A instrução a seguir mostra um exemplo:
select B:udf_in_other_project(arg0, arg1) as res from table_t;. Para obter mais informações sobre o compartilhamento entre projetos, consulte Cross-project resource access based on packages.
Para obter um passo a passo 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 do MaxCompute. Para obter mais informações, consulte os seguintes tópicos:
-
Escreva o código da UDTF.
No explorador Project, clique em (diretório source) com o botão direito do mouse 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.
O Name é o nome da classe Java do MaxCompute a ser criada. Se um pacote ainda não tiver sido criado, insira packagename.classname aqui para gerar um pacote automaticamente.
-
No editor de código, insira o código a seguir. Este é um exemplo 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); } } }
-
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 Execute e depure uma UDF localmente.
Clique em MyUDTF com o botão direito do mouse na árvore do projeto e selecione Execute '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, Limite de baixe de registros como100e Data Column Separator como|. Em seguida, clique em OK.NotaVocê pode usar 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 do 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 MyUDTF com o botão direito do mouse e selecione Deploy to server.... Na caixa de diálogo Empacote um jar, envie o recurso e registre a função, selecione o MaxCompute project de destino, confirme o caminho do Resource file, defina a Main class como a classe UDTF correspondente (por exemplo,
org.alidata.odps.udtf.examples.MyUDTF), insirauser_udtfpara Function name, selecione Forçar atualize se já existir e clique em OK para concluir a implantação. -
No painel de navegação à esquerda do MaxCompute Studio, clique em Project Explorer. Clique em MaxCompute project com o botão direito do mouse, inicie o cliente do 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.