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 = 0
For contadorLinha = 1 : 1 : 1000
{
#Dim linha As %String = ""
#Dim contatorColuna As %Integer = 0
For contatorColuna = 1 : 1 : 4
{
Set linha = $Get(linha) _ "^" _ $Replace($Replace("coluna Y linha X", "Y", contatorColuna), "X", contadorLinha)
}
Set ^CustomQueryTest(contadorLinha) = $Piece(linha, "^", 2, *)
}
}
ObjectScriptObjectScript
Assinatura da Custom Class Query
Query ListarDados() As %Query(ROWSPEC = "ID:%Integer,Coluna1:%String,Coluna2:%String,Coluna3:%String,Coluna4:%String") [ SqlProc ]
{
}
ObjectScriptObjectScript
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 dados
Set qHandle = "^CustomQueryTest"
// Define o último nó acessado
Set qHandle(1) = ""
//
Return $System.Status.OK()
}
ObjectScriptObjectScript
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 retorna
If (indice = "")
{
Set AtEnd = 1
Set Row = ""
//
Return statusCode
}
// Recupera os dados da global
#Dim linha As %String = $Get(@qHandle@(indice))
// Define a linha a ser retorna
Set Row = $ListBuild(indice, $Piece(linha, "^", 1), $Piece(linha, "^", 2), $Piece(linha, "^", 2), $Piece(linha, "^", 2))
// Definie último nó acessado
Set qHandle(1) = indice
//
Return statusCode
}
ObjectScriptObjectScript
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 handle
Kill qHandle
//
Return $System.Status.OK()
}
ObjectScriptObjectScript
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 dados
Set qHandle = "^CustomQueryTest"
// Define o último nó acessado
Set qHandle(1) = ""
//
Return $System.Status.OK()
}
ClassMethod ListarDadosClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = ListarDadosExecute ]
{
// Limpa o handle
Kill 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 retorna
If (indice = "")
{
Set AtEnd = 1
Set Row = ""
//
Return statusCode
}
// Recupera os dados da global
#Dim linha As %String = $Get(@qHandle@(indice))
// Define a linha a ser retorna
Set Row = $ListBuild(indice, $Piece(linha, "^", 1), $Piece(linha, "^", 2), $Piece(linha, "^", 2), $Piece(linha, "^", 2))
// Definie último nó acessado
Set qHandle(1) = indice
//
Return statusCode
}
}
ObjectScriptObjectScript
Exemplo de excução utilizando conexção JDBC com o DBeaver
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