Turbinando Consultas com Custom Class Queries
Quem nunca deve ter passado pela seguinte situação:
Tenho uma aplicação/global de configuração que não está e não pode ser mapeada para uma classe, porém é necessário fornecer uma procedure específica para que uma ferramenta de relatório usando ODBC ou JDBC ou ainda utilização de resultset do prório IRIS, possa ter acesso aos dados e gerar o relatório.
No IRIS temos uma funcionalidade que nos permite criar uma query que pode ser acessada internamente e também ser exposta como uma stored procedure, com nossa própria lógica. Essa funcionalidade é Custom Class Query.
Para definir uma Custom Class Query devemos serguir os seguintes passos (que podemos fazer uma analogia a implementar uma interface):
- Criar a assinatura da Custom Query na Classe com a dfinição dos parâmetros da da especificação das colunas a serem retornadas;
- Implementar o método de classe nomeQueryExecute;
- Implementar o método de classe nomeQueryFetch;
- Implementar o método de classe nomeQueryClose.
Para o nosso exemplo vamos criar uma global hipotética com um nó e 4 pieces. O nó será autoincrementado e os pieces serão alfanuméricos. Abaixo o código para gerar a nossa global de exemplo com 1000 linhas.
PopularGlobal() Public
{
#Dim contadorLinha As%Integer = 0For contadorLinha = 1 : 1 : 1000
{
#Dim linha As%String = ""#Dim contatorColuna As%Integer = 0For contatorColuna = 1 : 1 : 4
{
Set linha = $Get(linha) _ "^" _ $Replace($Replace("coluna Y linha X", "Y", contatorColuna), "X", contadorLinha)
}
Set^CustomQueryTest(contadorLinha) = $Piece(linha, "^", 2, *)
}
}Assinatura da Custom Class Query
Query ListarDados() As%Query(ROWSPEC = "ID:%Integer,Coluna1:%String,Coluna2:%String,Coluna3:%String,Coluna4:%String") [ SqlProc ]
{
}O corpo deve ser deixado em branco. O tipo de retorno deve ser %Query e não %SQLQuery que é utilizada com comandos SQL. O parâmetro ROWSPEC contém os metadados da linha que será retornada no formato NomeColuna:TipoDeDado.
Método ListarDadosExecute
ClassMethod ListarDadosExecute(ByRef qHandle As%Binary) As%Status
{
// Define a global que contém os dadosSet qHandle = "^CustomQueryTest"// Define o último nó acessadoSet qHandle(1) = ""//Return$System.Status.OK()
}Neste método devemos executar a inicialização da nossa Custom Class Query, podemos fazer chamadas à código interno, abrir conexões externas, etc. O parâmetro qHandle é o ponteiro para o "cursor".
Método ListarDadosExecute
ClassMethod ListarDadosFetch(ByRef qHandle As%Binary, ByRef Row As%List, ByRef AtEnd As%Integer = 0) As%Status [ PlaceAfter = ListarDadosExecute ]
{
#Dim statusCode As%Status = $System.Status.OK()
// Recupera o próximo nó a ser acessado#Dim indice As%Integer = $Order(@qHandle@(qHandle(1)))
// Caso não tenha mais dados set aflag indicando que os dados acabaram e retornaIf (indice = "")
{
Set AtEnd = 1Set Row = ""//Return statusCode
}
// Recupera os dados da global#Dim linha As%String = $Get(@qHandle@(indice))
// Define a linha a ser retornaSet Row = $ListBuild(indice, $Piece(linha, "^", 1), $Piece(linha, "^", 2), $Piece(linha, "^", 2), $Piece(linha, "^", 2))
// Definie último nó acessadoSet qHandle(1) = indice
//Return statusCode
}Este é o principal método onde conterá toda a lógica de montagem, transformação, adaptação dos dados a serem retornados. Quando se usa ResultSet por exmplo, este método é chamado toda vez que o método %Next é executado.
- O parâmetro qHandle é o ponteiro para o "cursor" que foi inicializado no método ListarDadosExecute;
- O parâmetro Row é a referência para a linha contendo os dados;
- O parâmetro AtEnd indica se existe ou não mais dados a serem retornados.
Método ListarDadosClose
ClassMethod ListarDadosClose(ByRef qHandle As%Binary) As%Status [ PlaceAfter = ListarDadosExecute ]
{
// Limpa o handleKill qHandle
//Return$System.Status.OK()
}Neste método fazemos a liberação de todos os recursos utilizados como por exemplo: fechar conexões, limpar globais temporárias, liberar arquivos abertos, etc.
Abaixo a definição completa da classe com todos métodos
Class cjs.concurso.CustomQuery
{
Query ListarDados() As%Query(ROWSPEC = "ID:%Integer,Coluna1:%String,Coluna2:%String,Coluna3:%String,Coluna4:%String") [ SqlProc ]
{
}
ClassMethod ListarDadosExecute(ByRef qHandle As%Binary) As%Status
{
// Define a global que contém os dadosSet qHandle = "^CustomQueryTest"// Define o último nó acessadoSet qHandle(1) = ""//Return$System.Status.OK()
}
ClassMethod ListarDadosClose(ByRef qHandle As%Binary) As%Status [ PlaceAfter = ListarDadosExecute ]
{
// Limpa o handleKill qHandle
//Return$System.Status.OK()
}
ClassMethod ListarDadosFetch(ByRef qHandle As%Binary, ByRef Row As%List, ByRef AtEnd As%Integer = 0) As%Status [ PlaceAfter = ListarDadosExecute ]
{
#Dim statusCode As%Status = $System.Status.OK()
// Recupera o próximo nó a ser acessado#Dim indice As%Integer = $Order(@qHandle@(qHandle(1)))
// Caso não tenha mais dados set aflag indicando que os dados acabaram e retornaIf (indice = "")
{
Set AtEnd = 1Set Row = ""//Return statusCode
}
// Recupera os dados da global#Dim linha As%String = $Get(@qHandle@(indice))
// Define a linha a ser retornaSet Row = $ListBuild(indice, $Piece(linha, "^", 1), $Piece(linha, "^", 2), $Piece(linha, "^", 2), $Piece(linha, "^", 2))
// Definie último nó acessadoSet qHandle(1) = indice
//Return statusCode
}
}
Exemplo de excução utilizando conexção JDBC com o DBeaver
.png)
Custom Class Query é uma ferramenta extremamente poderosa, podemos utilizá-la em diversas outras situações como:
- Queries com lógica extremamamente complexas;
- Acesso à fontes de dados externas como arquivos, chamadas à serviços REST, SOAP, etc;
- Requisitos especifícos de seguraça;
- Data mushup;
- Entre outros.
Qualquer dúvida ou sugestão por favor não exitem em interagir.
Para mais informações consulte a documentação Defining Custom Class Queries