Banco de dados: Programando com Classe(s)


A utilização dos módulos de classe na programação torna o nosso código mais elegante. Ao usar a técnica de encapsular nosso código , estamos isolando código complexo do resto da aplicação e somente expondo os métodos , propriedades e eventos. Nisto está as bases da criação dos controles ActiveX e da criação dos componentes reutilizáveis.

A programação com banco de dados é grandemente afetada pela utilização dos módulos de classe. Se você já teve que escrever complexas consultas SQL para o seu banco de dados , vai perceber que usando classes vai tornar o seu código mais funcional e fácil de manter.

Vamos mostrar em um exemplo simples os fundamentos para a utilização dos módulos de classe ajudando-o assim a dar os primeiros passos nesse terreno que parece mais um campo de areia movediça.

O projeto

Vamos usar o banco de dados Nwind.mdb (esse banco de dados vem com Access) o qual contém as seguintes tabelas que interessam ao nosso objetivo.

Categorias - Categorias das bebidas
Pedidos -
Informação sobre os pedidos
Detalhes do Pedido-
Detalhes dos pedidos
Produtos -
Relação dos produtos
Fornecedores -
Relação dos fornecedores

Obs: Exibimos aqui somentes as tabelas que iremos utilizar.

O relacionamento entre estas tabelas é mostrado a seguir:

Nosso objetivo é extrair informações sobre os fornecedores e qual bebidas eles distribuem .Queremos exibir somente o nome do Fornecedor e o nome da bebida que é distribuida . Por exemplo queremos saber quais bebidas são distribuidos por Exotic Liquidis; no caso , fazendo um consulta iremos descobrir que este fornecedor distribui as seguintes bebidas : : "Chai", "Chang"

Você já percebeu esta informação pode ser extraida usando SQL , porém note que temos três tabelas relacionadas envolvidas na obtenção desta informação. O relacionamento das tabelas que iremos usar para criar a consulta SQL e a consulta que iremos criar no modo estrutura é exibido a seguir:

A consulta SQL usada para o nosso caso usa dois parâmetros que devemos fornecer: o nome do fornecedor(nomedaEmpresa) e o nome da categoria(nomedaCategoria) que desejamos exibir. A seguir o código extraído da consulta montanda no Access :

SELECT Fornecedores.NomeDaEmpresa, Produtos.NomeDoProduto
FROM Fornecedores INNER JOIN (Categorias INNER JOIN Produtos ON Categorias.CódigoDaCategoria = Produtos.CódigoDaCategoria) ON Fornecedores.CódigoDoFornecedor = Produtos.CódigoDoFornecedor
WHERE (((Fornecedores.NomeDaEmpresa)=[nomeEmpresa]) AND ((Categorias.NomeDaCategoria)=[nomeCategoria]));

Você poderia fazer um programa criando uma função em um módulo que recebesse os parâmetros usados e retornasse o resultado de sua consulta ou usar módulo de classes para encapsular o seu código e torná-lo reutilizável.

Vamos lembrar as principais diferenças entre um módulo padrão e um módulo de classe:

Vamos optar por usar um módulo de classe em nosso projeto.

Escrevendo o Módulo de Classe ( Class Module )

Inicie um novo projeto no VB (stantard EXE) , referencie a Microsoft DAO 3.5/3.51 Object Library (menu: Project->References...) no seu projeto e a seguir inclua um módulo de classe (menu : Project->Add a Class Module) . Dê o nome cls_Sql para o módulo de classe e salve o seu projeto com o nome de class_SQL.

Vamos iniciar definindo duas propriedades públicas para a classe e uma váriavel pública para receber as mensagens que o objeto retornará. Insira as linhas de código como abaixo no início do módulo de classe:

Agora vamos definir o método que irá executar a consulta , vamos chamá-lo de , Executa_SQL. Definimos um método incluindo uma procedure no módulo de classe. Veja o código da procedure a seguir:

Sub Executa_SQL()

Dim strSQL As String
Dim db As Database
Dim qdfTemp As QueryDef
Dim rsResultado As Recordset

'Constroi a consulta SQL
strSQL = "SELECT Fornecedores.NomeDaEmpresa, Produtos.NomeDoProduto "
strSQL = strSQL & " FROM Fornecedores INNER JOIN (Categorias INNER JOIN Produtos"
strSQL = strSQL & " ON Categorias.CódigoDaCategoria = Produtos.CódigoDaCategoria)"
strSQL = strSQL & " ON Fornecedores.CódigoDoFornecedor = Produtos.CódigoDoFornecedor"
strSQL = strSQL & " WHERE Fornecedores.NomeDaEmpresa='" & NomedaEmpresa & "'"
strSQL = strSQL & " AND Categorias.NomeDaCategoria='" & NomedaCategoria & "'"

dbname = "c:\teste\nwind2000.mdb"
Set db = OpenDatabase(dbname)

Set qdfTemp = db.CreateQueryDef("")
qdfTemp.SQL = strSQL

Set rsResultado = qdfTemp.OpenRecordset(dbOpenSnapshot)
If rsResultado.RecordCount > 0 Then
   rsResultado.MoveFirst

  'Enumerando o recordset
  With rsResultado
    Do While Not .EOF
        strmensagem = strmensagem & .Fields(1) & vbCrLf
        .MoveNext
    Loop
   End With
Else
   strmensagem = "Não há dados para estes parâmetros ! "
End If

rsResultado.Close
qdfTemp.Close

End Sub

Analisando o código:

-Dimensionamos as variáveis objeto para acesso aos dados - db , qdftemp , rsresultado

Dim strSQL As String
Dim db As Database
Dim qdfTemp As QueryDef
Dim rsResultado As Recordset

-Construimos a consulta SQL que irá extrair o resultado com base nos parâmetros : NomedaEmpresa, NomedaCategoria

strSQL = "SELECT Fornecedores.NomeDaEmpresa, Produtos.NomeDoProduto "
strSQL = strSQL & " FROM Fornecedores INNER JOIN (Categorias INNER JOIN Produtos"
strSQL = strSQL & " ON Categorias.CódigoDaCategoria = Produtos.CódigoDaCategoria)"
strSQL = strSQL & " ON Fornecedores.CódigoDoFornecedor = Produtos.CódigoDoFornecedor"
strSQL = strSQL & " WHERE Fornecedores.NomeDaEmpresa='" & NomedaEmpresa & "'"
strSQL = strSQL & " AND Categorias.NomeDaCategoria='" & NomedaCategoria & "'"

-Definimos o caminho do banco de dados e a seguir o abrimos:

dbname = "c:\teste\nwind2000.mdb"
Set db = OpenDatabase(dbname)

- Limpamos a querydef e a seguir atribuimos a nossa consulta SQL para a QueryDef definida. Depois executamos a consulta para geração de um recordset do tipo snapshot
Set qdfTemp = db.CreateQueryDef("")
qdfTemp.SQL = strSQL
Set rsResultado = qdfTemp.OpenRecordset(dbOpenSnapshot)

-Se a consulta retorna valores , estes são exibidos , caso contrário uma mensagem alerta o usuário de não há dados para os critérios adotados.

Pronto já temos uma classe definida com duas propriedades , os nossos parâmetros e um método , que executa a nossa consulta, só nos resta usá-lo na prática.

Como cada módulo de classe tem um evento Initialize o qual contém código que é executado quando um objeto baseado na classe é criado e um evento Terminate quando o objeto é destruido. O código que será executado quando nosso objeto for destruido será o seguinte:

Private Sub Class_Terminate()

MsgBox strmensagem, Title:="Resultado da consulta para -  " _
& UCase(NomedaEmpresa), buttons:=vbExclamation

End Sub

Ele simplesmente exibe uma caixa de mensagem com o resultado da consulta.

Pondo a classe para Trabalhar

Vamos criar um projeto no VB para usar o módulo de classe criado. Inicie o VB e crie um projeto como o da figura a seguir:

Este projeto usa os seguintes controles:

- combo1 - cbofornecedor

- combo2 - cbocategoria

- commandbutton - Command1

A idéia é preencher as caixas de combinação , uma com o nome dos fornecedores e outra com as categorias existentes. O usuário irá selecionar o fornecedor e a categoria e clicar no botão Executa Consulta que irá chamar o método do nosso módulo de classe retornando o resultado desejado.

Para preencher as caixas de combinação usaremos a função - clssql.bas - definida em um módulo padrão. O código deste módulo é o seguinte:

Public db As Database
Public area As Workspace
Public Sub enchecombo(combo As Control,data As String,campo As String,Optional indice As
 Variant)
'-- cria variável recordset temporária
Dim arqtemp As Recordset
'-----limpa combo
combo.Clear
'-----abre tabela como Snapshot (economiza memoria)
Set arqtemp = db.OpenRecordset(data, dbOpenSnapshot)
'--inicia loop através da tabela---
If arqtemp.RecordCount > 0 Then
 Do Until arqtemp.EOF
    combo.AddItem arqtemp(campo)
     If Not IsMissing(indice) Then
       combo.ItemData(combo.NewIndex) = arqtemp(indice)
     End If
    arqtemp.MoveNext
 Loop
Else
  MsgBox "Não há dados ..."
  Exit Sub
End If
'---fecha recordset e seleciona primeiro opcao na combo
arqtemp.Close
combo.ListIndex = 0
'-----limpa memoria
Set arqtemp = Nothing
End Sub	

O evento Load do formulário - frmclsql - tem o seguinte código:

Private Sub Form_Load()
 
  Set area = DBEngine.Workspaces(0)
  dbname = "c:\teste\nwind2000.mdb"
  Set db = area.OpenDatabase(dbname)
    
  Call enchecombo(cbofornecedor, "fornecedores", "NomedaEmpresa", "códigodofornecedor")
  Call enchecombo(cbocategoria, "categorias", "NomedaCategoria", "códigodacategoria")
    
  End Sub

Nele abrimos o banco de dados e chamamos a função enchecombo definida no módulo - clssql.bas . A função recebe os parâmetros para preencher cada combobox com os valores referentes a tabela dos fornecedores e a tabela das categorias. Basta o usuário selecionar o fornecedor e a categoria e clicar no botão de comando Executa Consulta.

Aqui temos a combobox com os dados dos fornecedores:

.

O resultado da consulta é exibido em uma caixa de mensagem .( ver figura abaixo):

O código do botão Executa consulta é o seguinte:

Private Sub Command1_Click()
Dim objSQL As Cls_sql

'define a variavel objeto
Set objSQL = New Cls_sql

'define as novas propriedades do objeto
With objSQL
    .NomedaEmpresa = cbofornecedor.Text
    .NomedaCategoria = cbocategoria.Text
End With

'Retorna as propriedades
Debug.Print objSQL.NomedaEmpresa
Debug.Print objSQL.NomedaCategoria

'invoca  o metodo do objeto
objSQL.Executa_SQL

'destroi o objeto
Set objSQL = Nothing

End Sub

-Iniciamos dimensionando uma váriavel objeto baseada na nossa classe Cls_sql , ou seja , instânciamos um objeto da classe Cls_sql .

Dim objSQL As Cls_sql

-A seguir determinamos os valores para as propriedades do objeto - objSQL

With objSQL
 .NomedaEmpresa = cbofornecedor.Text
 .NomedaCategoria = cbocategoria.Text
End With
-Finalmente invocamos o método do objeto baseado na classe
objSQL.Executa_SQL

E acabou ! . Percebeu qual a vantagem dos módulos de classe ? Não ???

  1. Nosso código ficou encapsulado
  2. Podemos iniciar um novo objeto para cada consulta
  3. Se usassemos um módulo padrão teríamos que passar o controle para o módulo em cada consulta
  4. Podemos fácilmente reutilizar o código
  5. A depuração ficou mais fácil.

É obvio que a forma de exibir os resultados poderá ser melhorada , usando um msflexgrid por exemplo.

Este projeto completo está no Super CD VB.