Nilton Paulino Segunda-feira, 17 de abril de 2006
Acesso a dados com a classe CursorAdapter
Desde o princípio o FoxPro é famoso por seu poder de acesso a dados. Inicialmente, impressionava pela velocidade e facilidade em fazê-lo. Hoje em dia, há muito mais que isso. A flexibilidade sem dúvida impressiona muito mais. Teoricamente diz-se que através do Visual FoxPro pode-se acessar qualquer tipo de fonte de dados, desde que essa tenha conectividade através de ODBC, OLEDB ou ADO. No entanto, praticamente qualquer linguagem é capaz de fazê-lo também. Mas na prática temos visto que o Visual FoxPro é capaz de processar dados remotos (de outras fontes que não seja o DBC) com uma performance muito superior a outras linguagens.
No Visual FoxPro 8.0 foi introduzido um novo conceito de acesso a dados. Trata-se de uma classe orientada a objetos chamada CursorAdapter.
Através da classe CursorAdapter, que traduzido para o português quer dizer adaptador de cursores, podemos trabalhar com dados das mais diversas fontes sem nos preocupar muito com os detalhes da tecnologia do banco de dados remoto, ou mesmo em aprender os recursos do Driver ODBC ou OLEDB que provêem acesso à fonte de dados remota.
A classe encapsula o conhecimento sobre como transformar os dados remotos em dados locais do Visual FoxPro. Isso mesmo! O dado remoto é transformado em um cursor, o que facilita o trabalho do desenvolvedor. Quando da necessidade de enviar os dados do cursor para a fonte remota, a classe CursorAdapter sabe muito bem o que fazer. Prepara os dados, e envia para a fonte, assegurando que sejam gravados corretamente.
Antes da versão 8.0 do VFP precisávamos escrever nossas próprias classes de acesso a dados para trabalhar com fontes de dados externas. Geralmente utilizava-se objetos RecordSet da biblioteca ADO para acesso orientado a objetos. Para acesso através de programação não OOP, utilizava-se outros recursos como Remote Views e SQL Pass- Through.
Um aplicativo construído com Remote Views usando um banco de dados MS SQL Server, se necessário alterar para uso com Oracle teria no mínimo que refazer as Views. Com o uso de CA’s (CursorAdapter’s) as alterações seriam bem menos traumáticas. Como as CA’s são classes OOP, é totalmente possível criarmos classes mais especializadas capazes de agregar novos recursos e até mesmo novas funcionalidades, fazendo com que o acesso a dados em VFP continue sendo insuperável por qualquer outra ferramenta.
Através da classe CursorAdapter podemos acessar dados das seguintes fontes de dados:
. Nativa (DBF/DBC) . ODBC
. ADO
. XML
Vejamos o que significa cada tipo de fonte de dados:
NATIVE -Indica que o objeto CursorAdapter trabalhará com dados nativos do Visual FoxPro (banco DBC/tabelas DBF).
ODBC - Indica que a classe CursorAdapter trabalhará com fontes de dados que serão acessadas através de drivers ODBC. É possível acessar dados remotos numa rede ou fora dela.
ADO - Indica que a classe CursorAdapter usará a biblioteca ADO para acessar a fonte de dados. A biblioteca ADO trabalha com biblioteca de drivers OLEDB, que são mais atualizados que os drivers ODBC.
XML - Indica que a classe CursorAdapter trabalhará com documentos XML como fonte de dados.
Quando usando fontes de dados ODBC e ADO, necessita-se de uma conexão estabelecida previamente com o banco de dados.
Para ODBC, podemos estabelecer a conexão através das funções SQLCONNECT() e SQLSTRINGCONNECT(). Veja posteriormente ainda neste capítulo as definições para estas funções.
Principais PEM's da classe CursorAdapter
Antes de prosseguirmos, vamos conhecer as principais propriedades, eventos e métodos da classe CursorAdapter. A partir do conhecimento das PEM's desta classe, poderemos melhor aproveita-la.
Eventos do Objeto CursorAdapter
Evento Descrição
AfterCursorAttach Ocorre imediatamente depois que o método CursorAttach é executado.
AfterCursorClose Ocorre depois que o objeto CursorAdapter é fechado
AfterCursorDetach Ocorre imediatamente depois que o método CursorDetach é executado.
AfterCursorFill Ocorre depois que o método CursorFill é executado.
AfterCursorRefresh Ocorre imediatamente após o método CursorRefresh ser executado.
AfterCursorUpdate Ocorre depois que qualquer dado do cursor seja atualizado através da função TABLEUPDATE().
AfterDelete Ocorre sempre depois que um comando DELETE seja executado.
AfterInsert Ocorre sempre depois que um comando INSERT é executado.
AfterUpdate Ocorre imediatamente depois que uma operação de atualização é executada para um registro.
BeforeCursorAttach Ocorre imediatamente antes que o objeto CursorAdapter tente unir (attach) um cursor. Você pode usar este evento para executar todas as operações necessárias antes de unir (attach) o cursor ou tabela, determinar um apelido (alias) para a união (attach), e assim por diante.
BeforeCursorClose Ocorre imediatamente antes que um cursor se feche. Você pode usar este evento para executar todas as operações necessárias antes de fechar um cursor ou tabela, e assim por diante.
BeforeCursorDetach Ocorre antes do método CursorDetach ser executado.
Você pode executar este evento para executar todas as operações necessárias antes de desanexar o cursor ou a tabela.
BeforeCursorFill Ocorre imediatamente antes do método CursorFill ser executado.
BeforeCursorRefresh Ocorre antes do método CursorRefresh ser executado.
BeforeCursorUpdate Ocorre imediatamente antes da função TABLEUPDATE() ser executada.
BeforeDelete Ocorre imediatamente antes que um comando de deleção de registro seja
Principais Propriedades do Objeto CursorAdapter executado.
BeforeInsert Ocorre imediatamente antes de um comando de inserção de registro ser executado.
BeforeUpdate Ocorre imediatamente antes de um comando de atualização para um registro.
Propriedade Descrição
Alias Especifica um apelido para a tabela a ser gerada pelo objeto CursorAdapter.
AllowDelete Especifica se os dados apagados num cursor são submetidos para a origem de dados.
AllowInsert Especifica se os dados inseridos no cursor são enviados para a origem de dados.
AllowUpdate Especifica se os dados modificados num cursor associado a um objeto CursorAdapter são enviados para a origem de dados.
ConversionFunc Especifica funções para conversão de dados.
CursorSchema Especifica o formato de dados para o cursor local gerado.
DataSource Especifica a conexão com a origem de dados.
DataSourceType Especifica o tipo de origem de dados.
KeyFieldList Especifica a lista de campos chave para o CursorAdapter.
SelectCmd Especifica o comando para recuperar (consultar) dados da origem de dados.
SendUpdates Especifica se os dados resultantes de uma consulta em um objeto
Principais Métodos do Objeto CursorAdapter
CursorAdapter são enviados para a origem de dados.
Tables Especifica quais tabelas devem ser atualizadas para operações de inserção, alteração ou remoção quando um objeto CursorAdapter aceita atualizações. Quando mais de uma tabela, separa-las por vírgula.
UpdatableFieldList Lista de campos atualizáveis.
UpdateNameList Lista contendo os nomes dos campos atualizáveis em pares identificando o campo na tabela loca e o respectivo campo na tabela remota.
Método Descrição
AutoOpen Executa o método CursorFill sem passar nenhum parâmetro padrão.
Quando o objeto CursorAdapter está dentro do ambiente de dados de um formulário, o ambiente de dados chama o método AutoOpen automaticamente dentro do método OpenTables do ambiente de dados.
CursorAttach Anexa um cursor existente à uma instância já existe de um objeto CursorAdapter.
CursorDetach Libera um cursor anexado à uma instância do objeto CursorAdapter sendo executado.
CursorFill Executa o comando da propriedade SelectCmd de acordo com o DataSource selecionado, cria um cursor com os dados recuperados, executa a conversão de dados necessária de acordo com as propriedades DataSourceType e CursorSchema.
CursorRefresh Atualiza um cursor com os dados atuais da origem de dados.
Acessando dados nativos (DBC/DBF) com CursorAdapter
Uma boa forma de iniciar os estudos da classe CursorAdapter é utilizando os dados nativos do Visual FoxPro. Sim, aqueles que geralmente usamos comandos xBase como OPEN DATABASE, USE, etc., para abrir, o banco de dados.
Vejamos o exemplo da listagem 1 a seguir:
Listagem 1:
LOCAL loCAClientes AS CursorAdapter
OPEN DATABASE _Samples + "Northwind\Northwind.dbc"
*!* Instancia a classe CursorAdap
loCAClientes = CREATEOBJECT("CursorAdapter")
loCAClientes.DataSourceType = "NATIVE" && Banco nativo loCAClientes.DataSource = "" && Origem de dados
loCAClientes.Alias = "Clientes"&& Define o Alias loCAClientes.SelectCmd = "SELECT * FROM Customers"
IF loCAClientes.CursorFill()
BROWSE FONT 'Courier New',12 && Exibe os dados da tabela ELSE
MESSAGEBOX("Cursor não criado!",0+16,"Erro") ENDIF
CLOSE DATABASES ALL
Ao executar o exemplo acima teremos o seguinte resultado:
Figura 1 - Cursor criado pelo objeto loCAClientes
Examinando o código da listagem 1 podemos verificar as seguintes ações:
1. OPEN DATABASE _Samples + "Northwind\Northwind.dbc" - usamos um commando nativo do VFP para abrir o banco de dados Northwind.dbc.
2. loCAClientes = CREATEOBJECT("CursorAdapter") - instanciamos a classe CursorAdapter nativa do Visual FoxPro.
3. Definimos as propriedades mínimas para o funcionamento de nossa consulta:
loCAClientes.DataSourceType= "NATIVE" && Banco nativo loCAClientes.DataSource = "" && Origem de dados
loCAClientes.Alias = "Clientes" && Define o Alias loCAClientes.SelectCmd = "SELECT * FROM Customers"
4. Finalmente executamos o método responsável por enviar a solicitação da consulta ao banco de dados e criar o cursor resultante:
loCAClientes.CursorFill()
Como é notório, não é nada complicado o uso básico da classe CursorAdapter. É importante lembrar que a estamos utilizando a partir de um PRG. Para uso em um formulário há alguns cuidados a tomar que serão tratados posteriormente, em outro artigo.
Embora o cursor gerado com o código da listagem 1 seja de leitura e escrita, os dados alterados não serão enviados para a origem de dados, ou seja, nenhuma atualização será realizada nos dados originais. A seguir, veremos como tornar a origem de dados atualizável.
Atualizando os dados na Origem de Dados
No exemplo anterior fizemos uma consulta a uma origem de dados nativa. Embora o cursor resultante seja de leitura e escrita, qualquer alteração que fizermos em seus dados, não será enviada para a origem de dados. Para que essas modificações reflitam no banco de dados de origem é necessário preencher corretamente as propriedades SendUpdates, Tables, KeyFieldList, UpdatableFieldList, UpdateNameList.
Na próxima listagem de código, denominada listagem 2, partimos do código da listagem 1, acrescentando as linhas de código necessárias para enviar os dados alterados para a origem de dados.
Listagem 2:
LOCAL loCAClientes AS CursorAdapter
OPEN DATABASE _Samples + "Northwind\Northwind.dbc"
*!* Instancia a classe CursorAdapter
loCAClientes = CREATEOBJECT("CursorAdapter")
loCAClientes.DataSourceType = "NATIVE" && Banco nativo loCAClientes.DataSource = "" && Origem de dados
loCAClientes.Alias = "Clientes"&& Define o Alias loCAClientes.SelectCmd = "SELECT * FROM Customers"
*!* As linhas a seguir tornam a origem de dados atualizável loCAClientes.SendUpdates = .T.
loCAClientes.Tables = "Customers"
loCAClientes.KeyFieldList = "CustomerID"
loCAClientes.UpdatableFieldList="CompanyName, ContactName, Country"
loCAClientes.UpdateNameList = "CustomerID Customers.CustomerID, "+;
"CompanyName Customers.CompanyName, "+;
"ContactName Customers.ContactName, "+;
"Country Customers.Country"
*!* As linhas a seguir tornam a origem de dados atualizável IF loCAClientes.CursorFill()
BROWSE FONT 'Courier New',12 && Exibe os dados da tabela ELSE
MESSAGEBOX("Cursor não criado!",0+16,"Erro")
ENDIF
CLOSE DATABASES ALL
A diferença do código da listagem 2 em relação do código da listagem 1 são as linhas de código a seguir:
*!* As linhas a seguir tornam a origem de dados atualizável
loCAClientes.SendUpdates = .T.
loCAClientes.Tables = "Customers"
loCAClientes.KeyFieldList = "CustomerID"
loCAClientes.UpdatableFieldList="CompanyName, ContactName, Country"
loCAClientes.UpdateNameList = "CustomerID Customers.CustomerID, "+;
"CompanyName Customers.CompanyName, "+;
"ContactName Customers.ContactName, "+;
"Country Customers.Country"
Observe que as propriedades SendUpdates, Tables, KeyFieldList, UpdatableFieldList, UpdateNameList que foram citadas anteriormente como sendo pré-requisito para que os dados sejam atualizados na origem de dados estão presentes nessas linhas de código e consequentemente os dados podem ser atualizados na origem de dados. Para confirmar o que estou afirmando aqui, o melhor a fazer é executar o código e realizar qualquer alteração sobre os dados das colunas CompanyName, ContactName e Country. Qualquer alteração realizada em qualquer outra coluna não será enviada para a origem de dados, uma vez que estas não estão listadas nas propriedades UpdatableFieldList e UpdateNameList. Veja a figura 2 com dados alterados na coluna CompanyName, onde acrescentei Brazil ao final. Para que a alteração seja enviada para a origem de dados basta movimentar o ponteiro do registro para o próximo registro.
Figura 2 - Dados alterados no cursor são enviados para a origem de dados
Analisemos agora o que cada linha de código adicionada neste PRG realiza efetivamente.
1. Na linha de código a seguir, dizemos ao objeto loCAClientes que qualquer alteração deve ser enviada para a origem de dados. No entanto, o sucesso do envio dessas alterações dependerá da definição das demais propriedades que discutiremos a nos próximos tópicos:
loCAClientes.SendUpdates = .T.
2. Agora especificamos qual é a tabela que deve ser alterada na origem de dados:
loCAClientes.Tables = "Customers"
3. Aqui indicamos qual é o campo chave da tabela:
loCAClientes.KeyFieldList = "CustomerID"
4. A seguir passamos a lista de campos que queremos que sejam atualizáveis na origem de dados. Cada campo é separado por vírgula:
loCAClientes.UpdatableFieldList="CompanyName, ContactName, Country"
5. A última propriedade é responsável por fazer um mapeamento dos campos do cursor gerado pelo objeto CursorAdapter com os campos da tabela na origem de dados, onde o campo do lado esquerdo do par representa o campo do cursor e a seqüência nometabela.nomecampo representa a tabela e o campo na origem da dados:
loCAClientes.UpdateNameList= "CustomerID Customers.CustomerID, "+;
"CompanyName Customers.CompanyName, "+;
"ContactName Customers.ContactName, "+;
"Country Customers.Country"
Até aqui nada complicado. Diria que torna-se um pouco tedioso e meio repetitivo a tarefa de informar os dados das propriedades UpdatableFieldList e UpdateNameList, mas fora isso, o processo é bastante simples.
Nossa próxima tarefa é aprender a lidar com um objeto CursorAdapter dentro de um formulário, Mas este é um assunto para o próximo artigo.