O Microsof Jet Database Engine e o VB

O Microsoft Jet e os Tipos de Dados

O Jet suporta uma grande quantidade de diferentes tipos de dados, garantindo total compatibilidade tanto com o Microsoft Access quanto com os tipos de dados definidos pelo padrão ANSI da SQL. A figura abaixo ilustra os tipos de dados disponíveis no mecanismo Jet e no padrão SQL associado, além de sua compatibilidade com os diversos tipos de dados do VB.

Figura 14.0 Os tipos de dados suportados pelo Microsoft Jet

Modos de Interação da Aplicação com o Microsoft Jet Engine

O Visual Basic oferece duas formas de comunicação entre aplicações e o mecanismo Jet: o controle Data e os objetos de acesso a dados (DAO).

O controle Data faz parte da caixa de ferramentas padrão e pode ser inserido em formulários como qualquer outro controle. Seu papel é fazer intermediação entre a aplicação e um banco de dados de modo a automatizar tarefas comuns como inclusões, alterações, exclusões e navegação em meio à massa de registros.

Enquanto o controle Data lhe dá poder para acessar bancos de dados existentes com pouca programação, o modelo DAO é uma completa interface de programação que lhe dá total controle sobre o banco de dados. Esses dois recursos, porém, não são mutuamente exclusivos, podendo em muitas ocasiões até mesmo ser desejável combiná-los. Trataremos de início dos objetos de acesso a dados por ser um estudo que nos dará elementos para compreender melhor o uso do controle Data.

Biblioteca de Objetos de Acesso a Dados (DAO)

Um dos mais importantes recursos do Visual Basic é a sua biblioteca de objetos de acesso a dados ou DAO (Data Access Objects). São objetos que você pode criar em tempo de execução para acessar dados tanto em arquivos de banco de dados do Access - o banco de dados nativo do Visual Basic - como em outros tipos de banco de dados locais como dBase, Paradox e também acessar dados em bancos de dados cliente-servidor como o Microsoft SQL Server usando a tecnologia ODBC.

Os objetos de acesso a dados estão organizados numa hierarquia, na qual muitos objetos pertencem a coleções de objetos, que também, por sua vez, pertencem a outros objetos que lhes estão acima na ordem hierárquica. A completa hierarquia da DAO está ilustrada abaixo.

Figura 14.1: A hierarquia da DAO

Estamos usando a palavra objeto para nos referirmos aos data access objects, mas na verdade não são objetos e sim de classes. Do mesmo modo como você cria instâncias de suas próprias classes, você o faz com os objetos de acesso a dados. Veremos isso nos exemplos. Antes faremos uma breve introdução aos objetos da DAO.

O Objeto DBEngine

No topo da hierarquia da DAO está o objeto DBEngine e corresponde ao dispositivo Microsoft Jet de acesso a dados. O objeto DBEngine é usado para ajustar os parâmetros do sistema de banco de dados utilizado pelo VB e para definir a área de trabalho default. O DBEngine é o único objeto que você não pode instanciar. Ele é criado automaticamente quando sua aplicação faz a primeira referência a um objeto da DAO.

O Objeto Workspace

Logo abaixo do objeto DBEngine, na hierarquia da DAO, está o objeto Workspace (área de trabalho). Esse objeto é utilizado para criar e identificar uma sessão de trabalho para um usuário. Ele contém os bancos de dados abertos e provê controle sobre transações simultâneas e segurança de acesso.

Um objeto Workspace default - Workspace(0) - é criado automaticamente na primeira referência a um objeto DAO pela aplicação. Ele pode ser inicializado com um Username (nome de usuário) e com uma Password (senha). Ao contrário de outros objetos Workspace, a área de trabalho default está sempre disponível, não podendo ser fechada nem removida da coleção Workspaces.

Objeto Database

O objeto Database representa um banco de dados, nativo do Jet ou não. É utilizado para definir as tabelas do banco de dados, relacionamentos entre tabelas, consultas armazenadas, além de permitir a abertura de objetos Recordset - que veremos mais adiante.

Objeto TableDef

O objeto TableDef corresponde a uma definição de tabela armazenada. Cada TableDef da coleção TableDefs representa a definição de uma tabela no banco de dados corrente, ou uma tabela externa anexada à base de dados. Neste último caso o objeto TableDef não pode ter seus atributos alterados.

Objeto QueryDef

O objeto QueryDef representa uma consulta escrita em linguagem SQL armazenada no banco de dados. Uma consulta armazenada é um comando SQL pré-compilado. É possível ler e modificar o código SQL de um objeto QueryDef, ajustar seus parâmetros e, então , executar a consulta.

Objeto Recordset

O objeto Recordset corresponde a uma visão ponteirada de uma tabela ou do conjunto de registros resultantes de uma consulta a uma ou mais tabelas. Uma visão ponteirada é o armazenamento de registros numa área de memória (buffer), apontando para um registro de cada vez, chamado registro corrente. O ponteiro (registro corrente) pode ser reposicionado em qualquer registro utilizando-se os métodos move, seek ou find. Ele permite a navegação, atualização e remoção das tabelas subjacentes ao recordset. Como a visão dos dados de um banco, o Recordset não contém ele mesmo os dados: apenas aponta para eles. Nesse sentido não é armazenado no banco, tendo utilização temporária. Quando um recordset é fechado, ele é removido da coleção Recordsets e todos os recursos associados são removidos da memória.

É possível a criação de três tipos de recordsets:

Tipo tabela: Um recordset tipo tabela corresponde a uma tabela do banco de dados. Quando um recordset desse tipo é aberto, o programador passa a ter acesso à tabela subjacente.

Tipo dynaset: Um recordset do tipo dynaset corresponde a uma tabela "virtual" resultante de uma consulta feita a uma ou mais tabelas. Ao ser atualizada, todas as alterações feitas no objeto se refletem imediatamente nas tabelas subjacentes.

Tipo snapshot: Um recordset do tipo snapshot é um conjunto estático de dados, similar ao dynaset porém não admite atualização. Outra diferença é que os snapshots têm o conjunto de registros carregado na memória. Se de um lado isso os torna mais rápidos, por outro cria o problema de haver conflitos com limitações de memória. Ajustam-se melhor para pequenas quantidades de registros que não precisam ser editados.

Objeto Field

O objeto Field corresponde a uma coluna de dados contendo um tipo de dado comum e um conjunto de atributos. É apenas um campo do banco de dados. Nesse sentido, os objetos TableDef, QueryDef, Recordset, e Index possuem coleções Fields. A coleção de objetos Field associada ao ponteiro de um recordset descreve um registro. Os dados do recordset são lidos e atualizados através da propriedade Value do objeto Field. Quando o ponteiro do recordset é movido e passa a apontar para um novo registro, todos os objetos Field da coleção Fields são automaticamente atualizados.

Objeto Index

O objeto Index representa um índice associado a um objeto TableDef ou Recordset, sempre do tipo tabela. O Índice corrente pode ser especificado num recordset tabela atribuindo-se um dos índices da coleção Indexes à sua propriedade Index. A atribuição da propriedade Index permite reordenar rapidamente os registros de uma tabela.

Objeto Parameter

O objeto Parameter representa um parâmetro de consulta armazenada no banco de dados. A coleção Parameters dos objetos QueryDef e Recordset permite ao programador acessar as informações contidas no parâmetro da consulta .

 

Segurança de Acesso

Objeto User

O objeto User é utilizado para definir e garantir a segurança do banco de dados. Adicionando ou removendo membros à coleção Users, criam-se ou removem-se contas de acesso para os usuários do sistema. Cada objeto User é criado com um nome uma senha. As permissões de acesso aos objetos do sistema, tais como TableDef e QueryDef, podem ser definidas para os usuários individualmente.

Objeto Group

Um objeto Group representa uma coleção de usuários com os mesmos direitos de acesso. O objeto DBEngine suporta um conjunto de grupos no acesso ao sistema. Cada usuário dentro de um grupo herda as permissões de acesso a todos os objetos acessados pelo Group.

Objeto Container

Os objetos Container são utilizados em conjunto com os objetos Document para enumerar todos os objetos armazenados no banco de dados, incluindo aqueles definidos por aplicações clientes. O principal uso dos objetos Container é enumerar todos os objetos armazenados no banco de dados , definindo permissões de acesso e usuários proprietários.

Objeto Document

Os objetos Document são objetos de um tipo comum que partilham um Container. Permissões de acesso podem ser definidas para um objeto Document, alterando privilégios de segurança.

Objeto Relation

O objeto Relation é utilizado para definir o relacionamento entre campos de dois objetos TableDef. Cada objeto Database possui uma única coleção de objetos Relation. O Jet pode forçar determinadas condições de atualização dos dados associados aos campos dos objetos Relation visando manter a integridade referencial dos dados do banco. A adição ou remoção de objetos Relation cria ou remove relações entre tabelas do banco de dados.

Objeto Property

O objeto Property representa uma propriedade associada a um objeto do banco de dados. Tanto as propriedades "intrínsecas" quanto as propriedades definidas pelo programador são armazenadas na coleção Properties associada ao objeto. É possível criar novas propriedades adicionando novos objetos Property à coleção Properties. Tais propriedades podem ser armazenadas no banco de dados e referem-se somente ao objeto especificado e não aos demais objetos da mesma classe.

Os seguintes objetos DAO suportam a definição de novas propriedades:

Database

TableDef

QueryDef

TableDef.Index

TableDef.Field

QueryDef.Field

Praticando com os Objetos da DAO

É o momento de colocar em prática os inúmeros conceitos estivemos expondo. Usaremos a versão de 32 bits na criação das tabelas. Assim, não deixe de incluir uma referência à Microsoft DAO 3.5 Object Library na opção References do menu Project do VB5.

A Criação do Banco de Dados

O primeiro passo será a criação de um arquivo no formato Microsoft Access. Normalmente os programadores criam o banco de dados no Microsoft Access e não no VB. É muito mais cômodo utilizar a interface amigável do Access para criar todas as tabelas, campos, consultas, ... do que escrever código em VB para construir a estrutura do banco de dados. Como, no entanto, o programador de VB deve conhecer bem as relações entre os objetos da DAO, o estudo dos meios de construção de um banco de dados por código é de grande importância.

Sempre que um arquivo de banco de dados é aberto - ou criado - é necessário que especifiquemos uma área de trabalho onde abri-lo. Caso nenhuma área seja definida, o VB, automaticamente, abre o arquivo na área de trabalho default, endereçada pelo índice 0 da coleção Workspaces do objeto DBEngine. No entanto, é sempre boa prática de programação a definição explícita de uma área de trabalho.

A criação do banco de dados pode ser feita pelo método CreateDatabase( ) do objeto Workspace. A referência retornada pelo método pode ser utilizada, imediatamente, para criar tabelas, etc.

Método CreateDatabase

O método CreateDatabase( ) cria um novo objeto Database, salva o banco de dados no disco e retoma uma referência ao objeto criado. Aplicável somente aos objetos Workspace.

Sintaxe

Set ObDatabase = ObWorkspace.CreateDatabase(nomeDoArquivo, localização[, opções])

O seguinte fragmento de código cria um banco de dados na versão 3.0 da DAO chamado BASE32.MDB no diretório corrente:

 

Dim BaseDeDados As Database

Set BaseDeDados = DBEngine.Workspaces(0). _

CreateDatabase(App.Path & "\Base32.Mdb", _

dbLangGeneral, dbVersion30)

 

Utilizamos a área de trabalho default para abrir o banco de dados, referindo-a explicitamente. As constantes utilizadas como argumentos para o método são definidas no próprio VB: a constante dbLangGeneral indica a criação de um banco de dados com ordenamento de strings compatível com a Língua Portuguesa; a constante dbVersion30 indica um banco de dados no formato do Microsoft Access para Windows 95. Note apenas que tal formato é incompatível com versões anteriores da DAO ou do próprio Access.

O objeto Database retomado pelo método pode ser utilizado imediatamente para a definição das tabelas do arquivo. Observe que se o arquivo existir, um erro será gerado em tempo de execução.

Criando Tabelas

A criação de uma tabela requer as seguintes condições mínimas:

1 . Uma referência a um objeto Database onde será adicionada a tabela. Se o arquivo não existir, deve ser criado; caso contrário, deve ser aberto.

2. A criação de um objeto TableDef, através do método CreateTableDef( ). Este representará a

tabela criada.

3. A criação dos objetos Field necessários para representar a totalidade dos campos da tabela, feita através do método CreateField( ).

4. A atualização das propriedades de cada objeto Field, na medida das necessidades da tabela.

5. A anexação de cada objeto Field à coleção TableDefs, com o método Append.

6. A anexação do objeto TabIeDef criado à coleção Databases, também com o método Append.

Vamos criar uma tabela como exemplo. Utilizemos a definição dada à tabela Categorias, no Capítulo 13. A Figura 14.2 lista as propriedades de cada campo da tabela:

Figura 14.2: As definições da tabela Categoria.

Como o arquivo BASE32.MDB já existe - criamos no código para CreateDatabase -, precisamos abrí-lo, utilizando o método OpenDatabase( ), aplicável somente a um objeto Workspace.

Método OpenDatabase

O método OpenDatabase( ) abre o banco de dados especificado numa sessão, determinada por um objeto Workspace, e retorna uma referência a um objeto Database. O banco de dados aberto é automaticamente adicionado à coleção Databases da área de trabalho.

Sintaxe

Set ObDatabase = ObWorkspace.OpenDatabase(nomeDoArquivo [,

modoExclusivo[, somenteParaLeitura[, fonteDeDados]]])

O passo seguinte será a criação de um objeto TableDef para referir a tabela a ser criada. Isto é feito pelo método CreateTableDef( ), aplicável somente aos objetos Database.

 

Método CreateTableDef

 

Cria um objeto TableDef para representar uma nova tabela do banco de dados.

Sintaxe

Set ObTabIeDef = ObDatabase.CreateTableDef([nomeDaTabela[, _

atributos[, fonteDeDados[, informaçãoDeConexão]]]])

Como vimos, cada um dos campos da tabela será representado por um objeto Field, criado pelo método CreateField( ) aplicado ao objeto TabIeDef anteriormente criado.

Método CreateField

Cria um objeto Field para representar um novo campo de banco de dados, aplicável aos objetos Index, Relation e TableDef.

Sintaxe

Set ObField = ObVariável.CreateField([nomeDoCampo[, tipoDeDado[, tamanho]]])

O seguinte fragmento de código cria uma nova tabela no banco de dados aberto:

 

Figura 14.3

 

O código acima, por razões didáticas, segue rigorosamente o passo-a-passo sugerido para a criação de uma tabela. Nós definimos cada uma das propriedades dos campos utilizando um objeto Field e anexando à coleção Fields do objeto TableDef criado para representar a tabela. Isso foi feito com o método Append.

Método Append

Adiciona um novo objeto de acesso a banco de dados a uma coleção. Aplicável a todas as coleções da DAO.

Sintaxe

Coleção.Append novoObjeto

Após cada definição ter sido criada, anexamos o objeto TableDef à coleção TableDefs do banco de dados, referido pela variável Database BaseDeDados. Isso também foi feito com o método Append.

Adicionando Índices

A criação de um índice para uma tabela requer os seguintes passos:

1. Um objeto TableDef ao qual anexar a definição de índice. Se a tabela não existir, deverá ser criada como exposto anteriormente; caso contrário, sua definição deverá ser aberta na forma Set ObTableDef = ObDatabase.TableDefs("NomeDaTabela").

2. Um objeto Index, criado com o método CreateIndex( ) aplicado ao objeto TableDef.

3. A atualização das propriedades do objeto Index, quando for o caso.

4. Um objeto Field para cada campo componente da chave de índice, criados com o método CreateField( ), mostrado anteriormente.

5. A anexação de cada objeto Field ao objeto Index criado para armazenar a definição do índice. Isto é feito pelo método Append, tal como já falamos.

6. A anexação do objeto Index ao objeto TabIeDef aberto, também com o método Append.

Exemplo:

Figura 14.4: A definição de tabela Clientes.

Propriedades para o índice primário da tabela:

Figura 14.5: A definição do índice primário da tabela Clientes.

Note que ajustar a propriedade Unique do índice não é estritamente necessário, já que um índice primário (propriedade Primary igual a True) é, por definição, sempre único. O fragmento de código seguinte cria a tabela Clientes e seu índice primário:

Figura 14.6

Todo o código até a criação do objeto Index funciona da mesma maneira que no exemplo anterior. Apenas utilizamos um novo meio de expressar as mesmas ações. Para criar o índice, utilizamos o método CreateIndex( ), aplicável somente a um objeto TableDef e ajustamos suas propriedades de acordo com a definição de um índice primário.

Método CreateIndex

Cria um objeto Index para representar um novo índice de uma tabela.

Sintaxe

Set ObIndex = ObTableDef.CreateIndex([nomeDoÍndice])

O passo seguinte foi criar a expressão de indexação do objeto. Ora, uma expressão de índice é, simplesmente, uma lista de campos componentes. Assim, para cada campo da expressão, criamos um objeto Field para representá-lo. Isso foi feito com o método CreateField( ), já conhecido. Note que apenas especificamos o nome do campo, uma vez que sua definição já existe.

A criação final do índice envolveu apenas anexar o objeto Field ao objeto Index, definindo uma expressão de indexação e anexar este ao objeto TableDef. Tudo com o método Append.

Adicionando Relações

A definição de um relacionamento pela DAO envolve os seguintes passos:

1. Um objeto Database, referindo um banco de dados aberto. Naturalmente, as tabelas a relacionar devem existir e ser estruturadas de tal forma que o relacionamento possa ser estabelecido. O campo a relacionar deve ser a chave primária da tabela origem e do mesmo tipo na tabela dependente (a chave externa).

2. A criação de um objeto Relation, feita pelo método CreateRelation(, aplicável somente a um objeto Database.

3. A definição das propriedades do relacionamento; basicamente a tabela origem (propriedade Table) e a tabela dependente (propriedade ForeignTable).

4. Definir a natureza do relacionamento (se um-para-um ou um-para-vários, se a integridade referencial deve ser preservada, etc.), ajustando a propriedade Attributes do objeto. Consulte a documentação do VB para as constantes predefinidas disponíveis.

5. A criação de um objeto Field, utilizando o já conhecido método CreateField, para definir o campo comum da relação.

6. A definição da chave externa do relacionamento, ajustando a propriedade ForeignName do objeto Field para o campo relacionado.

7. A adição do campo à coleção Fields do objeto Relation e a sua adição à coleção Relations do objeto Database aberto, para criar efetivamente o relacionamento.

Método CreateRelation( )

Cria um novo objeto Relation para a especificação de um relacionamento entre tabelas.

 

Sintaxe

Set ObRelation = ObDatabase.CreateRelation([nomeDaRelação[, _

tabelaOrigem [, tabelaDependente [, atributos ]]]])

Uma vez mais, utilizaremos como exemplo uma das definições feitas no capítulo anterior. Vamos definir o relacionamento entre a tabela Produtos e a tabela Produtos Por Fornecedor. A Tabela 3.5 lista as definições do relacionamento:

Figura 14.7: As definições do relacionamento entre a tabela Produtos e a tabela Produtos Por Fornecedor.

O seguinte fragmento de código cria o referido relacionamento:

Figura 14.8

 

Objeto Relation

Propriedades

A propriedade Attributes indica as características do objeto. A propriedade ForeignTable especifica tabela dependente na relação. A propriedade Table especifica a tabela primária na relação definida.

Sintaxe

Objeto.Attributes [= valorConstante]

ObRelation.ForeignTable [= nomeDaTabela]

ObRelation.Table [= nomeDaTabela]

O código-fonte segue, rigorosamente, o passo-a-passo anterior, não merecendo maiores comentários. Note apenas que não precisamos definir o tipo de relacionamento um-para-vários, atribuído por default. Além disso, a imposição da integridade referencial dos dados é atribuída por default à propriedade Attributes. Caso precise utilizar mais de uma constante, apenas some seus valores. Por exemplo, a declaração dbRelationUnique + dbRelationUpdateCascade define um relacionamento de um-para-um com atualização em cascata.

ForeignName

Propriedade

Especifica o campo utilizado como chave externa de uma relação. Por definição, o campo deve pertencer à tabela dependente e se relacionar a um campo definido como chave primária na tabela de origem da relação.

Sintaxe

ObField.ForeignName [= nomeDoCampo ]

A Criação de Consultas

Como foi visto nas primeiras seções deste capítulo, o Microsoft Jet oferece amplo suporte à utilização da SQL. É possível não apenas a execução de declarações SQL, como também o seu armazenamento no banco de dados. Uma vez armazenada a consulta, ela pode ser alterada ou removida.

Nesta seção, enfocaremos apenas o armazenamento e a remoção de consultas de um banco de dados, deixando para mais adiante a sua utilização. Não discutiremos, porém, a própria linguagem SQL. Mais informações sobre SQL podem ser encontradas na apostila específica de SQL, que acompanha este manual.

Como vimos no sumário da DAO, o Microsoft Jet fornece um objeto QueryDef para a manipulação de declarações SQL armazenadas no banco de dados. Além disso, todo objeto Database possui uma coleção QueryDefs, contendo informações sobre todas as consultas armazenadas.

A criação e o armazenamento de uma consulta num banco de dados requerem somente os seguintes passos:

1. Um objeto Database, referindo o banco de dados onde a consulta será armazenada.

2. A criação de um objeto QueryDef, através do método CreateQueryDef( ), para o armazenamento da consulta. Observe que o método pode salvar, automaticamente, a consulta no banco de dados.

3.. A especificação da declaração geradora da consulta, atribuindo-se uma declaração válida à propriedade SQL do objeto QueryDef.

Método CreateQueryDef( )

Cria um objeto QueryDef, podendo armazenar a definição da consulta SQL no banco de dados.

Sintaxe

Set ObQueryDef = ObDatabase.CreateQueryDef([ nomeDaConsulta ][, declaraçãoSQL ])

Para o nosso exemplo, suponhamos que seja necessário um relatório contendo os seguintes dados: Descrição da categoria do produto, Nome do Produto e Preço de Venda. O objetivo básico do relatório é fornecer uma lista abrangente de produtos aos clientes. Para isso, são necessários dados da tabela

Produtos e da tabela Categorias, utilizando-se uma consulta simples para extraí-los.

O fragmento de código a seguir ilustra a criação da consulta:

Figura 14.9

 

A Modificação de Estrutura do Banco de Dados

Uma vez criado um banco de dados, a alteração da sua estrutura sofre algumas restrições. Índices, campos e relacionamentos não podem ter suas propriedades alteradas. Para isso, é necessário primeiro remover o objeto, recriando-o com a nova configuração. A remoção de um objeto de uma coleção qualquer é feita com o método Delete.

Método Delete

Aplicado a um Recordset, o método Delete remove o registro corrente. Aplicado a uma coleção, remove o objeto especificado.

Sintaxe

ObRecordset.Delete

Objeto.Coleção.Delete nomeDoObjeto

Mesmo a remoção de alguns objetos sofre restrições, a saber:

Não é possível remover um campo que faça parte de uma expressão de índice ou de uma relação. Para isso, é necessário, primeiro, remover o índice ou a relação.

Não é possível remover um índice que esteja mantendo uma relação. Para isso, é necessário remover, primeiro, a relação.

Não é possível remover uma tabela que esteja suportando um relacionamento. É preciso, primeiro, remover a relação.

A alteração de uma consulta armazenada envolve apenas a alteração da propriedade SQL do objeto QueryDef. O seguinte fragmento de código ilustra a remoção de uma tabela e a alteração de uma consulta SQL:

Figura 14.10

 

Manutenção do Banco de Dados

Visando incrementar a performance do acesso ao banco de dados, sempre crítica num sistema relacional, o Microsoft Jet (e o Microsoft Access) não removem as páginas de registros eventualmente deletadas do banco de dados. Esse artifício, embora garanta uma performance adequada aos aplicativos de acesso, consome consideráveis quantidades de disco, já que os arquivos tendem a se expandir mais do que o crescimento dos registros.

Por outro lado, erros de sistema ocorridos durante operações críticas podem danificar o banco de dados. Se o dano não for extenso, é possível corrigi-lo.

Para estes casos, são indispensáveis recursos de manutenção do bancos de dados, como compactação e correção de erros. O Microsoft Jet fornece ferramentas suficientes para tratar o problema. É o que veremos nesta seção.

Compactando um

Arquivo

O objeto de sistema DBEngine suporta um método CompactDatabase, que copia todos os dados de um banco de dados para outro. No processo, o arquivo é otimizado, resultando na recuperação de espaço eventualmente desperdiçado.

Método CompactDatabase

Copia e compacta um banco de dados, permitindo a alteração de versão, definição de ordenamento e criptografia do arquivo.

Sintaxe

DBEngine.CompactDatabase arquivoAntigo, arquivoNovo [, localização [,

opções]]

Não é possível compactar um banco de dados aberto. Além disso, não se deve utilizar o mesmo nome no processo, já que o arquivo anterior será removido, mesmo que a compactação não se complete.

Para ilustrar o método, criaremos uma rotina de uso geral razoavelmente segura para a compactação de bancos de dados compatíveis com o Microsoft Access. Veja o código nas três figuras que se seguem:

Figura 14.11: Função CompactDatabase.

Figura 14.12: Função para obter arquivo temporário.

Figura 14.13: Função para verificar existência de arquivo.

O objetivo básico da rotina é tornar mais fácil e completamente seguro o processo de compactação.

De uma forma geral, a compactação de um banco de dados envolve os seguintes passos:

1. Criar um novo arquivo de dados, compactado.

2. Se a compactação foi bem-sucedida, remover o arquivo original.

3. Renomear o arquivo compactado com o nome de arquivo anterior.

Evidentemente, é preciso ter certeza de que o novo nome não corresponde a um arquivo existente. Caso contrário, ele seria sobrescrito. Tudo isso faz com que uma rotina de compactação tenha uma razoável quantidade de linhas de código. Só isso já justifica a existência de uma rotina genérica para automatizar essa complexidade.

Análise

CompactDatabase( )

A função recebe três parâmetros e retoma um lógico indicando se a operação foi bem-sucedida:

Uma string contendo o path completo e o nome do banco de dados a compactar.

Uma string opcional com informações de localização. Se o parâmetro não for passado, a função assume a constante predefinida dbLangGeneral.

Um inteiro opcional com a definição de criptografia. Caso o parâmetro não seja passado, a função assume o valor 0 (sem criptografia).

O primeiro passo da rotina é, portanto, validar seus argumentos, como descrito acima. A seguir, obtemos um nome de arquivo temporário da função TempNam( ), discutida mais adiante, ainda nesta seção. Se, por qualquer motivo, a função falhar, a rotina retoma um valor False.

Estamos, então, prontos para compactar o banco de dados, utilizando o DBEngine. Claro que precisamos monitorar eventuais erros no processo. Ainda assim, visando aumentar a segurança do processo (afinal, a rotina remove o banco de dados anterior), testamos novamente o sucesso da compactação. Simplesmente tentamos abrir o novo banco de dados, ainda monitorando erros. Se isso for possível, o banco foi corretamente compactado.

O passo final é remover o banco de dados antigo e renomear o novo banco de dados. Se o banco de dados temporário tiver sido criado num diretório diferente daquele onde se localizava o anterior, o comando Name moverá o arquivo e o renomeará.

TempNam( )

A função recebe os seguintes parâmetros, todos opcionais:

O prefixo do nome de arquivo temporário gerado. Caso o parâmetro não seja passado, a função assume o valor "~AXC".

A extensão do arquivo, incluindo o ponto. O valor default é ".TMP".

O caminho para o diretório onde se deseja criar o arquivo temporário. Caso o parâmetro esteja ausente, a função assume o diretório especificado nas variáveis ambientais "TEMP" e "TMP" ou, na ausência destas, o diretório corrente.

O primeiro procedimento da função é verificar os argumentos e assumir os valores default, se for o caso. A seguir, validamos o argumento Path. Isso é feito de maneira extremamente simples: apenas salvamos o diretório corrente e tentamos mudar para o path especificado. Se a operação não for bem-sucedida, o path é inválido e a função retorna. Caso contrário, retomamos ao diretório corrente e seguimos em frente.

Depois, construímos um path completo, incluindo a definição de drive e removemos a contrabarra final. Estamos prontos para gerar um nome temporário na instrução seguinte, concatenando o path e o prefixo do arquivo. A seguir, inicializamos o gerador de números aleatórios para a criação do restante do nome do arquivo.

A geração e teste do nome do arquivo é feita dentro de um loop. Os quatro caracteres restantes do arquivo são gerados aleatoriamente pela função Rnd e, formatados apropriadamente, são armazenados na variável TempName. Esta é concatenada à variável TempFile para a criação de um caminho e nome completos de arquivo. Por fim, o loop testa se o arquivo já existe, chamando a função ArquivoExiste( ), comentada mais adiante. Se o arquivo existir, a função retorna True e o loop retorna. Caso contrário, um arquivo temporário foi gerado e a função o retorna.

Note que a função não cria efetivamente o arquivo, como a função da API do Windows GetTempFileName( ), apenas gera um nome válido para um arquivo inexistente.

ArquivoExiste( )

Para testar a existência do arquivo apenas tentamos abri-lo no modo compartilhado, tendo o cuidado de utilizar um modo de operação que não o danifique. Se a operação gerar um erro, mais especificamente o código de erro 53, o arquivo não existe no path especificado.

Reparando um Arquivo

Para reparar um arquivo danificado, utilizamos o método RepairDatabase, aplicável exclusivamente ao objeto DBEngine. A operação realizada por ele recupera um arquivo invalidado por uma operação incompleta de leitura e escrita de página de dados. Este tipo de dano ocorre freqüentemente quando o sistema é desligado subitamente, ainda com o arquivo aberto.

Método RepairDatabase

Recupera um arquivo Access danificado.

Sintaxe

DBEngine.RepairDatabase nomeDoArquivo

É preciso notar que nem todos os tipos de danos podem ser recuperados. Além disso, a operação de recuperação não otimiza o arquivo, deixando inúmeras páginas desnecessárias alocadas, consumindo espaço em disco. Assim, após uma recuperação bem-sucedida, é conveniente a compactação do banco de dados.

A rotina listada a seguir efetua as operações de reparação e compactação subsequente:

Figura 14.14: Reparando um banco de dados

Análise

A função RepairDatabase( ) recebe os seguintes parâmetros:

O nome do banco de dados a reparar. Note que o arquivo deve estar fechado para que a operação possa se realizar sem erros.

Uma string opcional contendo as informações de localização. Se o valor não for declarado, a rotina assume a constante predefinida dbLangGeneral.

Um inteiro opcional, definindo a criptografia do arquivo. Seu valor default é 0 (sem criptografia).

Os dois últimos parâmetros não são necessários à reparação do banco de dados. Servem, contudo, para a compactação realizada imediatamente após.

Realizamos a compactação executando o método RepairDatabase( ), como indicado. A seguir, se nenhum erro ocorreu, compactamos o banco de dados através da função CompactDatabase( ), já discutida. E isso é tudo.

Navegação num Banco de Dados

Evidentemente, a criação e alteração da estrutura de um banco de dados, bem como a manutenção da sua integridade e otimização são tarefas eventuais na programação comercial. Só com estes recursos, a DAO não seria útil. A principal utilização dos objetos disponibilizados pela DAO é na edição e navegação de um banco de dados.

O Microsoft Jet Database Engine disponibiliza dois mecanismos distintos de execução das diferentes tarefas relacionadas a um banco de dados:

Um mecanismo navegacional, representado pelo conjunto de objetos da DAO, para a movimentação entre os registros individuais de um banco de dados.

Um mecanismo relacional, baseado na Linguagem Estruturada de Consultas (SQL), para o processamento de massas relacionadas de registros do banco de dados.

A boa programação em VB utilizará os dois mecanismos em conjunto, obtendo o melhor de dois mundos. De uma forma geral, utilizaremos a SQL para extrair uma visão particular dos registros do banco de dados e os objetos da DAO para editar e visualizar cada registro em particular. Por outro lado, sempre que for necessário atualizar grandes massas de registros segundo critérios consistentes, daremos preferência à SQL.

Nesta seção, conheceremos os diversos recursos navegacionais disponibilizados pelo Microsoft Jet. Uma discussão mais ampla da SQL pode ser encontrada na apostila que acompanha este manual.

Abrindo o Banco de Dados

Evidentemente, todas operações num banco de dados se iniciam após sua abertura. Isso é feito, como já sabemos, pelo método OpenDatabase( ), já amplamente utilizado nos exemplos deste capítulo. No entanto, há ainda alguns aspectos a discutir.

A versão 5 do Visual Basic foi especialmente remodelada para operar, no que concerne ao tratamento de bancos de dados, em ambientes multiusuário. Ora, pelas razões vistas, o acesso a um banco de dados num ambiente multiusuário deve obedecer a um conjunto de, requisitos e restrições, distintos dos sofridos em ambientes monousuário, especialmente numa arquitetura cliente-servidor. Destacamos em particular:

Deve-se definir um conjunto de regras de permissão e acesso para cada grupo de usuários do sistema.

A conexão de um usuário ao sistema - uma sessão - deve ser especificada com referência às regras de permissão e acesso ao banco de dados.

Conseqüentemente, as transações que estiverem ocorrendo no sistema devem estar de acordo com as regras especificadas.

Assinalamos anteriormente que a definição da segurança de acesso ao banco de dados não pode ser realizada no escopo do Visual Basic. Este só é capaz de utilizar um banco de dados cuja segurança foi estabelecida no Microsoft Access ou em outro gerenciador compatível. Assim, no VB, o primeiro passo é definir uma sessão para o logon do usuário.

Uma sessão define uma seqüência de operações executadas pelo Microsoft Jet. Ela se inicia na conexão do usuário ao sistema e termina no seu logoff. Todas as operações executadas durante uma sessão formam o escopo de uma transação e estão sujeitas às permissões determinadas pela segurança de acesso definida para o usuário.

Definindo uma Área de Trabalho

No Microsoft Jet, uma sessão é implementada pela criação (ou utilização) de um objeto Workspace.

É através deste objeto que uma sessão é iniciada e gerenciada. Numa sessão, é possível a abertura de diversos bancos de dados, a manipulação de transações e a garantia da segurança de acesso baseada em senhas e identificadores de usuário.

Uma transação é definida como uma série de alterações nos dados ou no esquema de um banco de dados.

No Microsoft Jet, cada sessão é definida pelos seguintes passos:

A sessão é iniciada quando um objeto Workspace é criado pelo método CreateWorkspace( ), quando é definido um nome para a sessão, um identificador de usuário e uma senha.

Os diversos bancos de dados necessários são abertos na área de trabalho definida pelo já conhecido método OpenDatabase( ).

Os dados do banco de dados são manipulados em transações, definidas pelos métodos BeginTrans, CommitTrans e Rollback.

Os diversos bancos de dados são fechados.

A sessão se encerra quando o objeto Workspace é fechado pelo método Close.

Método CreateWorkspace( )

Cria um objeto Workspace, iniciando uma sessão e definindo uma nova área de trabalho.

Sintaxe

Set ObWorkspace = DBEngine.CreateWorkspace( nomeDaSessão, identificadorDoUsuário, senha)

Assinalamos anteriormente que, ao ser inicializado, o Microsoft Jet cria um objeto Workspace default, referido pelo índice 0 na coleção Workspaces do DBEngine. Sempre que não houver necessidade de condições especiais de segurança de acesso, esta sessão pode ser utilizada livremente.

Métodos BeginTrans, CommitTrans e

Rollback

Os métodos aplicáveis aos objetos Workspace e Database gerenciam as transações de uma sessão. Uma transação é iniciada por BeginTrans e terminada por CommitTrans, podendo ser abortada por Rollback.

Sintaxe

Objeto.BeginTrans

Objeto.CommitTrans

Objeto.Rollback

A utilização de transações melhora a performance geral da atualização de um banco de dados. Elas permitem ao Jet acumular diversas atualizações e gravá-las numa única operação. Uma das vantagens mais evidentes de tratar as diversas operações como um todo é que elas serão bem-sucedidas ou falharão em conjunto, auxiliando na preservação da integridade dos dados.

Imagine a seguinte situação. Por qualquer motivo, é necessário atualizar a base de dados através de três consultas de atualização consecutivas, que processam grandes massas de registros. A operação só pode ser considerada bem-sucedida se todas as consultas forem executadas. Assim, antes de executar a primeira consulta, utilize o método BeginTrans. Somente quando o método CommitTrans for executado a operação será completada, num único passo.

Método Close

Fecha um objeto de acesso a banco de dados. Aplicável aos objetos Workspace, Database e Recordset.

Sintaxe

Objeto.Close

O código a seguir ilustra a utilização de uma sessão no Visual Basic, para atualizar a tabela de Produtos definida anteriormente

Figura 14.15: Utilizando uma sessão no Visual Basic.

Este é um exemplo bem simples de transação. Poderíamos ter utilizado múltiplas consultas, combinadas com diversas operações de E/S no banco de dados via objetos da DAO. O resultado seria sempre o mesmo.

Abrindo Tabelas e Consultas

Uma vez iniciada uma sessão e aberto(s) o(s) banco(s) de dados, estamos livres para manipular os dados de inúmeras e diferentes maneiras. Sempre que desejamos extrair uma determinada visão dos dados armazenados, de uma tabela a uma consulta englobando múltiplas tabelas, recorremos aos objetos Recordset. Há três tipos de recordsets:

Tabela - refere-se a uma tabela local (não ODBC) armazenada num banco de dados. Este é o único tipo de recordset com suporte à localização indexada.

Dynaset - um conjunto dinâmico de dados, podendo se referir tanto a uma tabela, local ou não, quanto ao resultado de uma consulta SQL. Um dynaset pode ser editado normalmente.

Snapshot - um objeto do mesmo tipo de um Dynaset, exceto que não admite edição de dados. Como o Microsoft Jet não precisa manter uma série de referências e ponteiros, como nos dynasets, a operação com um snapshot tende a ser mais rápida.

Seja qual for o tipo de recordset, ele é criado sempre pelo método OpenRecordset( ). Ao executá-lo, podemos especificar tanto a fonte de dados (um comando SQL, uma tabela anexada, uma tabela local, etc.) quanto o tipo de recordset a ser criado. Se nenhum tipo de recordset for especificado, o método tenta criar um do tipo tabela, se possível.

Método OpenRecordset

Cria um novo objeto Recordset, representando qualquer conjunto de dados extraídos de um banco de dados. Aplicável aos objetos Database, QueryDef, Recordset e TableDef.

Sintaxe

Set Objeto = ObDatabase.OpenRecordset( fonteDeDados [, tipo [, opções]])

Set Objeto = ObjetoFonte.OpenRecordset([ tipo [, opções ]])

Além dos comandos SQL, há inúmeras maneiras de personalizar a visão dos dados obtida num recordset. A primeira delas é ordenando os dados obtidos.

Os registros num recordset do tipo tabela aparecem na ordem em que foram originalmente adicionados ao banco de dados. Isso, geralmente, de nada serve. Há duas maneiras básicas de ordenar um recordset deste tipo:

Através da sua propriedade Index, atribuindo a ela um dos índices predefinidos para a tabela.

Através da criação de um novo objeto Recordset a partir do primeiro, após ajustar a propriedade Sort deste último para um critério de ordenação.

Não é possível alterar a ordenação de um dynaset ou snapshot já criado. Ao contrário da tabela, é necessária a criação de um novo recordset com o critério de ordenação requerido.

Um outro mecanismo fundamental de personalização dos dados extraídos por um recordset é o estabelecimento de filtros.

A propriedade Sort dos objetos Recordset retorna ou determina a ordenação do conjunto de dados extraído. Ela requer uma string contendo uma cláusula SQL ORDER BY válida, sem a expressão-chave da linguagem.

Da mesma maneira que a propriedade Sort, é possível alterarmos a propriedade Filter de um recordset existente e, então, extrairmos um novo recordset dele. Este novo recordset apontará apenas para os registros correspondentes ao critério de filtro selecionado.

A propriedade Filter dos objetos Recordset retorna ou determina o critério de seleção dos registros de um conjunto de dados. Ela requer uma string contendo uma cláusula SQL WHERE válida, sem a palavra-chave da linguagem.

O fragmento de código a seguir ilustra a abertura de tabelas e a ordenação e filtragem dos registros:

Figura 14.16: Abrindo tabela, ordenando e filtrando registros

O exemplo fala por si. Note apenas a utilização de apóstrofos para delimitar o valor do CEP desejado ,no ajuste da propriedade Filter do objeto obDynaset. Esta é a convenção para indicar que o valor incluso é uma string e não um objeto do próprio banco de dados. Selecionar a condição CEP 20002000 sem os apóstrofos não retomará registros.

Abrindo uma Consulta Parametrizada

Lembre-se de que, no exemplo anterior, o recordset obtido pela condição de filtro do objeto obDynaset não retornava o cadastro completo do fornecedor, apenas os campos armazenados na tabela Fornecedores. Como os campos Cidade e UF pertencem à tabela relacionada Localidades, eles não seriam devolvidos.

Vamos supor agora, que você precisasse do endereço completo para, digamos, uma mala direta. Neste caso, você precisaria também dos dois campos armazenados na tabela Localidades. Na programação em Clipper, por exemplo, você deveria abrir ambas as tabelas, relacionar os campos através do comando SET RELATION e codificar inteiramente um algarismo de extração dos dados. Nada disso é necessário com o Microsoft Jet. Uma simples declaração SQL é capaz de fazer a mágica.

Para que possamos utilizar os registros retornados por uma declaração SQL de extração de dados, devemos atribuir o resultado da consulta a um objeto Recordset. Como vimos mais acima, isso pode ser feito também com o método OpenRecordset( ). Neste caso, passamos como parâmetro ao método a declaração que extrairá os dados. Veja o exemplo de código a seguir:

Figura 14.17

O código acima produz o mesmo resultado do exemplo anterior, onde ajustamos a propriedade Filter do objeto obDynaset. Neste caso, contudo, executamos diretamente no dispositivo Jet uma consulta seleção de registros e atribuímos o seu resultado a um objeto Recordset.

Este exemplo, contudo, é pouco realista. Dificilmente saberemos de antemão, num ambiente programado e não interativo, o valor do CEP desejado. Provavelmente, ele será obtido, em tempo de execução, através de um diálogo, onde o usuário informará o valor desejado para a mala direta. O que precisamos, neste caso, é de uma consulta parametrizada.

As consultas parametrizadas são armazenadas previamente no banco de dados. Quando necessárias, elas são abertas e seus parâmetros atribuídos. A partir daí, podemos criar um recordset com o valor atribuído agindo como filtro.

Para criar uma consulta parametrizada é preciso preceder o texto da instrução SELECT com uma cláusula PARAMETERS, onde definimos os parâmetros da consulta com seus nomes e tipos de dados. A cláusula PARAMETERS tem a seguinte sintaxe:

PARAMETERS nomeparâmetro tipodado ;

Poderíamos transformar a consulta anterior em uma consulta parametrizada , incluindo uma cláusula PARAMETERS no seu início e modificando sua cláusula INNER JOIN para WHERE. Os colchetes aqui servem para indicar que o valor entre eles é um parâmetro. Veja como ficaria:

"PARAMETERS [CódigoPostal] Text; SELECT Fornecedores.[Razão Social],

Fornecedores.Endereço, Fornecedores.CEP, Localidades.Cidade, Localidades.UF

FROM Localidades, Fornecedores WHERE Localidades.CEP = Fornecedores.CEP AND

Fornecedores.CEP = [CódigoPostal]"

Em tempo de execução, abrimos a definição da consulta e atribuímos seu parâmetro, extraindo então um recordset.

Veja o fragmento de código seguinte:

Figura 14.18: Abrindo uma consulta parametrizada.

Utilizamos a coleção Parameters do objeto QueryDef para obter e atribuir o parâmetro desejado. O código acima produz o mesmo resultado que no exemplo anterior. Exceto que, numa aplicação real, o valor atribuído ao parâmetro seria obtido de uma variável ou de um controle de janela de diálogo.

Fechando Tudo

Ao longo dos últimos exemplos não nos preocupamos em fechar os recordsets e bancos de dados abertos. Utilizamos uma característica da DAO: o recordset ou banco de dados (numa palavra, o objeto) é fechado automaticamente, sempre que a variável que o referencia sai de escopo. Como utilizamos variáveis locais para referir os diversos objetos, não foi preciso fechá-los.

Isso, no entanto, não é boa prática de programação. Preocupe-se sempre em fechar cada um dos objetos utilizados com o já conhecido método Close. Lembre-se apenas de que, como numa pilha de pratos sujos, o último a entrar deve ser o primeiro a sair. Feche os objetos sempre do nível inferior da hierarquia (Recordset, QueryDef ou TableDef, Database e Workspace, nessa ordem). Caso contrário, a pilha de pratos cai e um erro é gerado em tempo de execução.

Localizando um Registro

Na programação de front-ends de sistemas de bancos de dados é comum a necessidade de localizar um registro específico num recordset. A biblioteca de Objetos de Acesso a Bancos de Dados fornece um bom conjunto de mecanismos de localização de registros.

Localização Indexada

A mais rápida, já conhecida dos programadores de outras linguagens com filosofia de banco de dados diferente, como o Clipper ou o Paradox, é a pesquisa indexada. Ela só é possível em objetos Recordset do tipo tabela, desde que o índice apropriado tenha sido definido como o índice de controle. Se você tentar localizar um registro sem um índice aberto ou por uma chave diferente do índice corrente, um erro é gerado.

Para localizar registros numa tabela indexada procedemos da seguinte forma:

1. Criamos um Recordset do tipo tabela e o atribuímos a um objeto.

2. Alteramos a propriedade Index do objeto para o nome do índice desejado.

3. Utilizamos o método Seek para localizar um registro cujo valor corresponde à chave especificada.

Método Seek

Localiza um registro numa tabela indexada. Aplicável somente a um Recordset do tipo tabela.

Sintaxe

ObRecordset.Seek stringDeComparação, listaDeChaves

Quaisquer dos operadores de comparação podem ser utilizados como argumento para o método. Caso a chave de índice possua mais de um campo, uma lista de valores correspondente deve ser especificada.

Veja o fragmento de código a seguir:

Figura 14.19: Localizando um registro com Seek

Primeiro, definimos o índice PrimaryKey como o índice de controle de ordenação do recordset. A seguir, pesquisamos o código de Produto número 2. Note que especificamos um tipo de dado estritamente compatível com a chave de índice. Por fim, como seria de se esperar, verificamos se a busca falhou, testando o valor da propriedade NoMatch.

Propriedade NoMatch

A propriedade retorna um lógico indicando se a última operação de busca realizada num recordset falhou.

Sintaxe

ObRecordset.NoMatch

Localização Seqüencial

Como dissemos, a pesquisa indexada só pode ser utilizada nos recordsets do tipo tabela. Isso não quer dizer, contudo, que o Microsoft Jet não forneça mecanismos suficientes para a localização de registros em dynasets ou snapshots. Ao contrário. Os seguintes métodos podem ser utilizados:

FindFirst - localiza o primeiro registro que satisfaça ao critério especificado a partir do início do recordset.

FindLast - localiza o último registro que satisfaça o critério especificado a partir do fim do recordset.

FindNext - localiza o próximo registro que satisfaça o critério especificado a partir da posição corrente.

FindPrevious - localiza o registro anterior que satisfaça ao critério especificado a partir da posição corrente.

O critério especificado aos métodos geralmente corresponde a uma cláusula SQL WHERE válida, sem a palavra-chave da linguagem. Normalmente, buscamos um valor idêntico a um critério para um determinado campo. Como no método Seek, podemos, contudo, utilizar quaisquer dos operadores de comparação.

O fragmento de código a seguir executa a mesma operação do exemplo anterior. Desta vez operamos sobre um dynaset, daí a utilização do método FindFirst:

Figura 14.20: Localizando com FindFirst

Note o modo como especificamos o conteúdo da expressão a ser buscada. Não utilizamos delimitadores no valor 2 porque não se trata de tipo de dado string!

Movendo-se nos Registros

Como já assinalado, o mecanismo navegacional (por oposição ao mecanismo relacional) disponibilizado pela DAO se baseia no conceito de movimento pelos registros de um recordset para localizar um ou mais registros. Como também assinalamos, tais mecanismos são ineficientes para o processamento de grandes massas de registros. Nestes casos, deve-se utilizar os comandos SQL suportados pelo Microsoft Jet. No entanto, para operações de alcance limitado, esse mecanismo é inteiramente satisfatório, sobretudo quando se quer processar um único registro.

A localização de um registro particular é indispensável porque somente os campos do registro podem ser editados, a menos que utilizemos consultas de ação. Assim, métodos para saltarmos entre os registros de um recordset são indispensáveis. O Microsoft Jet disponibiliza os seguintes métodos:

MoveFirst - salta para o primeiro registro lógico de um recordset.

MoveLast - salta para o último registro lógico de um recordset.

MoveNext - salta para o próximo registro lógico de um recordset.

MovePrevious - salva para o registro lógico anterior de um recordset.

No caso de utilizarmos tais métodos, é indispensável determinarmos os limites de um recordset. Caso contrário, um erro será gerado quando tentarmos fazer uma referência sem um registro corrente. Os objetos Recordset possuem as seguintes propriedades:

BOF - determina se foi atingido o início lógico do recordset.

EOF - determina se foi atingido o fim lógico de recordset.

O fragmento de código a seguir ilustra a navegação por todo um recordset, enviando o conteúdo de um de seus campos para a impressora:

Figura 14.21: Navegando num recordset

Editando o Banco de Dados

Uma vez localizado o registro desejado, é hora de editá-lo. A DAO fornece os seguintes métodos de edição de um recordset (evidentemente do tipo dynaset ou tabela):

Edit - copia o registro corrente para um buffer temporário para edição dos dados.

AddNew - cria um novo registro para um recordset e aloca o buffer de edição.

Update - copia o conteúdo do buffer de edição para o registro corrente ou o novo registro criado.

O fragmento de código a seguir ilustra a utilização dos métodos de edição:

Figura 14.22: Editando o Banco de Dados

Usando o Controle Data Para Acessar um Banco de Dados

O controle data automatiza uma série de tarefas comuns no acesso a dados, mas não faz isto sozinho. Para que sua capacidade seja posta em uso ele precisa estar ligado a outros controles que são utilizados para apresentar as informações colhidas do banco de dados na tela. Cabe ao controle Data o uso destes controles para realizar seus objetivos.

Vejamos como utilizar o controle data para manipular uma tabela do Banco de Dados de exemplo Biblio.mdb. Se você fez a instalação padrão do Visual Basic, este arquivo pode ser encontrado no diretório VB. Crie um novo projeto no Visual Basic. Selecione o controle Data na caixa de ferramentas e acrescente-o ao formulário Form1. Na janela de propriedades, ajuste a sua propriedade Align para 2 - Align Bottom. Selecione a propriedade DatabaseName e clique no botão de reticências. A caixa de diálogo DatabaseName surgirá para que você localize o banco de dados que será acessado pelo controle Data. Localize o arquivo Biblio.mdb e clique em Abrir. Selecione agora a propriedade RecordSource do controle Data e de um clique na seta para baixo para visualizar as opções de configuração do valor desta propriedade. Uma lista com vários nomes de tabelas pertencentes ao Biblio.mdb aparecerão. Selecione a tabela Authors da lista. Agora, quando o formulário Form1 for carregado, o banco de dados Biblio.mdb será aberto automaticamente pelo controle Data e uma consulta será feita à tabela Authors para obter como retorno o seu conjunto de registros. A este conjunto de registros retornados damos o nome de recordset. Mas e então? Onde será apresentado este recordset?

Para fazer a apresentação dos registros da tabela Authors, vamos criar três caixas de texto e ligá-las ao controle Data1. Cada uma será usada pelo controle Data1 para apresentar um dos campos do registro atual. Chamamos de registro atual o registro que atualmente pode ser exibido, alterado ou excluído do recordset. Podemos navegar dentro de um recordset mudando o registro atual através de comandos de avanço e retrocesso.

Para ligar as caixas de texto ao controle Data1 usamos a propriedade DataSource das caixas de texto. Atribua o valor Data1 a esta propriedade para as três caixas de texto. Essa atribuição fará com que o controle Data1 possa exibir informações da tabela Authors nestas caixas de texto, mas ainda falta definir que informação será esta em cada caso.

Selecione a propriedade DataField na caixa de texto Text1. Clique na seta para baixo para ver as opções de valor. As opções apresentadas correspondem aos nomes dos campos da tabela Authors acessada pelo controle Data1. Configure a propriedade DataField como Au_Id para Text1, Author para Text2 e Year Born para Text3. Feito isto, inicie a aplicação. Você verá na caixa de texto Text1 um número que corresponde ao conteúdo do campo Au_Id na tabela Authors, em Text2 é exibido o nome do autor. Em Text3, infelizmente, poucos registros da tabela Authors possuem informação de data de nascimento (Year Born) do autor, por essa razão será difícil ver alguma informação nesta caixa de texto. Mas você pode navegar pela tabela clicando nos botões de seta do controle Data1 e vez por outra verá um registro com informação no campo Year Born.

  Novidades do VB6.0

 

Mantenha uma lista de tarefas a realizar por projeto com RabJump