You are on page 1of 144

DELPHI

Delphi

Wendel Wagner Martins Nogueira


Rua General Osrio n 2551
CEP 79824-040
Dourados - MS
Telefone (067) 421-1532 (067) 422-5122 ramal 238
Informatica@cpao.embrapa.br

ndice Analtico
INTRODUO AO MODELO CLIENTE/SERVIDOR..................................................................
Mudanas de Paradigmas.......................................................................................................................
Paradigma Computacional.................................................................................................................
Paradigma do Negcio........................................................................................................................
Evoluo da Arquitetura.........................................................................................................................
Arquitetura Time-Sharing....................................................................................................................
Arquitetura Resource-Sharing.............................................................................................................
Arquitetura Cliente/Servidor...............................................................................................................
Primeira Gerao Cliente/Servidor........................................................................................................
Segunda Gerao Cliente/Servidor.........................................................................................................
SGDB - SISTEMAS GERENCIADORES DE BANCO DE DADOS..............................................
Modelos de Banco de Dados...................................................................................................................
Sistema de Gerenciamento de Arquivos..............................................................................................
Banco de Dados Hierrquico..............................................................................................................
Banco de Dados de Rede.....................................................................................................................
Banco de Dados Relacional................................................................................................................
BANCOS DE DADOS RELACIONAIS..............................................................................................
Classificao...........................................................................................................................................
Corporativos........................................................................................................................................
Departamentais....................................................................................................................................
Locais ou Mveis.................................................................................................................................
Modelagem de Dados..............................................................................................................................
Normalizao.......................................................................................................................................
Propagao de chaves primrias........................................................................................................
Ferramentas.........................................................................................................................................
Criao da Base de Dados..................................................................................................................
Utilizando Interbase Windows ISQL...................................................................................................
Linguagem SQL......................................................................................................................................
Categorias da Linguagem SQL...........................................................................................................
Utilizando o Windows ISQL para definir o Banco de Dados.............................................................
Utilizando o Windows ISQL para acessar o Banco de Dados............................................................
Consistncia e Integridade dos Dados....................................................................................................
Integridade Referencial.......................................................................................................................
Domnio dos dados..............................................................................................................................
Regras de Negcio...............................................................................................................................
Utilizando o Windows ISQL para definir integridades e consistncias.............................................
Utilizando o Windows ISQL para testar as consistncias..................................................................
Distribuio da Consistncia e Integridade dos Dados.....................................................................
SQL EXPLORER..................................................................................................................................
Criao de Alias......................................................................................................................................
Visualizao e Edio de Dados.............................................................................................................
Definio de novos elementos................................................................................................................
Definio de Dicionrios de dados.........................................................................................................

Criao de um novo Dicionrio..........................................................................................................


Importao das definies do Banco de Dados..................................................................................
Definio das propriedades dos Attribute Sets...................................................................................
Utilizao do Dicionrio no Delphi....................................................................................................
TRABALHANDO COM BANCOS DE DADOS RELACIONAIS..................................................
Componentes da Arquitetura Cliente/Servidor......................................................................................
Conexes e Contextos.............................................................................................................................
Conexes e Contextos no Delphi........................................................................................................
Cursores e Result Sets.............................................................................................................................
Cursores e Result Sets no Delphi........................................................................................................
Transaes...............................................................................................................................................
Transaes no Delphi..........................................................................................................................
Concorrncia...........................................................................................................................................
Tipos de travamentos (locks)...............................................................................................................
Nveis de isolamento............................................................................................................................
Optimistic Lock....................................................................................................................................
Concorrncia no Delphi......................................................................................................................
PROJETANDO APLICAES CLIENTE/SERVIDOR..................................................................
Estrutura de uma Aplicao....................................................................................................................
Apresentao.......................................................................................................................................
Lgica do Negcio...............................................................................................................................
Gerenciamento de Dados....................................................................................................................
Vantagens da Organizao da Aplicao em Camadas..........................................................................
Estrutura de uma Aplicao Delphi........................................................................................................
Componentes visuais...........................................................................................................................
Componentes de Acesso base de dados...........................................................................................
Componente de ligao.......................................................................................................................
CONSTRUINDO APLICAES CLIENTE/SERVIDOR...............................................................
Utilizando Data Modules........................................................................................................................
Componente TDatabase..........................................................................................................................
Escolhendo entre TTable e TQuery.........................................................................................................
Abertura...............................................................................................................................................
Filtros...................................................................................................................................................
Transaes...........................................................................................................................................
Nmero de Tabelas Acessadas.............................................................................................................
Trabalhando com o TQuery....................................................................................................................
Utilizando Cached Updates....................................................................................................................
Utilizando o Componente TUpdateSQL.................................................................................................
Gravao Linha a Linha ou em Batch....................................................................................................
Trabalhando com o TTable.....................................................................................................................
FILTRANDO REGISTROS..................................................................................................................
QBE na mesma Tela de Manuteno......................................................................................................
Controlando os Estados da Tela..............................................................................................................
Tela de Consulta Especfica....................................................................................................................
Tela de Consulta..................................................................................................................................
Tela de Manuteno.............................................................................................................................
Recursos de LookUp............................................................................................................................
Buscando Registros de outras TabSheets............................................................................................
Controlando Transaes......................................................................................................................
Mantendo o Result Set da Consulta....................................................................................................
CONTROLANDO TRANSAES MASTER/DETAIL...................................................................
Tela de Consulta......................................................................................................................................
Tela de Manuteno................................................................................................................................
Lgica visual........................................................................................................................................
Controle visual da Transao.............................................................................................................
Transao - Lgica de negcio...........................................................................................................

Calculando o Valor Total do Pedido...................................................................................................


Regras de Negcio...............................................................................................................................
APNDICE A ( TRATAMENTO DE EXCEES )...................................................................127
Aplicaes robustas.......................................................................................................................127
Principais excees.......................................................................................................................127
Blocos de finalizao....................................................................................................................127
Gerando excees.........................................................................................................................127
Erros de Bancos de dados.............................................................................................................127
Aplicaes robustas.......................................................................................................................127

Introduo ao Modelo
Cliente/Servidor
Esse captulo mostra a evoluo na
arquitetura de computadores e a tendncia a
ambientes de aplicaes cada vez mais
distribudos.

Captulo

termo Cliente/Servidor foi inicialmente aplicado


para a
arquitetura de software que descrevia o processamento entre
dois programas. Nesse contexto, a aplicao cliente
requisitava um servio que era ento executado pelo programa
servidor. Entretanto, esse termo ainda no distinguia se o programa
cliente e o programa servidor estavam sendo executados em uma
mesma mquina ou em mquinas diferentes. Hoje, quando se fala
em cliente/servidor, est se referindo a um processamento
cooperativo distribudo, onde o relacionamento entre clientes e
servidores so relacionamentos entre componentes tanto de software
quanto de hardware. Portanto, estaremos interessados na
arquitetura cliente/servidor envolvendo duas ou mais mquinas em
um processo cooperativo para executar a aplicao.

Mudanas de Paradigmas
Paradigma Computacional
A viso tradicional da computao era centralizada na figura do
computador. que concentrava todos os servios e recursos fornecidos
aos usurios. O acesso a esses computadores era feito diretamente
pelo usurio atravs de teclados e monitores. Em alguns casos, essa
mquina era um equipamento poderoso capaz de atender vrios
usurios simultaneamente (Mainframe). Ou ento, eram pequenos
computadores isolados capazes de atender um nico usurio de cada
vez.

Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

Fig. 1.1: Arquitetura de Mainframe esquerda e um computador PC isolado direita. Ambos simbolizam a
viso computacional tradicional, onde o foco o computador.

Para que essas mquinas pudessem acompanhar a crescente


demanda de novos recursos e servios seria necessrio expandir seus
recursos de memria e processamento. No caso de se utilizar apenas
uma mquina servindo vrios usurios (Mainframe), seria difcil
expandir ainda mais seus recursos para suportar as novas demandas
tecnolgicas, principalmente a interface grfica. Por outro lado,
atravs de vrias mquinas isoladas, os servios teriam que ser
replicados e cada mquina incrementada para suportar tal
processamento. Alm disso, essa alternativa no prov uma maneira
de comunicao entre os usurios.
Para atender a necessidade crescente do usurio de novos servios e
recursos, surgiu-se a necessidade de interligar essas mquinas, para
que juntas pudessem fornecer um nmero maior de benefcios. Assim
comearam a aparecer ambientes de redes que permitiam a
distribuio de recursos e servios em locais diferentes, aumentando
a capacidade do usurio final. Com isso as mquinas que tinham que
ser equipamentos caros e poderosos podiam ser aos poucos
substitudas por vrias mquinas menores e de custo mais baixo, mas
que atravs de um processo cooperativo conseguiam prover
funcionalidade maior.
Com o nmero cada vez maior de computadores interligados, esses
deixaram de ser o foco do ambiente computacional e toda a ateno e
importncia foram destinadas s redes, elementos responsveis em
reunir os recursos e servios de vrias mquinas e disponibiliz-los
aos usurios de forma transparente e eficaz.

Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

Web
Informao

Dados

Rede
Data
WareHousing

Fig 1.2: As redes interligam os computadores fornecendo servios e recursos de forma escalar e
transparente para os usurios finais.

As redes permitem o crescimento do ambiente computacional de


forma flexvel e escalar, possibilitando a disponibilidade de novos
recursos e servios ao usurio final com uma certa transparncia.
Hoje, servios novos como Web, DataWareHousing e outros podem
ser mais facilmente agregados ao parque computacional e
disponibilizados a vrios usurios j pertencentes a rede. Nesse
modelo, o computador se torna apenas um meio de acesso a
infinidade de servios e recursos oferecidos por toda a rede.
Paradigma do Negcio
Do ponto de vista do negcio, as organizaes esto sendo afetadas
por grandes mudanas no mercado. As empresas precisam se
preparar para a globalizao da economia e serem mais rpidas na
tomada de deciso tornando-se mais competitivas.

Fig 1.3: Globalizao da economia. A empresas atuam mundialmente, no havendo mais limites geogrficos
para a competitividade.

Uma ateno especial deve ser dada aos clientes que exigem
produtos cada vez mais especficos e personalizados. Para suportar
essa presso competitiva do mercado, os gerentes de negcio devem
estar munidos de informaes e ferramentas que os permitam tomar
Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

decises de forma rpida e precisa. Informaes especficas e


fundamentais devem ser extradas da base de dados corporativa
para darem suporte a essas decises. Portanto, a velocidade que a
informao trafega pela empresa muito importante e um ambiente
capaz de prover essa funcionalidade se torna realmente necessrio.

Informao

Fig: 1.4: Descentralizao da informao.

Com o objetivo de se tornarem mais competitivas, as empresas tm


procurado uma forma de se organizar melhor promovendo um
conjunto de mudanas organizacionais e estruturais em toda a
corporao. Tcnicas como Reengenharia
so normalmente
aplicadas, com o objetivo de reorganizar a empresa e seus processos
de negcio. Essas organizaes do negcio so muitas vezes
acompanhadas de uma restruturao dos sistemas de informtica e
de todo o ambiente computacional. preciso estruturar um novo
ambiente descentralizado e eficiente que proporcione a
funcionalidade e a velocidade desejada para viabilizar a corporao
em um mercado cada vez mais abrangente e competitivo.

Evoluo da Arquitetura
O objetivo da arquitetura cliente/servidor proporcionar um
ambiente capaz de compartilhar os recursos e servios de vrias
mquinas interligadas e prover uma maneira cooperativa de executar
as aplicaes dos usurios finais.
A seguir sero mostradas algumas arquiteturas para explicar a forte
tendncia a ambientes distribudos cooperativos.
Arquitetura Time-Sharing
Essa arquitetura baseada em um processamento centralizado. Uma
mquina, chamada de hospedeiro, responsvel por rodar todos os
programas e gerenciar
todos os recursos. O tempo de
processamento compartilhado pelos programas, simulando uma
execuo em paralelo. Os usurios tm acesso a esses servios e
recursos atravs de terminais conectados localmente ou
Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

remotamente. Esse terminais no possuem nenhuma capacidade de


processamento e consistem basicamente de uma tela, um teclado e
do hardware necessrio para se comunicar com o hospedeiro. Essa
arquitetura permite o compartilhamento da base de dados da
aplicao tornando a informao disponvel de qualquer terminal.
Entretanto, com o surgimento de novas necessidades, como a
interface grfica e outros servios que necessitam cada vez mais de
processamento, esse modelo comeou-se a tornar economicamente e
fisicamente invivel j que todo o processamento realizado em uma
nica mquina. Os exemplos mais conhecidos dessa arquitetura so
os sistemas em Mainframes e alguns sistemas em UNIX.

Processamento

Fig 1.5. Arquitetura Time-Sharing

Arquitetura Resource-Sharing
Essa arquitetura consiste de vrios computadores (estaes de
trabalho) interligados, sendo cada um capaz de realizar seu prprio
processamento. Alguns desses computadores so responsveis em
compartilhar e gerenciar recursos tais como impressora, disco, etc
(servidores de rede).
Entretanto, a rede no utilizada para proporcionar um
processamento
cooperativo
entre
as
mquinas.
Todo
o
processamento da aplicao ainda feito por uma nica mquina,
havendo apenas o compartilhamento de recursos. Atravs dessa
arquitetura possvel compartilhar a base de dados da aplicao,
permitindo o acesso por vrias pessoas simultaneamente. Mas como
todo o processamento dos dados realizado em cada mquina, a
necessidade de um volume maior de informaes torna invivel o
trfego de informaes pela rede. Para resolver esse problema seria
necessrio que a mquina responsvel em armazenar os dados
fizesse um processamento local capaz de enviar uma quantidade
menor de dados para a mquina que est processando a aplicao.
Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

Servidor de Rede

Aplicaes

Processamento
Processamento
Fig. 1.6: Arquitetura Resource-Sharing.

Arquitetura Cliente/Servidor
Essa arquitetura tambm consiste de vrios computadores, cada um
com seu prprio processamento, interligados em rede. A diferena
bsica para a arquitetura Resource-Sharing que aqui j comea a
haver um processamento distribudo cooperativo. Parte do
processamento, que era feito pela mquina da aplicao, feito
agora pela prpria mquina responsvel pelo armazenamento e
distribuio da informao, diminuindo assim o trfego de
informaes na rede. Portanto, pode-se e deve-se selecionar os dados
que sero enviados para o usurio para uma melhor eficincia do
ambiente.
Esse modelo j comea a retirar partes especficas de processamento
das aplicaes que eram executadas pelas mquinas clientes,
centralizando-as nas mquinas de localizao fsica mais adequada,
garantindo assim uma melhor distribuio do processamento e
utilizao do ambiente. Atravs dessas especializaes garante-se
tambm um melhor gerenciamento e facilidade de manuteno dos
servios devido a sua concentrao em um ou poucos locais fsicos.

Delphi Client/Server

I N T R O D U O

A O

M O D E L O

Servidor de Banco
de Dados (DBMS)

C L I E N T E / S E R V I D O R

Servidor de Rede

Processamento
Processamento

Processamento

Aplicaes
Processamento
Fig. 1.7 Arquitetura Cliente/Servidor.

Podemos citar alguns benefcios que esta nova arquitetura traz para
o ambiente computacional das empresas:
Permite s corporaes alavacarem a tecnologia de computadores
desktops. Hoje, as estaes de trabalho j possuem um poder
computacional considervel, por um custo muito mais baixo, antes
s disponvel em Mainframes.
Permite que o processamento seja feito mais prximo da origem
dos dados reduzindo o trfego na rede.
Facilita a utilizao de aplicaes grficas e multimdias. Isto
permite a construo de aplicaes que excedem as expectativas
dos usurios proporcionando-lhes um real aumento de
produtividade.
Permite e encoraja a utilizao de sistemas abertos, j que clientes
e servidores podem rodar em diferentes hardwares e softwares,
livrando as corporaes de arquiteturas proprietrias.
Entretanto, essa arquitetura ainda no perfeita. Algumas
caractersticas necessrias para um completo processo distribudo
ainda no foram observadas nesse modelo que ainda apresenta
algumas deficincias:

Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

Se uma poro significante da lgica da aplicao for movida para


o servidor, esse se torna um gargalo assim como na arquitetura
de Mainframe. Para resolver esse problema seria necessrio
uma melhor distribuio e gerenciamento do processamento da
lgica da aplicao. Isto d origem a uma nova arquitetura
chamada Segunda Gerao Cliente/Servidor.
O processo de construo de aplicaes distribudas bem mais
complexo que o desenvolvimento de aplicaes no distribudas
devido ao maior nmero de parmetros a serem definidos, ao
desconhecimento da tecnologia e a falta de padres e ferramentas
que auxiliem essa ambientao. Portanto, muito tempo pode ser
consumido no processo de definio e construo do ambiente de
desenvolvimento. Esse tempo muitas vezes subestimado pelas
empresas devido ao desconhecimento e as falsas propagandas dos
vendedores de ferramentas.

Primeira Gerao Cliente/Servidor


Podemos
definir
cliente/servidor
como
uma
arquitetura
computacional que visa distribuir os recursos e o processamento de
forma inteligente com o objetivo de otimizar o desempenho da rede e
dos sistemas, maximizando a utilizao dos recursos de cada
mquina e fornecendo uma base slida e flexvel para a implantao
de um nmero crescente de servios.
Algumas implementaes desse tipo j vm sendo utilizadas em
vrias empresas e so conhecidas como a primeira gerao
cliente/servidor.
Para compartilhar recursos, como disco e impressora, so utilizados
Servidores de Arquivos na rede. Estes so sistemas com a funo de
processar as requisies aos arquivos e impressoras e gerenciar seu
acesso e distribuio.
Alm disso, parte do processamento das aplicaes tambm foi
distribudo. Alguns servios de manipulao e gerenciamento de
dados foram retirados das aplicaes e colocados em pontos
centralizados conhecidos como Servidores de Banco de Dados,
tornando o processamento dos dados mais prximo do seu local de
armazenamento. Os sistemas que fornecem tais servios foram
chamados de Sistemas Gerenciadores de Banco de Dados - SGDB.
Basicamente, a primeira gerao de cliente/servidor se caracteriza
por essa distribuio do processamento da aplicao entre dois
componentes: a estao de trabalho do usurio e o servidor de banco
de dados. medida que a arquitetura cliente/servidor evolui, novas
partes da aplicao vo sendo distribudas e novos elementos vo
aparecendo no ambiente.
Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

Segunda Gerao Cliente/Servidor


Hoje, a tecnologia cliente/servidor j caminha para sua segunda
gerao. Essa gerao explora mais o ambiente de rede e suas
mquinas. Surgem novos servidores coma finalidade de retirar das
estaes de trabalho grande parte do processamento que elas
realizam. Os principais elementos dessa nova arquitetura so os
servidores de aplicao e os servidores Web.
Os servidores de aplicao so responsveis por retirar o restante da
camada de manipulao de dados que ainda havia na estao cliente.
Alm disso, tem o objetivo de concentrar a lgica de negcio, antes
distribuda entre a estao cliente e o servidor de banco.
Normalmente, esse trabalho no feito por um nico servidor de
aplicao e sim por um conjunto de servidores onde o processamento
balanceado atravs de elementos chamados Midlleware. Desta
forma resta para a estao cliente, o processamento da interface
visual com o usurio, deixando-a mais leve, exigindo uma menor
configurao e melhorando seu desempenho.
Os servidores Web tentam ir mais longe ainda, permitindo retirar
das estaes de trabalho at parte da lgica da interface visual,
deixando-as responsveis apenas por interpretar o cdigo HTML
enviado pelos servidores. Entretanto, com a utilizao de
componentes como Java e ActiveX, parte do processamento pode
retornar estao de trabalho.
Essas novas tecnologias trazem mais recursos, mas tornam o
ambiente mais complexo e difcil de ser implementado. preciso
estar bem certo do que se pretende e no fazer uso de uma
tecnologia mais nova sem a conhec-la direito ou sem a real
necessidade de utiliz-la.

Delphi Client/Server

I N T R O D U O

A O

M O D E L O

C L I E N T E / S E R V I D O R

SGDB - Sistemas
Gerenciadores de Banco de
Dados
Esse captulo apresenta os sistemas
gerenciadores de banco de dados, seus
servios, recursos e modelos.

Captulo

s SGBDs so sistemas responsveis em armazenar os dados e


fornecer servios capazes de manipul-los. Para armazenar
os dados em um disco, os SGBDs possuem uma forma
estruturada padro de representar os registros e campos, fornecendo
servios que permitem a criao e alterao dessas definies de
dados. Fornecem ainda, um mecanismo interno para manter os dados
no disco e para saber onde est cada elemento em particular.
Tambm so responsabilidades do SGDBs disponibilizar servios para
incluir, classificar, atualizar e eliminar os dados armazenados.
Pela forma com que os dados so armazenados e de como eles se
relacionam podemos classificar os SGBDs atravs de vrios modelos
at chegarmos aos bancos de dados relacionais.

Modelos de Banco de Dados


Analisando os diversos modelos de banco de dados, pode-se notar
uma evoluo no relacionamento entre os dados, eliminando cada
vez mais a redundncia e proporcionando mais flexibilidade e
portanto uma maior facilidade de manuteno das definies de
dados. Para melhor entendermos os modelos vamos utilizar os dados
das trs guias de pedidos da fig. 2.1.

Delphi Client/Server

10

Fig. 2.1: Guia de Pedido.

Sistema de Gerenciamento de Arquivos


Esse o nico sistema que descreve como os dados so
armazenados. Nesse modelo cada campo ou item de dado
armazenado seqencialmente no disco em um nico e grande
arquivo. Para encontrar um item de dado necessrio verificar cada
elemento desde o incio do arquivo. A nica vantagem desse mtodo
sua simplicidade, j que seu formato muito se parece com o de um
arquivo texto onde as palavras se encontram escritas em uma
determinada seqncia. Entretanto, por causa dessa simplicidade,
muitos dados tm que ser escritos repetidamente gerando uma
enorme redundncia que dificulta a manuteno e a integridade dos
dados. No h tambm nenhuma indicao de relacionamento entre
os dados, e o programador precisa saber exatamente como os dados
esto armazenados para poder acess-los de forma consistente. Alm
disso, sua estrutura fsica rgida, o que dificulta alteraes na
definies dos dados, gerando a necessidade de reconstruir todo o
arquivo.

1 | 20/10/96 | Ana Maria Lima | 999.876.555-22 | Caneta | 10


|10,00 | Lpis | 5 | 5,00 | 15,00 | 2 | 21/10/96 | Maria Jos |
111.111-22 | Caneta | 15 | 15,00 | Caderno | ...
111.
Fig. 2.2: Sistema de Gerenciamento de Arquivos.
Delphi Client/Server

11

Banco de Dados Hierrquico


Nesse modelo, os dados so organizados em uma estrutura de rvore
que se origina a partir de uma raiz. Essa estrutura identifica as
relaes pai-filho entre os vrios itens do banco de dados,
mostrando assim suas vantagens sobre o modelo de gerenciamento
de arquivos. No modelo hierrquico possvel definir relaes deum-para-muitos que facilita e acelera o processo de pesquisa dos
dados. Para encontrar uma informao, no mais necessrio
percorrer o arquivo inteiro. Basta examinar o item pedido, decomplo em componentes e descer pelos ramos necessrios at encontr-lo.
Esse mtodo, tambm facilita a insero de novos dados, devido aos
relacionamentos entre os campos serem feitos atravs de ponteiros.
Para inserir um novo elemento basta alterar os ponteiros dos
relacionamentos entre pais e filhos.

Raiz
1
20/10/96 15,00

Ana
Maria

Caneta

10

10,00 5

999.876.555-22

Lpis

2
21/10/96 65,00
111.111.111-22

5,00

3
Maria
Jos
15

Caneta
15,00

Caderno
5

50,00

Fig. 2.3: Banco de Dados Hierrquico.

Entretanto, esse mtodo ainda possui alguns problemas. Cada nvel


da rvore inicialmente definido e qualquer alterao na estrutura
desses nveis uma tarefa difcil. Alm disso, o problema da
redundncia de dados ainda no foi resolvido, j que esse modelo no
implementa relaes de muitos-para-muitos. No exemplo, podemos
notar a repetio de algumas informaes, o que dificulta a
integridade e manuteno dos dados.
Banco de Dados de Rede
O modelo de rede descreve, conceitualmente, os banco de dados nos
quais permitem relaes de muitos-para-muitos entre os elementos
de dados. Desta forma cada item possui um ponteiro para os itens
com os quais se relaciona, eliminando assim a necessidade de
qualquer tipo de redundncia de dados. O grande problema desse
modelo a sua complexidade devido a flexibilidade existente em suas
relaes. Quando o volume de dados comea a crescer, os
relacionamentos entre os itens de dados ficam cada vez mais
complexos, tornando sua visualizao e entendimento cada vez mais
difceis.

Delphi Client/Server

12

Raiz
1

Ana
Maria

Lpis

Caneta

Caderno

Maria
Jos
111.111.111-22

999.876.555-22

Fig. 2.4: Banco de Dados de Rede.

Banco de Dados Relacional


Em 1969, foi publicado pelo Dr. E. F. Codd, o primeiro artigo que
definia um modelo com base no conceito matemtico dos conjuntos
relacionais. A partir desse artigo, o modelo relacional tem sido
refinado, at que 1985 o prprio Dr. Codd lanou as 12 regras que
definiam um banco de dados relacional. Em 1990, foram publicadas
as 333 regras que so subconjuntos e expanses das 12 regras
originais.
O modelo relacional abandona o conceito de relaes pai-filho
feitas diretamente entre os dados e os organiza em conjuntos
matemticos lgicos de estrutura tabular. Nesse modelo, cada item
de dado pertence a uma coluna da tabela e uma linha da tabela
composta por vrios elementos diretamente relacionados.
As tabelas tambm se relacionam atravs de funes matemticas
como JOINs e UNIONs. Para fazer esse relacionamento parte dos
dados, que identificam unicamente o registro da
tabela, so
repetidos dentro da outra tabela.
As vantagens desse mtodo so sua simplicidade e flexibilidade nas
definies das relaes entre os vrios itens de dados, j que no so
feitos diretamente entre os dados e sim entre as tabelas. Entretanto,
esse mtodo no elimina por completo a redundncia de dados, j
que no mnimo os relacionamentos entre as tabela so feitos atravs
da repetio de parte dos dados. Alm dessa redudncia, fica a cargo
do projetista do banco de dados se mais repeties de dados iro ou
no fazer parte do modelo. O processo de fragmentao dos dados, a
fim de serem organizados em subconjuntos (tabelas), conhecido
como normalizao.

Delphi Client/Server

13

Pedido
NumPed
1
2
3

Itens
Data

20/10/96
21/10/96
22/10/96

Valor

Cliente

15,00
65,00
25,00

1
2
2

Cliente
Codigo
1
2

Nome

NumPed

Produto

1
1
2
2
3
3

Caneta
Lpis
Caneta
Caderno
Lpis
Caderno

CPF

Ana Maria Lima 999.876.555-22


Maria Jos
111.111.111-22

Fig. 2.5: Banco de Dados Relacional.

Delphi Client/Server

14

Quantid. Valor
10
5
15
5
15
1

10,00
5,00
15,00
50,00
15,00
10,00

Bancos de Dados
Relacionais

Captulo

Esse captulo apresenta algumas


caractersticas dos bancos de dados
relacionais.

s bancos de dados relacionais vm se tornando um padro no


mercado, servindo como base de dados para a maioria dos
sistemas das empresas. A cada dia, mais ferramentas so
construdas para tirar proveito dessa tecnologia, fazendo surgir um
nmero crescente de recursos e produtos a serem oferecidos para os
usurios. Pode-se dizer ento, que esta uma tecnologia slida e
consistente e que ir acompanhar o mercado por um longo perodo
de tempo. No entanto, uma tecnologia que continua evoluindo com
o objetivo de disponibilizar as informaes para os usurios de
maneira eficiente, viabilizando o negcio da corporao e
descentralizando as tomadas de deciso.

Classificao
Quanto a capacidade, recursos e facilidade de uso, os banco de dados
relacionais podem ser divididos em trs categorias: corporativos,
departamentais e locais. Em uma mesma empresa todas as
categorias podem coexistir e cooperar entre si para juntas formarem
uma poderosa base de dados distribuda.

Delphi Client/Server

15

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Corporativo

Departamentais

Locais

Fig 3.1: Classificao dos bancos de dados.

Corporativos
Sistema de banco de dados para atender toda a corporao. Os
principais sistemas dessa categoria so: DB2, Oracle, Sybase,
Informix e Ingres. Estes bancos devem ser capazes de armazenar e
manipular de maneira eficiente grandes volumes de dados e permitir
acesso rpido a um nmero elevado de usurios conectados ao
mesmo tempo. Devido a sua enorme capacidade, os preos desses
bancos so tambm mais elevados e normalmente necessitam de
profissionais especializados para configur-los e monitor-los no dia a
dia.
Departamentais
Sistemas de banco de dados capazes de atender as requisies de
dados a nvel de um departamento. Nesse patamar, existem bancos
de dados mais baratos e mais fceis de configurar, entre eles esto:
Interbase, SQLServer, SQLBase.
A maioria dos fornecedores de bancos de dados da categoria
corporativa esto tambm disponibilizando verses departamentais
para disputar esse mercado, trazendo como vantagem a possibilidade
de permanecer com o mesmo fornecedor em todos os nveis,
tornando o ambiente mais integrado. Por outro lado, as principais
vantagens dos bancos desenvolvidos especialmente para essa
plataforma so: a facilidade de configurao, o preo dos produtos e
a menor requisio de recursos de hardware.

Delphi Client/Server

16

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Locais ou Mveis
Sistemas de banco de dados capazes de rodar na mesma mquina
que a aplicao. Nessa categoria os principais requisitos so: ser um
banco de dados bastante leve que gaste poucos recursos do sistema
operacional e da mquina(memria, processamento, etc) e que no
necessite de gerenciamento podendo rodar basicamente com sua
configurao original.
Nessa categoria podemos citar os bancos Interbase, SQLBase e
SQLAnywhere. Os fornecedores corporativos tambm esto
descendo para esse nvel. Porm, para manter os bancos de dados
com os mesmos recursos tem sido difcil torn-los leves e fceis
suficientemente. Alguns fornecedores possuem at sistemas de
banco de dados diferentes para cada plataforma.
Como esses bancos so instalados na mesma mquina possvel sua
utilizao em notebooks. Isso permite as empresas manterem a
mesma tecnologia de banco de dados relacional em aplicaes
mveis, facilitando a integrao com a base corporativa da empresa.
Deve-se notar entretanto, que no est se fazendo uso da arquitetura
cliente/servidor nesse caso, j que a mesma mquina utilizada para
rodar o aplicativo e o sistema gerenciador de banco de dados. A
principal vantagem dessa utilizao sobre a utilizao de base de
dados de arquivos, que a ltima no possui recursos suficientes
para manter a integridade dos dados e das transaes. Os sistemas
gerenciadores de banco de dados so tambm menos factveis a
corrupo de dados e falhas do que os sistemas baseados em
arquivos onde o gerenciamento feito por cada aplicao. Outra
vantagem prover mais facilidade de integrao com as bases
departamentais e corporativas, por utilizarem a mesma tecnologia.
Entretanto, os SGDBs possuem um custo adicional e exigem uma
configurao de mquina melhor devido a um nmero maior de
camadas que tero que ser percorridas para se chegar ao dado.

Modelagem de Dados
Modelagem de dados o processo utilizado para definir a estrutura
do banco de dados atravs da distribuio dos dados nas tabelas.
Existem maneiras diferentes de se definir o mesmo conjunto de
dados, e uma boa modelagem de dados pode facilitar bastante o
desenvolvimento das aplicaes. Vamos ressaltar aqui, dois pontos
que devem ser observados na construo do modelo: o processo de
normalizao e a propagao de chaves primrias.

Delphi Client/Server

17

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Normalizao
Tcnica de anlise e organizao de dados que visa determinar a
melhor composio para uma estrutura de dados. Os principais
objetivos dessa tcnica so:
Eliminar anomalias que dificultam as operaes sobre os dados;
Minimizar as redundncias
inconsistncias;

os

conseqentes

riscos

de

Reduzir e facilitar as manutenes.


Como exemplo, vamos utilizar um modelo, no qual todos os dados
foram colocados em uma nica tabela atravs dos seguintes campos:

NumPed, DataPed, ValorPed, NomeCliente, CPFCliente,


NomeProduto1, PreoProduto1, Quantidade1, ValorItem1,
NomeProduto2, PreoProduto2, Quantidade2, ValorItem2,...,
NomeProduto5, PreoProduto5, Quantidade5, ValorItem5

Primeira Forma Normal

Uma tabela est na primeira forma normal se no possuir grupos de


atributos repetitivos.

Chave

D
C

D
C

Grupo
Repetitivo
Fig. 3.2: Presena de grupos repetitivos em uma tabela relacional.

Para resolver esse problema, deve-se remover os grupos repetitivos


para uma outra tabela A chave primria dessa nova tabela pode ser
formada atravs da chave primria da tabela original mais um
conjunto de novos atributos.

Delphi Client/Server

18

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Estrutura Original
Nova Estrutura

Obs.: Chave primria da nova estrutura =


atributo-chave da estrutura original + atributo
identificador do grupo repetitivo.
Fig. 3.3: Eliminao de grupos repetitivos em uma tabela.

Desta forma, elimina-se a deficincia do primeiro modelo que


limitava o nmero de itens de produto que um pedido podia ter.
Nessa nova estrutura no h limite para os itens, mas continua-se
mantendo a relao que existia entre os itens e o pedido atravs da
incluso do nmero do pedido na nova tabela.

*NumPed
*NumPed
DataPed
*NomeProduto
ValorPed
PreoProduto
NomeCliente Quantidade
CPFCliente
ValorItem
Fig. 34: Exemplo na primeira forma normal.

Segunda Forma Normal

Uma tabela est na segunda forma normal quando sua chave


primria no for composta ou quando todos os atributos no chaves
forem funcionalmente dependentes da chave inteira e esta estiver na
primeira forma normal. Um atributo b dependente
funcionalmente de a se dado o valor de a, o valor de b puder
ser determinado.

Delphi Client/Server

19

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Chave Composta

C depende de A e B
D depende s de A
Fig. 3.5: Modelo na 1FN mas no em 2FN. Presena de atributos dependentes funcionalmente de apenas
parte da chave primria.

Para resolver esse problema, basta remover os atributos que


dependem apenas de parte da chave primria para uma nova tabela.
A chave primria da nova tabela passa a ser ento essa parte da
chave primria da tabela original da qual o atributo dependia.

*
D

Estrutura Original

Nova Estrutura

Obs.: Chave primria da nova estrutura =


atributo do qual o(s) atributo(s)
removido(s) depende(m).
Fig 3.6: Restruturao do modelo na segunda forma normal

No exemplo de pedido, isso ocorre com a coluna PRECOPRODUTO,


que depende apenas do NOMEPRODUTO e no depende de
NUMPED. Dado o nome do produto possvel saber seu preo no
importando o pedido que o produto pertena. Assim o preo do
produto seria repetido em vrios registros da estrutura
desnecessariamente e dificultando a manuteno e consistncia dos
dados. Para evitar essa redundncia, deve-se criar uma nova
estrutura e a chave primria dessa nova tabela seria o
Delphi Client/Server

20

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

NOMEPRODUTO, porm por questes de eficincia pode-se criar


uma nova coluna CODPRODUTO de tamanho menor para fazer a
ligao entre as tabelas. Alm da eficincia, esse procedimento
possibilita alteraes futuras nos nomes dos produtos, j que esses
no so mais usados como chave primria.

*NumPed
*NumPed
DataPed
*CodProduto
ValorPed
Quantidade
NomeCliente ValorItem
CPFCliente

*CodProduto
NomeProduto
PreoProduto

Fig. 3.7. Exemplo na segunda forma normal.

Terceira Forma Normal

Uma tabela est na terceira forma normal quando est na segunda


forma normal e no possui nenhum atributo no chave dependendo
funcionalmente de outro atributo no chave.

C depende de B que no chave.


Fig. 3.8: Presena de atributos dependentes funcionalmente de outros atributos no-chave.

Para resolver esse problema, basta remover os atributos que


dependem de outros atributos no chave para uma nova tabela. A
chave primria da nova tabela passa a ser ento o atributo no chave
que possui dependentes.

Delphi Client/Server

21

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Estrutura Original

Nova Estrutura

Obs.: Chave primria da nova estrutura =


atributo do qual o(s) atributo(s) removido(s)
depende(m).
Fig. 3.9: Restruturao do modelo na terceira forma normal.

No exemplo apresentado, h uma dependncia funcional do


CPFCLIENTE com o NOMECLIENTE, ou vice-versa j que ambos so
candidatos a definir unicamente um cliente. Dado o valor de
NOMECLIENTE possvel determinar o valor de CPFCLIENTE
portanto esse no depende da chave primria NUMPED. Para
eliminar tal redundncia, deve-se criar uma outra estrutura e colocar
o modelo na terceira forma normal.

Pedido
*NumPed
DataPed
ValorPed
CodCliente

Cliente
*CodCliente
NomeCliente
CPFCliente

ItemPedido
*NumPed
*CodProduto
Quantidade
ValorItem

Produto
*CodProduto
NomeProduto
PreoProduto

Fig. 3.10: Exemplo na terceira forma normal.

Delphi Client/Server

22

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Existem outras formas normais, entretanto, colocando-se o modelo na


terceira forma normal j possvel tratar os dados de forma
consistente e segura provendo uma certa facilidade no
desenvolvimento.
Propagao de chaves primrias
Outro processo que deve ser bem definido quanto a forma de
propagao de chaves entre as tabelas. No exemplo anterior pode-se
observar que a chave primria da tabela Item foi formada pela
propagao da chave primria das tabelas Pedido e Produto.

ItemPedido
*NumPed
*CodProduto
Quantidade
ValorItem
Fig. 3.11: Tabela de Itens. Chave primria composta pela propagao das chaves primrias de outras
tabelas.

Essa tcnica de propagar as chaves primrias tornando-as tambm


chaves primrias na tabela destino pode facilitar consultas
diminuindo o nmero de tabelas utilizadas no comando. Imagine uma
tabela de histricos de itens que armazenasse cada atualizao que
ocorresse em cada item. Seria fcil ento consultar os histricos de
um determinado pedido ou produto, sem a necessidade de utilizar
uma outra tabela, j que essa tabela possui os campos NUMPED e
CODPRODUTO.

Hist Itens
*NumPed
*CodProduto
*Data
Fig 3.12: Tabela de Hist. Itens

Entretanto, esse mtodo pode tornar mais difcil a construo das


aplicaes de incluso e manuteno dos dados.
Uma outra alternativa para montar a chave primria da tabela de
itens criar uma outra coluna com um nmero seqencial do sistema
Delphi Client/Server

23

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

e substituir a coluna CODPRODUTO. Desta forma o campo


CODPRODUTO seria utilizado apenas para se fazer o relacionamento
entre as tabelas.

ItemPedido
*NumPed
*NumSeq
CodProduto
Quantidade
ValorItem
Fig 3.13 Tabela de Itens com chave primria alternativa.

Assim, definindo novamente a tabela de histrico de itens ficaria da


seguinte forma:

Hist Itens
*NumPed
*NumSeq
*Data
Fig 3.14 Tabela de Hist. De Itens.

Percebe-se, agora, que no h informao na tabela de histrico de


itens sobre o produto. Para consultar os registros de um determinado
produto necessrio fazer um join com a tabela de Itens,
dificultando a consulta mas facilitando a construo das telas de
manuteno. Alm disso, esta alternativa permite que o usurio
altere o produto de um item j que esse no mais parte da chave
primria.
Concluindo, no existe uma melhor forma de se definir as chaves
primrias. Existe a melhor alternativa para um determinado caso.
Ferramentas
Para modelar e definir a base de dados, vrias ferramentas CASE
fornecem um diagrama de Entidade-Relacionamento que permite
definir as entidades que iro originar as tabelas do banco de dados e
seus relacionamentos. Cria-se graficamente todos os elementos
necessrios, gerando uma documentao do modelo atravs de um
dicionrio de dados. Essas informaes podem ser utilizadas para
Delphi Client/Server

24

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

continuar o processo de definio do sistema. Em alguns casos,


permitindo a gerao da base de dados e at mesmo a gerao da
aplicao.
Criao da Base de Dados
Algumas ferramentas CASE permitem a gerao do script da base
de dados. Esse script um arquivo texto com as definies das
tabelas, colunas e outros elementos escritos na linguagem SQL
suportada pela base de dados a ser utilizada. Atravs de ferramentas
do sistema de banco de dados possvel criar a base de dados
simplesmente abrindo esse arquivo script e executando-o.
Utilizando Interbase Windows ISQL
Para o Interbase da Borland pode-se utilizar a ferramenta Windows
ISQL para l o arquivo script e criar o banco de dados. O Windows
ISQL uma ferramenta que permite gerenciar, definir e manipular o
banco de dados interativamente atravs de comandos SQL ou atravs
de opes contidas no Menu.

Fig. 3.15: Interactive SQL program.

Pode-se criar um novo banco de dados atravs da opo File/Create


Database do Menu.

Delphi Client/Server

25

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Use Local Engine para definir um banco de dados local e Remote


Server caso o banco de dados esteja em um servidor remoto na rede.
Na opo Database entre o nome do banco de dados com seu
respectivo diretrio. A seguir entre com um usurio/senha que tenha
permisso para criar um novo banco de dados.

Fig. 3.16. Criao de um novo banco de dados.

A seguir deve-se executar o arquivo script vendas.sql atravs da


opo do Menu File/Run an ISQL Script. Assim o programa ir
criar as tabelas, colunas e outras definies presentes no arquivo. A
estrutura criada representada pelo modelo de dados da figura
3.12.

Delphi Client/Server

26

S G D B

S I S T E M A S

G E R E N C I A D O R E S

UF
*UF_SG
UF_NM

CIDADE
*CID_CD
*UF_SG
CID_NM
CID_NUMHABITANTES

PESSOA
*PES_CD
PES_NM
PES_TP
PES_CGCCPF
PES_LOGRADOURO
PES_NUMERO
PES_COMPLEMENTO
PES_BAIRRO
CID_CD
UF_SG

CLIENTE
*PES_CD
CLI_LIMITECREDITO
CLI_DEBITO

D E

B A N C O

D E

VENDEDOR
*PES_CD
VEN_VENDAS
VEN_PERCCOMISSAO
VEN_COMISSAO

PEDIDO
*PED_CD
PED_DT
PED_VALOR
PED_TIPO
PES_CD_CLI
PES_CD_VEN

DAD O S

PRODUTO
*PRO_CD
PRO_NM
PRO_PRECO
PRO_ESTOQUE

ITEM
*PED_CD
*PRO_CD
ITE_VALOR
ITE_QUANT

Fig. 3.17: Modelo de Dados Exemplo.

Linguagem SQL
Para acessar os bancos de dados relacionais foi desenvolvida uma
linguagem chamada SQL. O Objetivo dessa linguagem fornecer
uma forma padro de acesso aos bancos de dados, no importando a
linguagem com que esses tenham sido desenvolvidos. Apesar da
tentativa de se tornar um padro (ANSI), cada fornecedor hoje possui
uma srie de extenses que tornam as vrias verses incompatveis
entre si. Por isso, no pense que uma aplicao construda para
acessar um determinado banco de dados de um fornecedor, ir
acessar tambm, sem qualquer modificao, o banco de dados de um
outro fornecedor. Isto s possvel se a aplicao utilizar somente a
parte comum (ANSI) da linguagem SQL, mas isto faz com que ela
perca vrios recursos importantes disponveis em cada verso SQL
de fornecedores diferentes. Alguns bancos de dados j suportam o
padro SQL ANSI-92 que j mais abrangente numa tentativa de
facilitar o processo de deixar transparente a base de dados utilizada
pela aplicao. Entretanto, alguns fornecedores ainda no fornecem
suporte ao SQL ANSI-92 de forma completa porque teriam que
alterar partes estruturais de seus sistemas gerenciadores.
Categorias da Linguagem SQL
A linguagem SQL se divide em trs categorias:
DDL ( Linguagem de Definio de Dados). Parte da linguagem com
comandos para criao das estruturas de dados como as tabelas,
colunas, etc. Ex: CREATE TABLE.

Delphi Client/Server

27

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

DML ( Linguagem de Manipulao de Dados). Parte da linguagem


com comandos para acessar e alterar os dados armazenados no
banco de dados. Os principais comandos dessa categoria so:
SELECT, UPDATE, INSERT, DELETE.
DCL ( Linguagem de Controle de Dados). Parte da linguagem com
comandos para definir usurios e controlar seus acessos aos
dados. Ex: GRANT.
Utilizando o Windows ISQL para definir o Banco de Dados
O arquivo script utilizado para criao do banco de dados possui
uma srie de comandos SQL do tipo DDL. Esses comandos podem ser
utilizados diretamente na linha de comando do ISQL. Um exemplo de
SQL-DDL o comando para criao de tabelas e colunas:
CREATE TABLE PEDIDO (PED_CD CODIGO NOT NULL,
PED_DT DATE NOT NULL,
PED_VALOR VALOR,
PED_TIPO TIPO NOT NULL,
PES_CD_CLI CODIGO NOT NULL,
PES_CD_VEN CODIGO NOT NULL,
TIMESTAMP DATE,
PRIMARY KEY (PED_CD));

Esse comando cria a tabela pedido e suas colunas j identificando


sua chave primria atravs da clusula primary key.
Utilizando o Windows ISQL para acessar o Banco de Dados
Para se conectar ao banco de dados, pode-se utilizar a opo
File/Connect to Database. Deve-se selecionar o banco de dados e
entrar com o usurio e senha de acesso.

Delphi Client/Server

28

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Fig. 3.18: Conexo com o Banco de Dados.

Para executar um comando SQL, basta escrev-lo na seo SQL


Statement. O comando seguinte insere um registro na tabela de
produto. Para executar o comando, pode-se utilizar o boto Run.
insert into PRODUTO(PRO_CD,PRO_NM,PRO_ESTOQUE,PRO_PRECO) values
(1,Impressora,5,200.00)

Para verificar a insero, pode-se selecionar o registro atravs do


comando seguinte e pressionar novamente o boto Run:
select * from PRODUTO

ou
select PRO_CD,PRO_NM,PRO_ESTOQUE,PRO_PRECO from PRODUTO

Para alterar os dados de um registro existente, pode-se utilizar o


comando Update:
update PRODUTO set PRO_PRECO = 250.00,PRO_ESTOQUE = 6 where PRO_CD= 1

Para verificar os dados novamente, pode-se pressionar o boto


Previous at encontr-lo e a seguir pressionar o boto Run.
Finalmente, para excluir um registro, pode-se utilizar o comando
Delete:
delete from PRODUTO where PRO_CD = 1

Delphi Client/Server

29

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Para concluir as operaes feitas e registr-las no banco, de modo


que outros usurios sejam capazes de v-las, utiliza-se o comando:
commit

Consistncia e Integridade dos Dados


A maioria dos bancos de dados relacionais possuem recursos para
consistir e tornar ntegro os dados armazenados em suas tabelas.
Desta forma, qualquer ferramenta que o usurio utilizar para acesslo obrigada a respeitar as regras, mantendo a integridade dos
dados. Os recursos de consistncia variam de banco para banco, mas
pode-se definir conceitualmente algumas categorias de integridade
como: integridade referencial, domnios e regras do negcio.
Integridade Referencial
Integridade referencial um conjunto de regras e de consistncias
entre os registros de duas tabelas que se relacionam. Como foi visto
no modelo relacional, quando duas tabelas se relacionam, a chave
primria de uma copiada para a outra e se esses dados forem
alterados ou excludos da tabela original necessrio verificar o que
ser feito com os dados e registros duplicados na outra tabela.
Quando se define uma integridade referencial, est se definindo o
procedimento que ser tomado quando esses processos ocorrerem.
Sejam duas tabelas A e B que se relacionam atravs de uma
coluna c que a chave primria de A e portanto foi repetida em
B para se fazer o relacionamento. Quando se define uma
integridade referencial para esse relacionamento, est se definindo
que a coluna c da tabela B s pode conter valores j cadastrados
na coluna c da tabela A.

Tabela B
*a
b
c

Tabela A
*c
d
e
.
.
Fig 3.19: Integridade Referencial.
Delphi Client/Server

30

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Existem trs formas de se manter essa regra quando registros da


tabela A so excludos:
Restrict: Define que se o valor da coluna c de A existir em
algum registro de B, o registro no poder ser excludo e uma
mensagem de erro retornar para a aplicao;
Cascade: Define que se o valor da coluna c de A existir em
algum registro de B, todos os registros que possurem esse valor
sero tambm excludos;
Set Null: Define que se o valor da coluna c de A existir em
algum registro de B, os valores de c em todos os registros
sero transformados para Null;
Em todos os casos, se o usurio tentar inserir um registro em B
com um valor de c que no exista na tabela A, um erro ser
reportado aplicao.
Alguns bancos, tambm permitem fazer o mesmo tratamento quando
a chave da tabela for alterada, ao invs de excluda. Entretanto, essa
alterao de chaves no deve ser um processo comum nas
aplicaes, sendo normalmente proibidas.
Os bancos de dados possuem formas diferentes de disponibilizar
esses servios. Alguns possuem at mais de uma forma de fazer o
mesmo processo. Mas de maneira geral existem duas possibilidades
de implementao:
Integridade Referencial Declarativa

Essa uma forma mais fcil, porm menos flexvel. Com apenas um
comando se define a integridade. Porm, alguns bancos no
suportam essa sintaxe ou a fornecem de maneira limitada somente
para regras do tipo Restrict. A maior vantagem dessa alternativa
sua simplicidade e por isso menos sujeita a erros de implementao.
Triggers

Os triggers so pedaos de cdigo escritos em uma extenso da


linguagem SQL fornecida por cada fornecedor, sendo portanto uma
linguagem proprietria. Essa extenso possui instrues para
implementar loops e verificar condies permitindo fazer pequenos
programas
estruturados.
Os
triggers
so
disparados
automaticamente pelo banco quando eventos de incluso, alterao
ou excluso ocorrem em uma determinada tabela. Portanto, atravs
dos triggers pode-se desenvolver a lgica necessria para se manter
a integridade referencial entre as tabelas.

Delphi Client/Server

31

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Domnio dos dados


Outro tipo de consistncia com relao ao domnio dos dados. Para
um determinado campo de dado pode existir um conjunto de valores
permitidos e o banco de dados deve reportar um erro aplicao se
um valor invlido for informado. Um tipo de consistncia de domnio
que normalmente fornecido pelos bancos a possibilidade do
campo ser nulo ou no. Alguns bancos possuem tambm comandos
para definir um conjunto de valores vlidos para um determinado
campo. Ex: Valores (J,F) para um campo que armazena se a pessoa
fsica ou jurdica. Outros possuem uma sintaxe para definir valores
mnimos e mximos. Pode-se, ainda, utilizar o recurso de triggers
para esse propsito.
Regras de Negcio
Pode-se definir como regras de negcio a integridade que deve existir
entre os valores de mais de um campo de uma ou mais tabelas. Podese considerar como regra do negcio as consistncias e atualizaes
que devem ser feitas em vrias tabelas, quando um registro
inserido, alterado ou excludo de uma tabela. Ou um processo de
clculo disparado sobre os dados do banco que tambm fazem
atualizaes em diferentes locais, mas de forma a manter os valores
dos dados sempre ntegros e consistentes. Um exemplo seria a
atualizao de estoque que normalmente envolve outras tarefas em
um sistema de controle de estoque.
Para implementar essas regras de negcio no banco de dados so
utilizados os recursos de triggers e stored procedures. Stored
procedures so blocos de cdigo assim como os triggers. A diferena
entre eles que os triggers so disparados automaticamente por
inseres, alteraes e excluses feitas nas tabelas, enquanto as
stored procedure so chamadas explicitamente pela aplicao.
Utilizando o Windows ISQL para definir integridades e
consistncias
O arquivo script utilizado na criao do banco de dados exemplo
tambm possui comandos que definem regras de integridade e
domnios que sero seguidas pela base de dados:
CREATE TABLE PEDIDO (PED_CD CODIGO NOT NULL,
PED_DT DATE NOT NULL,
PED_VALOR VALOR,
PED_TIPO TIPO NOT NULL,
PES_CD_CLI CODIGO NOT NULL,
PES_CD_VEN CODIGO NOT NULL,
TIMESTAMP DATE,
PRIMARY KEY (PED_CD));
Delphi Client/Server

32

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

Na prpria definio da tabela j atribuda a verificao de


nulidade para alguns campos atravs da sintaxe Not Null.
O prximo comando define um conjunto de valores possveis para o
campo PES_TP da tabela PESSOA.
ALTER TABLE PESSOA ADD CHECK( (PES_TP='J') OR (PES_TP='F'))

Para definir a integridade referencial entre duas tabelas foi usada a


forma declarativa atravs do comando:
ALTER TABLE ITEM ADD FOREIGN KEY (PED_CD) REFERENCES PEDIDO(PED_CD);

Esse comando define que um item s pode ser includo se o valor da


coluna PED_CD da tabela de ITEM j existir na tabela PEDIDO. Alm
disso, s possvel excluir um PEDIDO que no possua ITENS
associados. A sintaxe do Interbase para definio de integridade
referencial declarativa somente permite a forma restrict. As
demais formas teriam que ser definidas atravs de triggers.
Utilizando o Windows ISQL para testar as consistncias.
O modelo exemplo de pedido possui definies para fazer a
integridade referencial entre suas tabelas. Uma delas na relao
que existe entre as tabelas UF e CIDADE como mostrada na figura
3.13. Esta integridade no permite ento inserir um registro na
tabela CIDADE se o valor atribudo a UF_SG no existir na tabela UF.
Alm disso, se tentarmos excluir um registro da tabela UF que esteja
sendo referenciado pela tabela CIDADE ir retornar uma mensagem
de erro, j que o tipo da integridade restrict.
UF
*UF_SG
UF_NM

Restrict
CIDADE
*CID_CD
*UF_SG
CID_NM
CID_NUMHABITANTES
Fig 3.20: Integridade Referencial

Executando o seguinte comando no Windows ISQL...


insert into CIDADE(CID_CD,UF_SG,CID_NM,CID_NUMHABITANTES) values
(1,MG,Belo Horizonte, 500000)
Delphi Client/Server

33

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

uma mensagem de erro ir retornar j que no existe um registro


com UF_SG = MG na tabela UF, portanto deve-se primeiro inserir o
registro:
insert into UF(UF_SG,UF_NM) values (MG, Minas Gerais)

A seguir pode-se inserir novamente o registro na tabela CIDADE


insert into CIDADE(CID_CD,UF_SG,CID_NM,CID_NUMHABITANTES) values
(1,MG,Belo Horizonte, 500000)

Assim, os dois registros foram inseridos com sucesso, e deve-se


portanto efetivar as inseres utilizando o comando commit. Pode-se
tentar excluir o registro da tabela UF atravs do comando:
delete from UF where UF_SG = MG

Pelo mesmo motivo visto anteriormente, o registro tambm no pode


ser excludo. Deve-se ento excluir primeiro o registro de CIDADE
para depois poder excluir o de UF.
O modelo exemplo tambm possui algumas consistncias de domnio,
principalmente em relao a nulidade. Pode-se tentar inserir na
tabela UF utilizando o seguinte comando:
insert into UF(UF_SG,UF_NM) values ( Null, Null)

Esse comando deve retornar erro informando que UF_SG no pode


ser nulo. Se o comando for alterado para:
insert into UF(UF_SG,UF_NM) values ( SP, Null)

ir continuar apresentando erro, agora na coluna UF_NM que


tambm no pode conter nula.

Distribuio da Consistncia e Integridade dos Dados


Como foi visto, possvel implementar vrias regras de consistncia
dos dados no prprio banco de dados. Entretanto, possvel
implement-las tambm na aplicao e portanto fica uma dvida de
qual seria o melhor lugar para implement-las. Ambas as alternativas
possuem vantagens e desvantagens que devem ser observadas no
incio do processo de desenvolvimento.
A principal vantagem em se colocar todas as regras no banco de
dados que esse mantm a consistncia e integridade dos dados
independente da ferramenta de acesso utilizada pelo usurio. Alm
Delphi Client/Server

34

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

disso, as manutenes dessas regras ficam localizadas em um nico


local, ao invs de ficarem espalhadas por toda a aplicao ou por
diversas aplicaes. Entretanto, o desenvolvimento utilizando essa
linha de trabalho muito mais rduo e demorado. As ferramentas
disponveis nesse ambiente no apresentam ainda muitos recursos
que dem produtividade na construo das regras. As linguagens
utilizadas para escrever as stored procedures e triggers so
proprietrias dos fornecedores, tornando a empresa dependente de
nico fornecedor, dificultando a distribuio dos dados em bancos de
fornecedores diferentes. Alm disso necessrio dividir o
desenvolvimento em duas linguagens. A linguagem da ferramenta
para se montar a interface e a chamada dos processos e a linguagem
para construo das stored procedures.
Atualmente, o mercado se encontra dividido com cada empresa
tomando um caminho diferente para distribuir essas regras entre o
cliente e o servidor. Mas no pode-se dizer que um caminho j
seguido por uma empresa que obteve sucesso o mais indicado para
uma outra empresa. Muitas outras variveis precisam ser
consideradas e dificilmente sero iguais entre duas empresas. Alm
disso, as ferramentas e a tecnologia evoluem fazendo como que um
mesmo caminho possa obter resultados diferentes, considerando-se a
tecnologia atualmente disponvel.
De uma maneira geral, as duas primeiras categorias de consistncia
(integridade referencial e domnios) so normalmente implementadas
no banco de dados, sendo que os domnios so implementados
repetidamente na aplicao para evitar que os comandos sejam
enviados ao banco. A grande dvida mesmo, com relao s regras
de negcio que algumas empresas implementam totalmente no banco
de dados, outras totalmente na aplicao e algumas distribuem entre
esses dois componentes. Atualmente, existe mais um alternativa na
nova gerao da arquitetura Cliente/Servidor: a implementao das
regras de negcio em um novo componente chamado Servidor de
Aplicaes.

Delphi Client/Server

35

S G D B

S I S T E M A S

G E R E N C I A D O R E S

D E

B A N C O

D E

DAD O S

SQL Explorer
Esse captulo apresenta a ferramenta de
definio e manipulao de dados SQL
Explorer da Borland.

Captulo

ode-se utilizar a ferramenta SQL Explorer para criar a


estrutura do banco de dados de maneira grfica e interativa.
Alm disso, essa ferramenta permite definir novos aliases para
os bases de dados, manipular os dados (inserir, alterar, excluir), e
construir um dicionrio de dados com definies que podem ser
utilizadas para facilitar a construo de aplicaes em Delphi.

Criao de Alias
Uma das possibilidades do SQL Explorer a definio de aliases.
Ao abrir o SQL Explorer exibido na lista Databases todos os
aliases definidos no BDE.

Fig 4.1: Lista de aliases no SQL Explorer

Delphi Client/Server

36

Para criar um novo alias, deve-se pressionar o boto direito do


mouse em qualquer regio do SQL Explorer para exibir um popup
menu e selecionar a opo New.... Uma tela ser exibida para que
seja informado o tipo de driver do novo alias.

Fig. 4.2: Definio de um novo alias.

Visualizao e Edio de Dados


Para visualizar e editar os dados de um banco de dados, deve-se
expandir sua lista de opes exibindo seus componentes. Cada tipo
de driver de banco de dados pode possuir opes diferentes na
lista, entretanto uma opo comum a todos : Tables. Ao expandir
tambm essa opo, ser listada todas as tabelas contidas nesse
banco de dados. Pode-se, ento, selecionar uma das tabelas e exibir
seus dados atravs da ficha Data do lado direito da tela. Na barra de
ferramentas existem botes que permitem a navegao entre os
registros, incluso, edio e excluso dos dados.

Delphi Client/Server

37

Fig 4.3: Visualizao e edio dos dados.

Pode-se tambm utilizar comandos SQL par visualizar e editar os


dados atravs da ficha Enter SQL.

Delphi Client/Server

38

Fig 4.4: Visualizao de dados atravs de SQL

Pode-se tambm manipular os dados atravs de comandos SQLs.

Fig. 4.5 Edio de dados atravs de SQl.

Definio de novos elementos


Atravs do SQL Explorer pode-se tambm definir novos elementos na
estrutura do banco de dados. Para criar uma nova tabela no banco de
Delphi Client/Server

39

dados, deve-se pressionar o boto direito sobre o item Tables e


selecionar a opo New.... no popup menu. Uma nova tabela
criada com um nome default. Pode-se ento alterar seu nome e
definir os demais elementos que a compem.

Fig 4.6: Definio de uma nova tabela.

Pode-se definir as colunas, chave primria, integridade referencial,


triggers, ndices e outros elementos para as tabelas atravs do SQL
Explorer para os vrios tipos de banco de dados.

Definio de Dicionrios de dados


Atravs do SQL Explorer pode-se definir um dicionrio de dados.
Esse dicionrio um banco de dados especial usado para armazenar
um conjunto de definies dos itens de dado existente nas tabelas do
banco de dados da aplicao. Esse conjunto de definies descreve
as propriedades do componente Field de um DataSet no Delphi, o
tipo do campo e o tipo de controle visual a ser criado quando o
componente Field arrastado para a tela. Desta forma, consegue-se
definir uma nica vez as propriedades de um domnio de dado que
pode estar sendo utilizado por diversos itens em vrios locais da
aplicao.

Delphi Client/Server

40

Criao de um novo Dicionrio


Para criar um novo dicionrio de dados, deve-se selecionar a ficha
Dictionary do lado esquerdo da tela, pressionar o boto direito e
escolher a opo New do Menu. A seguir ser exibida uma tela para
se definir o nome do dicionrio, o alias do banco aonde a tabela do
dicionrio ser criada e o nome da tabela.

Fig 4.7: Criao do Dicionrio de Dados

Importao das definies do Banco de Dados


Para importar os atributos e definies do banco de dados da
aplicao deve-se selecionar a opo Dictionary/Import From
Database.

Fig 4.8: Importao das definies do Banco de Dados.

Definio das propriedades dos Attribute Sets


A seguir so exibidas as definies importadas e os Attribute Sets
encontrados pelos constraints definidos no banco de dados para
cada elemento.

Delphi Client/Server

41

Fig 4.8: Definio de propriedades dos Attribute Sets.

Utilizao do Dicionrio no Delphi


Para utilizar as definies estabelecidas no dicionrio em uma
aplicao Delphi, pode-se arrastar os campos desejveis do SQL
Explorer para a tela da aplicao. Automaticamente, sero criados
um DataSet e um Datasource para cada tabela do banco de dados.

Delphi Client/Server

42

Fig 4.9: Criao da tela atravs do Dicionrio do SQL Explorer.

Alm disso, pode-se tambm associar um componente Field atravs


da opo Associate attributes... do popup menu.

Fig 4.10: Associao de Fields com o dicionrio.

Delphi Client/Server

43

Trabalhando com Bancos de


Dados Relacionais
Esse captulo apresenta a forma de trabalho
dos bancos de dados relacionais.

Captulo

s bancos de dados relacionais fornecem um conjunto de


servios para que as aplicaes possam acess-los e
manipul-los. Portanto, importante entender bem sua
funcionalidade para melhor aproveitar seus recursos nas aplicaes.
Existem algumas diferenas na forma de trabalhar entre os
fornecedores, mas grande parte dos conceitos podem ser aplicados a
todos os bancos de dados relacionais.

Componentes da Arquitetura Cliente/Servidor-dlsf


Como foi visto, a arquitetura Cliente/Servidor composta de dois
componentes fsicos que se comunicam atravs da rede: a estao de
trabalho do usurio e o servidor de banco de dados.
Para se estabelecer a comunicao entre esses dois componentes so
utilizadas vrias camadas de software que so instaladas em cada
componente fsico. A estao de trabalho cliente deve ter, alm da
aplicao final, vrios outros elementos para acessar a base de dados
em um SGBD atravs de rede.
Database Engine: biblioteca fornecida pelo fornecedor da
ferramenta de desenvolvimento com o objetivo de fornecer uma
forma nica e transparente da aplicao acessar diferentes bases
de dados. Ex: BDE (Borland Database Engine);
SQL Links: driver
fornecido tambm pelo fornecedor da
ferramenta de desenvolvimento responsvel pela comunicao do
Database Enginer com uma base de dados especfica. Sua
principal caracterstica traduzir os comandos utilizados pelo
Database Enginer para comandos conhecidos pela base de dados
utilizada. Ex: SQL links para acessar Oracle, Sybase, Informix, MS
SQL Server, Interbase, etc. A aplicao pode optar por utilizar o
padro ODBC para acessar a base de dados, ao invs de utilizar
SQL Links (acesso nativo). Entretanto, o acesso feito pelos SQL
Links ainda possui um desempenho superior em relao ao acesso
feito via driver ODBC.

Delphi Client/Server

44

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Client do Banco de Dados: API fornecida pelo fornecedor do


SGDB e instalada na estao cliente para estabelecer a
comunicao com o banco de dados. Nessa API se encontram as
funes de acesso a base de dados. tambm, responsvel por
utilizar um determinado protocolo de rede para encontrar o
servidor de banco para que a aplicao possa acess-lo enviando
comandos e buscando os dados.
Protocolo de Rede: softwares responsveis pela transmisso dos
dados pela rede entre a mquina cliente e o servidor.

CLIENTE
CLIENTE

MIDDLEWARE

Aplicaes
Ferramentas
Desenvolvimento

Servios Especficos

Database
DatabaseEnginer
Enginer

SERVIDOR
SERVIDOR

SQL/API
SQL/API

Banco
Bancode
deDados
Dados

Transporte
NetBios
NetBios

TCP/IP
TCP/IP IPX/SPX
IPX/SPX

SNA
SNA

SOR
Serv.
Serv.Diretrio
Diretrio
Sistema Operacional

RPC
RPC

Serv.
Serv.Diretrio
Diretrio

Mensagens
Mensagens

Segurana
Segurana

Sistema Operacional

Fig 5.1: Componentes da comunicao entre o Cliente e o Servidor.

Conexes e Contextos
Pode-se definir como conexo um caminho estabelecido entre a
aplicao e o banco de dados atravs de um usurio e sua senha.
Para cada conexo criada uma estrutura na memria do servidor de
banco preparada para receber os comandos SQL enviados pela
aplicao.
Cada conexo pode ter um ou mais contextos. Para executar um
comando SQL na aplicao necessria a criao de pelo menos um
contexto dentro da conexo.
Os comandos SQLs enviados por um contexto passam por pelo menos
duas fases: preparao e execuo. Na primeira fase verificada a
sintaxe do comando e definido o caminho de acesso que ser
utilizado pela execuo do comando (otimizao). Na segunda fase o
comando finalmente executado. Depois que a primeira fase for
completada, pode-se repetir vrias vezes a execuo da segunda
fase, otimizando o processo, j que o tempo gasto para compilar e
otimizar o comando no ser gasto novamente. Entretanto, um
mesmo contexto pode ser usado para a execuo de mais de um
Delphi Client/Server

45

B A N C O S

D E

DA D O S

R E L A CI O N A I S

comando SQL. Quando um outro comando SQL enviado pelo


mesmo contexto, o comando anterior deixa de existir e uma nova
necessidade de execuo desse comando ter que passar novamente
pelas duas fases. Portanto um contexto s permite controlar e manter
preparado um nico comando SQL. Comandos anteriores no so
mais vistos pelo contexto.

Contextos
Aplicao

Conexo
Banco

Fig 5.2: Conexes e Contextos

Para resolver esse problema, alguns bancos permitem a utilizao de


mltiplos contextos dentro de uma mesma conexo, possibilitando o
controle de mais de um comando SQL ao mesmo tempo. Para os
bancos que no possuem mltiplos contextos, necessrio criar
vrias conexes para se ter o mesmo recurso. Porm existem
algumas diferenas entre essas duas formas de utilizao. Uma
conexo totalmente independente de uma outra conexo, como se
fossem dois usurios diferentes acessando o banco de dados. J
vrios contextos dentro de uma conexo so interligados e cooperam
para a execuo de um mesmo processo.
Conexes e Contextos no Delphi
Para acessar a base de dados, deve-se primeiramente criar um
alias no BDE para o banco de dados exemplo no interbase. Pode-se
atribuir o nome vendas para o alias, definir o parmetro server
name para o nome do banco de dados e seu diretrio, user name
para SYSDBA.

Delphi Client/Server

46

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.3: Configurao do alias no BDE.

TDatabase e TDataSet

O componente do Delphi que representa uma conexo com o banco


de dados o TDatabase. Deve-se utilizar um componente TDatabase
para cada conexo que se deseje fazer com o banco. Alguns bancos
controlam o nmero de licenas de usurios atravs do nmero de
conexes. Portanto, o nmero de conexes deve ser bem reduzido.

Fig 5.4: Componente TDatabase do Delphi.

Os componentes TQuery e TTable so utilizados para fornecer


contextos dentro de uma conexo do banco de dados. Atravs da
propriedade DatabaseName possvel lig-los a um componente
TDatabase.
Delphi Client/Server

47

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.5: Os componentes TQuery e TTable do Delphi.

Cursores e Result Sets


Quando o SQL executado um comando select e retorna mais de um
registro, necessrio que a aplicao abra um cursor para poder
percorrer e trazer todos os registros do banco de dados para a
aplicao. Desta forma criado um result set, que uma estrutura
temporria com a indicao dos registros que foram selecionados. O
cursor inicialmente posicionado na primeira linha e a aplicao
pode requisitar que o cursor avance linha a linha no result set at
que seu final seja alcanado. Os cursores e result sets podem ser
criados no banco de dados se estes suportarem ou na prpria estao
de trabalho. A principal utilidade dos cursores e result sets
permitir percorrer os dados em qualquer sentido e poder fazer
atualizaes de registro atravs do posicionamento do cursor.
Se nenhum cursor for declarado explicitamente no banco de dados, o
banco de dados cria um cursor internamente apenas para enviar
seqencialmente os dados para a aplicao quando estes forem
requisitados. Assim, no necessrio que o servidor envie todos os
dados selecionados de uma s vez para aplicao. Cabe a aplicao
dizer quando cada registro de dados deve ser trazidos para a
mquina cliente.
Normalmente, utilizado buffers que pode conter mais de um
registro para enviar os dados para aplicao. O tamanho desses
buffers pode ser redefinido para otimizar os pacotes de dados
enviados pela rede.

Delphi Client/Server

48

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Contextos
Aplicao

Conexo
Comando
Insert

Banco

Comando
Update

Comando
Select

Cursor
Result
Set
.
.
.
.

Fig 5.6: Cursores e Result Sets

O comando select, como qualquer outro comando executado em


um determinado contexto de uma conexo. Ento, se outro comando
SQL utilizar o mesmo contexto antes que todas as linhas do comando
select anterior tenham sido trazidas para aplicao o cursor e seu
result set sero destrudos e o restante das linhas perdido.
Portanto, para se manter o result set de um comando select at
que todas as linhas sejam trazidas, preciso abrir um contexto de
maneira exclusiva para esse comando.
A medida que as linhas so trazidas para aplicao, normalmente so
criados caches na memria ou em disco para armazenar os dados
permitindo a navegao entre registros (inclusive registros
anteriores). Esse recurso normalmente conhecido como Front End
Result Set por estar localizado na mquina cliente.
Cursores e Result Sets no Delphi
Os componentes TTable e TQuery do Delphi utilizam esses recursos
para acessar o banco de dados. Quando se abre uma Query ou uma
Table no Delphi, um cursor apontando para um result set criado
e as linhas so trazidas e armazenadas na aplicao a medida que o
usurio percorre os registros at o final da consulta. Portanto, se o
usurio selecionar um nmero grande de registros (1.000.000), ele
ir esperar apenas o tempo necessrio para compilar e executar a
consulta e trazer alguns poucos registros suficientes para popular a
parte visvel dos dados na tela. Os demais registros sero trazidos a
medida que o usurio navegue em direo ao final da consulta. Se o
usurio executar um comando para ir para o final da consulta, poder
ter que esperar o tempo necessrio para se trazer todos os 1.000.000
de registros para a estao cliente.

Delphi Client/Server

49

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.7: Cursores e Result Sets no Delphi.

Transaes
Um dos recursos mais importantes fornecidos pelos bancos de dados
o controle de transao. O controle de transao permite as
aplicaes manterem a integridade lgica dos dados em um
determinado processo. Uma transao um conjunto de comandos
executados no banco de dados que devem ser aplicados
integralmente. Se um dos comandos falhar, todos os outros tm que
ser desfeitos para manter a integridade e a unicidade do processo em
execuo. Desta forma pode-se garantir a confiabilidade dos dados
armazenados no banco. Considere o seguinte exemplo: um processo
de transferncia entre contas correntes que deve debitar um valor
em uma das contas e creditar exatamente o mesmo valor em uma
outra conta. Se o processo falhar no meio e esse recurso de controle
de transao no estiver sendo utilizado, o dinheiro pode ser retirado
da primeira conta, mas no ser creditado na segunda, assim o valor
que deveria ser transferido, simplesmente desaparece da base de
dados.
Para fazer o controle de transao, so fornecidos normalmente pelos
bancos de dados trs comandos: um para iniciar a transao (begin
transaction), um para finalizar e aplicar as modificaes executadas
(commit) e outro para finalizar cancelando e desfazendo toda a
operao (rollback). Portanto, quando uma transao iniciada,
todos os comandos seguintes fazem parte de uma nica transao at
ser encontrado um comando que a finalize (commit ou rollback). Se
todos os comandos foram executados com xito, pode-se ento
disparar o comando commit para que estes sejam aplicados e se
tornem visveis para os demais usurios. Caso contrrio, se um erro
foi encontrado em algum dos comandos, pode-se disparar um
Delphi Client/Server

50

B A N C O S

D E

DA D O S

R E L A CI O N A I S

rollback descartando todas as alteraes desde o comeo da


transao.
Cada conexo, normalmente suporta apenas um controle de
transao e todos os seus contextos fazem parte dessa mesma
transao. Quando uma transao iniciada, qualquer comando
disparado em qualquer um dos contextos faz parte da transao. E
quando a transao fechada os comandos executados por todos os
contextos so aplicados e os contextos so destrudos. Sendo assim,
comandos que eram mantidos preparados em algum contexto
necessitam ser novamente compilados e contextos que possuam
cursores e result sets que no haviam sido trazidos totalmente para
a aplicao so fechados e portanto seus dados perdidos. Algumas
ferramentas, como o Delphi, ao verificarem que os contextos sero
destrudos buscam todos os registros de result sets pendentes para
a aplicao antes que estes sejam destrudos.
Alguns bancos de dados permitem que sejam mantidos os contextos
quando a transao finalizada. Entretanto, necessrio que a
ferramenta de desenvolvimento tambm faa uso desse recurso.
Pode-se tambm criar mais de uma conexo, para conseguir manter
abertos os contextos, deixando-os em uma conexo separada da
conexo que a transao est sendo executada, j que o controle de
transao s vlido para uma nica conexo. Porm, deve-se tomar
bastante cuidado com essa ltima alternativa. Como ser visto
adiante, o controle de travamento de registros tambm feito para
cada transao e portanto uma conexo pode travar comandos da
outra conexo deixando a aplicao em DeadLock, se os cuidados
necessrios no forem tomados.
Alguns servidores de banco de dados tambm suportam o controle de
transao entre dois bancos diferentes atravs do recurso chamado
two-phase commit. Esse recurso permite que a finalizao da
transao seja feita em dois passos, possibilitando que um banco de
dados notifique ao outro o sucesso da transao para que ela possa
ser finalizada como um todo.
Transaes no Delphi
Para se fazer esse controle de transao no Delphi deve-se utilizar
um conjunto de mtodos existentes no componente TDatabase.
StartTransaction

- Inicia a transao.

Commit
transao

- Efetiva as alteraes feitas desde o incio da

Rollback
transao

- Cancela as alteraes feitas desde o incio da

Delphi Client/Server

51

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Projeto Exemplo

O projeto a seguir mostra como as transaes so tratadas nas


operaes feitas pelo prprio Delphi e como se comportam os
cursores e contextos j abertos. Quando no se usa explicitamente os
comandos vistos acima, o Delphi abre implicitamente uma transao
a cada comando de atualizao disparado no banco e logo a seguir
fecha a transao com um comando commit. O objetivo do projeto
observar o que ocorre com o Result Set aberto, quando um
comando que finaliza a transao executado. Para construir a
aplicao deve-se desenhar o form como mostrado na figura e
definir as propriedades listadas na tabela.

Fig 5.8: Form do projeto de transaes.

Componente
Form1
Table1
DataSource1
Grid1
Query1

DataSource2
Grid2

Propriedade
Name
DatabaseName
TableName
Active
DataSet
DataSource
DatabaseName
SQL
Request Live
DataSet
DataSource

Valor
frmTransacao
Vendas
Produto
True
Table1
DataSource1
Vendas
Select * from Produto
True
Query1
DataSource2

Tabela de Propriedades: Projeto de transaes.

Aps ter construdo o form, deve-se abrir o SQLMonitor atravs da


opo do Menu Database/SQL Monitor para que se possa observar
os comandos enviados pelo Delphi ao servidor de banco de dados.
Delphi Client/Server

52

B A N C O S

D E

DA D O S

R E L A CI O N A I S

SQL Monitor - Ferramenta capaz de monitorar as chamadas feitas


pelas aplicaes Delphi aos servidores de banco de dados. Permite
ver as instrues enviadas para o banco como os comandos SQLs
(select, update, insert, delete). Alm disso permite ver os valores
de parmetros enviados para os comandos e os dados que so
retornados pelo servidor. Concluindo, essa uma importante
ferramenta que auxilia o desenvolvedor a descobrir o comportamento
da aplicao em relao ao banco de dados, tornando possvel o
aperfeioamento da aplicao para se obter uma melhor
performance no ambiente cliente/servidor.

Fig 5.9: SQL Monitor.

Pode-se escolher o que ser monitorado pelo SQL Monitor atravs da


opo Trace Option.

Delphi Client/Server

53

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.10: Trace Options do SQLMonitor

Aps abrir o SQL Monitor deve-se executar a aplicao e observar


que somente so trazidos para a aplicao uma quantidade de dados
suficiente para preencher a parte visvel do Grid. Quando o usurio
navega pelo Grid, novas linhas so trazidas, at que o usurio
atinja o fim do result set.
Alm disso, deve-se observar que se for feita uma atualizao em
algum registro, ser disparado um comando close fechando o result
set e quando o usurio navega para uma linha que ainda no foi
trazida para a aplicao, um novo comando select ser feito
buscando as linhas que ainda no foram trazidas atravs de um filtro
colocado na clusula where do comando.
SELECT PRO_CD ,PRO_NM ,PRO_PRECO ,PRO_ESTOQUE ,TIMESTAMP
WHERE (PRO_CD> ?) ORDER BY PRO_CD ASC

FROM PRODUTO

Atravs desse exemplo, pode-se observar que quando uma transao


finalizada todos os result sets abertos so fechados e no caso do
componente TTable um novo select feito quando o usurio navega
por registros ainda no enviados a estao.
Existe, no entanto, uma diferena quando se utiliza o componente
TQuery. Nesse componente, quando a transao finalizada, o
Delphi l todas as linhas pendentes at o final do result set antes
de executar o comando commit. Para verificar esse comportamento,
deve-se alterar a propriedade Active do Table1 para False e a
Delphi Client/Server

54

B A N C O S

D E

DA D O S

R E L A CI O N A I S

propriedade Active do Query1 para True e fazer os mesmos


procedimentos do exemplo anterior.
Porm alguns bancos suportam que os cursores permaneam abertos
quando a transao finalizada. O Interbase um dos bancos que
possuem essa caracterstica e para utiliz-la necessrio alterar a
propriedade DRIVER FLAGS no BDE. Entretanto essa alternativa s
vlida para transaes controladas implicitamente pelo Delphi.
DRIVER FLAGS
0
512
4096
4068

Isolation level and


commit type
Read committed, hard
commit
Repeatable read, hard
commit
Read committed, soft
commit
Repeatable read, soft
commit

Para cada servidor de banco de dados existe uma configurao


diferente. Para o Oracle, por exemplo, nada precisa ser feito porque
este o comportamento padro.

Concorrncia - DLSF
Pode-se definir como concorrncia o processo de disponibilizar a
mesma base de dados vrios usurios conectados simultaneamente.
Para manter a unicidade de uma transao e a confiabilidade no valor
do dado apresentado ao usurio, necessrio a utilizao de alguns
controles sobre os registros do banco de dados.
Tipos de travamentos (locks)
Para disponibilizar tal funcionalidade, o banco de dados prov
servios para bloquear um registro, de forma que outros usurios
no possam acess-lo. Alguns bancos permitem o travamento de um
nico registro, enquanto outros trabalham com unidades compatveis
com o dispositivo fsico, permitindo o travamento atravs de pginas.
Dependendo do banco, o tamanho dessas pginas pode variar (2K,
1K), podendo essas serem formadas por mais de um registro.
Portanto, quando uma pgina travada, todos os registros que ela
possui sero travados. Esse controle de atribuir e remover as travas
realizado para cada transao em cada conexo. Mesmo se duas
conexes so feitas pelo mesmo usurio, o controle de travamento
distinto, podendo assim uma das conexes bloquear registros para a
outra conexo. Deve-se ter cuidado ao se trabalhar em mais de uma
conexo para no acontecer os chamados Deadlocks, que so
travamentos causados por mais de uma conexo e que cada uma
Delphi Client/Server

55

B A N C O S

D E

DA D O S

R E L A CI O N A I S

delas est a espera da liberao de um registro da outra para


continuar sua execuo.
Pode-se dizer que os bancos de dados trabalham com dois tipos de
travas: exclusive lock e shared lock.
Exclusive Lock

So travas atribudas aos registros ou pginas quando o usurio


executa comandos de atualizao sobre elas (update, insert,
delete). Como seu nome indica, nenhuma outra trava pode ser
colocada na mesma pgina ou registro, proibindo qualquer tipo de
acesso ao registro ou pgina por outra conexo (transao). Todas as
travas exclusive locks so retiradas automaticamente quando a
transao for finalizada por um commit ou rollback.
Shared Lock

So travas que podem ser colocadas nos registros ou pginas quando


o usurio seleciona o registro. Uma trava shared lock permite que
outras travas do mesmo tipo sejam atribudas ao mesmo registro ou
pgina, entretanto probe a atribuio de uma trava exclusive lock.
Portanto, quando se atribui a um registro ou pgina uma trava
shared lock, bloqueia-se qualquer atualizao por outra transao
(conexo), permitindo assim a aplicao ter acesso ao dado com a
certeza que este no ser alterado por mais ningum. Entretanto,
possvel que o mesmo usurio na mesma conexo possa alterar o
registro ou pgina marcado por ele como shared lock
transformando-o para exclusive lock, desde que nenhuma outra
conexo possua tambm uma trava shared lock para o mesmo
registro ou pgina. Todas as travas shared lock tambm so
retiradas quando se finaliza a transao.
Pg. ou
Regist.\Transaes
1
2
3
4

1
Exclusiv
e
Shared

Shared
Shared

Shared

Shared
Exclusiv
e

5
Tabela de Transaes: Um exclusive lock em uma linha, exclui a possibilidade de existir outro exclusive
lock ou shared lock em uma outra coluna na mesma linha.

Nveis de isolamento
Podemos definir alguns nveis de isolamento, de acordo com a forma
que os shared locks so atribudos aos registros ou pginas. A
seguir sero mostradas as vrias formas de se trabalhar com locks
no banco de dados, independente dos nomes utilizados pelos vrios
fornecedores. Sendo possvel que alguns bancos de dados no
apresentem todas as possibilidades descritas abaixo.
Delphi Client/Server

56

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Travamento de todos os registros

Nessa modalidade atribuda uma trava shared lock para todos os


registros da seleo feita atravs de um comando select. Este o
nvel de isolamento que mais trava os registros e deve ser utilizado
com cautela para no inviabilizar a concorrncia dos dados e o
funcionamento da aplicao.
Travamento de um registro ou pgina

Nesse tipo de travamento, so colocadas travas shared locks


somente para o registro ou pgina onde o cursor se encontra
posicionado. Quando a aplicao movimenta o cursor para um
prximo registro, retirada a trava do registro anterior e colocada no
registro atual . Portanto, somente um registro ou pgina fica travado
em um determinado momento.
Sem travamento

Nessa modalidade atribuda uma trava shared lock no momento


de acesso a cada registro. Entretanto, essa trava retirada logo a
seguir e o registro ou pgina no permanece travado. Essa pequena
atribuio da trava simplesmente para verificar se no existe
nenhuma outra trava do tipo exclusive lock sobre o registro, que
no permitiria a sua leitura. Portanto, mesmo nessa categoria no
possvel acessar registros alterados no meio de uma transao que
esto marcados como exclusive lock.
Seleo de registros travados com um exclusive lock.

Alguns bancos permitem que uma conexo acesse os dados de um


registro ou pgina mesmo se estes estejam
marcados como
exclusive lock. Para tal, o registro acessado sem a atribuio de
nenhuma trava, j que uma trava SL no pode coexistir com uma
trava EL em um mesmo registro ou pgina. Como os registros
acessados possuem dados que no foram efetivados atravs de um
commit, so normalmente disponibilizadas cpias antigas do mesmo
registro armazenadas pelo banco de dados. Apesar de permitir esse
tipo de acesso, muitos bancos de dados probem a alterao dos
dados quando acessados desta forma.
Optimistic Lock
Para se maximizar a concorrncia entre os dados nas aplicaes,
podem ser utilizadas outras formas de controle que diminuem os
travamentos causados na base de dados. Para isso pode-se utilizar o
nvel de isolamento Sem travamentos e fazer um controle adicional
para ter certeza que os dados mostrados para o usurio no foram
alterados quando esse inicia o processo de alterao do registro.
Existem algumas alternativas que podem ser seguidas para se
implementar esse processo. O processo normal de um comando de
update ou delete conter na clusula where a verificao da chave
primria para possibilitar a identificao do registro na tabela.
Delphi Client/Server

57

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Entretanto, explorando um pouco mais esse recurso de identificao


do registro, pode-se implementar um bom controle de concorrncia.
Where All

Consiste em verificar na clusula where no s a chave primria,


mas todos os registros. Assim, se qualquer um dos registros for
alterado ou excludo por outro usurio, esse no ser encontrado
quando o comando update ou delete for disparado, sendo possvel a
aplicao notificar o usurio tal acontecimento. A aplicao pode
ento permitir o usurio buscar novamente os dados para verificar as
alteraes feitas e reiniciar o processo de alterao.
Entretanto, campos de difcil comparao, como campos contendo
imagens e textos grandes, normalmente no podem ou no devem
ser utilizados desta forma.
Where Changed/Key

Consiste em verificar na clusula where a chave primria mais os


campos que sofreram alterao pelo usurio. Desta forma garante-se
que os mesmos campos no foram alterados por outro usurio. Mas,
permite atualizar um registro alterado por outro usurio desde que
no sejam os mesmos campos.
Where Key

Nesse mtodo a concorrncia livre, pois permite que o registro seja


alterado sem verificar se este foi alterado por outro usurio. Na
clusula where de alterao, somente utilizada a chave primria da
tabela para localizar o registro.
Where Timestamp/Rowid

Consiste em verificar na Clusula where a chave primria mais uma


coluna definida exclusivamente para fazer o controle de
concorrncia. Essa coluna atualizada para um novo valor toda vez
que o registro for inserido ou alterado. Portanto, se o registro for
alterado por um outro usurio, o registro no ser encontrado para
atualizao.
Alguns tipos de bancos de dados j possuem algum suporte para esse
tipo de controle. O SQLServer e o Sybase possuem um tipo de dado
chamado timestamp, para o qual pode se criar uma coluna que ser
atualizada automaticamente com a hora, minuto, segundo e dcimos
de segundo do momento em que ocorrer uma operao de insero
ou alterao. O SQLBase um outro banco que permite esse controle
atravs de uma coluna chamada rowid que j definida
automaticamente para todas as tabelas e atualizada para um valor
nico quando ocorre as operaes insert e update.
Para os bancos que no possuem automaticamente esses recursos,
deve-se criar uma nova coluna que pode armazenar a Data/hora
como no caso do timestamp, desde que o tipo permita a atribuio
Delphi Client/Server

58

B A N C O S

D E

DA D O S

R E L A CI O N A I S

de dcimos de segundo, porque a unidade segundo no possui


preciso suficiente para essa operao. Ou pode-se criar uma coluna
numrica, cujo o valor ser incrementado atravs de um contador
nico a cada atualizao realizada sobre o registro.
Alm de criar a coluna, preciso fazer tambm o processo de
atualizao dos dados quando uma operao de insert ou update
ocorrer. Pode-se fazer isso atravs da aplicao ou atravs do recurso
de triggers dos bancos de dados. A utilizao de triggers
possibilita que a atualizao possa ser feita atravs de outras
ferramentas que no sejam a prpria aplicao e portanto mais
recomendado.
Concorrncia no Delphi
Para se configurar os nveis de isolamento utilizados pelo Delphi
deve-se utilizar a propriedade TransIsolation do componente
TDatabase.
tiDirtyRead - Permite ler alteraes feitas por outro usurio (ou
transao) ainda no comitadas (efetivadas), ou seja no
respeitando os exclusives locks. Essa opo normalmente no
est disponvel nos banco de dados.
tiReadCommitted - Esse o tipo mais normal de se trabalhar.
Somente as alteraes feitas por outro usurio (ou transao) j
comitadas so vistas pelo usurio. Se a alterao ainda no foi
efetivada o registro fica travado e a leitura fica esperando o fim da
transao.
tiRepeatableRead - Esse mtodo utilizado para que o dado visto
pelo usurio seja sempre o mesmo durante a transao se este for
lido novamente. Alguns bancos implementam este recurso
permitindo ver uma cpia mais antiga de um registro alterado por
outra transao e ainda no comitado. Desta forma a aplicao
no fica travada esperando a liberao do registro pela transao.
Esta alternativa diminui a possibilidade de travamentos entre as
transaes, porm consome um nmero maior de recursos do
banco para fazer as cpias (verses) dos registros. Outros bancos,
implementam este recurso travando com um shared lock todos
os registros lidos, no permitindo assim que outro usurio altere o
registro, desta forma se os dados forem relidos seus valores
permanecem o mesmo. Ao contrrio da implementao anterior,
essa alternativa aumenta a possibilidade de travamentos entre as
transaes e deve ser utilizada com bastante cuidado.

Delphi Client/Server

59

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.11: TDatabase.TransIsolation.

Para se configurar o processo de Optimistic Lock no Delphi, devese utilizar a propriedade UpdateMode dos componentes DataSets:
TQuery e TTable.

Delphi Client/Server

60

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.12: TQuery.UpdateMode.

Projeto Exemplo

O projeto a seguir mostra como esses recursos so utilizados no


Delphi. O formulrio apresentado na figura define como a aplicao
deve ser construda. Deve-se utilizar duas instncias da aplicao
para simular o acesso de dois usurios simultaneamente. O exemplo
mostra o processo de travamento do registro quando uma atualizao
feita por um usurio antes do comando commit ser executado.

Fig 5.13: Projeto Exemplo

Delphi Client/Server

61

B A N C O S

D E

DA D O S

Componente
Form1
Database1

Query1
DataSource1
Grid1
Button1
Button2
Button3

R E L A CI O N A I S

Propriedade
Name
Alias
DatabaseName
Connected
DatabaseName
SQL
Request Live
DataSet
DataSource
Caption
Caption
Caption

Valor
frmLock
VENDAS
DBVENDAS
True
DBVENDAS
Select * from Cidade
True
Query1
DataSource1
Start Trans
Commit
Open

Tabela de Propriedades.

O boto Start Trans ser utilizado para iniciar a transao e


portanto implementado da seguinte maneira:
procedure TfrmLock.Button1Click(Sender: TObject);
begin
Database1.StartTransaction;
end;

O boto Commit ao ser pressionado fechar


executando o mtodo commit do TDatabase:

a transao

procedure TfrmLock.Button2Click(Sender: TObject);


begin
Database1.Commit;
end;

E o boto Open deve alternar entre abrir e fechar o Dataset


Query1.
procedure TfrmLock.Button3Click(Sender: TObject);
begin
Query1.Active:= Not Query1.Active;
end;

Depois de montada a aplicao deve-se compil-la, execut-la e abrir


mais uma instncia executando o EXE atravs do windows explorer.

Delphi Client/Server

62

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Fig 5.14: Duas instncias do projeto exemplo em execuo.

Deve-se executar agora os seguintes passos:


1. Pressionar o boto Open da primeira instncia para abrir o
DataSet;
2. Pressionar o boto Start Trans da mesma instncia para iniciar a
transao;
3. Alterar o campo CID_NUMHABITANTES do primeiro registro para
400.000;
4. Navegar para o segundo registro para que a gravao seja feita no
banco;
5. Tentar abrir a query na segunda instncia da aplicao atravs
do boto Open. O result set vir vazio, tratamento feito pelo
Delphi quando encontra o registro bloqueado;
6. Pressionar o boto commit da primeira instncia para efetivar a
gravao e liberar os locks;
7. Fechar e abrir novamente a query da segunda instncia atravs
do boto Open. Agora, os registros so trazidos para a tela;
8. O mesmo acontece se o usurio da segunda instncia tentar
alterar os dados que ainda no foram efetivados. Pressionar
Delphi Client/Server

63

B A N C O S

D E

DA D O S

R E L A CI O N A I S

novamente o boto Start Trans da primeira instncia e alterar o


campo CID_NUMHABITANTES para 300.000;
9. Tentar, agora, alterar o mesmo registro na segunda instncia. Uma
mensagem de DeadLock retornada informando que o registro
se encontra travado. Isso acontece, porque quando se tenta alterar
o registro da segunda instncia o Delphi tenta atualizar o registro,
mas esse se encontra bloqueado e no pode ser lido;

Fig 5.15: DeadLock.

Nesse exemplo foi utilizado o nvel de isolamento ReadCommited,


que somente permite o usurio l registros que j foram efetivados
por outro usurio. Pode-se fazer o mesmo exemplo anterior alterando
antes o nvel de isolamento no componente Database1 para
RepeatableRead;

Fig. 5.16: Propriedade TransIsolation do componente Database1

Entretanto, esse nvel s atribudo quando se inicia a transao.


Portanto, antes de executar o passo 5, que abre a query, deve-se
abrir a transao na segunda instncia da aplicao para definir o
novo nvel de isolamento. Assim, os dados so lidos mesmo antes do
primeiro usurio efetuar o commit, mas com os valores anteriores a
alterao feita por ele.

Delphi Client/Server

64

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Podemos utilizar esse exemplo para verificar tambm o


comportamento da propriedade UpdateMode do componente
Query1.

Fig. 5.17: Propriedade UpdateMode do componente Query1.

O objetivo desse exemplo foi monitorar o acesso ao banco de dados


atravs da alternncia de vrias propriedades dos componentes de
uma aplicao Delphi, de forma a verificar as diversas possibilidades
que o desenvolvedor pode optar durante a construo da aplicao.
Em uma aplicao real, no se separa dessa forma os comandos de
abrir uma transao, executar a operao e fechar a transao. Essas
operaes devem ser executadas seqencialmente em uma nica
interao com o usurio em um perodo de tempo bem pequeno. No
cabe ao usurio tomar a deciso de abrir e fechar a transao,
porque ele poderia deixar travado durante muito tempo um registro
que vrios outros usurios precisam utilizar.

Delphi Client/Server

65

B A N C O S

D E

DA D O S

R E L A CI O N A I S

Projetando Aplicaes
Cliente/Servidor
Esse captulo mostra algumas tcnicas de
construo de aplicaes voltadas para o
ambiente Cliente/Servidor.

Captulo

construo de aplicaes Cliente/Servidor deve obedecer


algumas regras de organizao para que os sistemas possam
ter um tempo de vida mais longo e acompanhar a evoluo do
negcio da empresa e a evoluo tecnolgica da arquitetura.

Estrutura de uma Aplicao


Como foi visto, o ambiente cliente servidor permite que a aplicao
seja distribuda entre dois componentes fsicos: a estao cliente e o
servidor de banco de dados. Entretanto, logicamente podemos
identificar trs camadas distintas dentro de uma aplicao.
Apresentao
Composta por componentes responsveis pela interao da aplicao
com o usurio final. responsabilidade dessa camada receber os
dados e comandos do usurio e devolver-lhe informaes atravs de
elementos visuais como consultas, grficos, relatrios e etc;
Lgica do Negcio
Parte da aplicao responsvel por manter as regras de negcio da
empresa. Essa camada recebe os dados da camada de interface e
executa as operaes e validaes necessrias para envi-los ao
banco de dados. Da mesma forma, extrai os dados do banco de dados
de acordo com as regras de negcio da aplicao e os envia para
elementos da interface para que sejam exibidos.
Portanto, essa camada responsvel em interligar a interface visual
com o banco de dados atravs da execuo de transaes,
consistncia dos dados e regras de negcio, ou seja, a parte funcional
da aplicao.

Delphi Client/Server

66

Gerenciamento de Dados
Parte da aplicao responsvel pelo acesso e a manipulao dos
dados no servidor. Como j foi visto anteriormente, grande parte
dessa camada implementada pelo prprio servidor de banco de
dados.
Normalmente o acesso aos servios feito atravs da linguagem
SQL. Porm, tambm necessrio um conjunto de comandos para
enviar as sentenas SQLs e gerenciar a comunicao entre a
aplicao e o servidor. Esses comandos se encontram em bibliotecas
disponibilizadas pelos prprios fornecedores de banco de dados que
so instaladas em cada estao de trabalho. Alm disso cada
fabricante de ferramentas de desenvolvimento fornece tambm
mtodos e componentes capazes de simplificar e tornar mais
transparente o acesso aos diversos SGDBs.

CLIENTE
APRESENTAO
FSICO

LGICA do NEGCIO

LGICO

GERENCIAMENTO de DADOS
SERVIDOR
Fig 6.1: Camadas Fsicas e Lgicas de uma Aplicao.

Vantagens da Organizao da Aplicao em


Camadas
A diviso da aplicao nessas trs camadas lgicas possibilita a
organizao e padronizao da codificao e construo da aplicao,
alm de proporcionar uma maior facilidade de manuteno e
evoluo para novas fases da arquitetura Cliente/Servidor. Como j
foi visto, a tendncia da arquitetura Cliente/Servidor retirar cada
vez mais parte do processamento da aplicao realizado pelas
estaes de trabalho clientes e centraliz-lo em servidores, provendo
um melhor gerenciamento do processo e facilitando a evoluo da
funcionalidade que foi distribuda.

Delphi Client/Server

67

A distribuio da aplicao em camadas lgicas possibilita tambm


que cada camada possa evoluir independente das outras desde que
se mantenha a interface entre elas. Por exemplo, pode-se alterar as
regras de negcio para atender as necessidades do mercado sem
necessariamente ter que modificar a camada de interface ou a
camada de gerenciamento de dados. Por outro lado, pode-se evoluir a
apresentao para novas tecnologias como multimdia, sem precisar
alterar as regras de negcio. Evolues tecnolgicas, como a
distribuio da base de dados na camada de gerenciamento de dados
pode ser feita de forma transparente das demais camadas. Portanto,
esses tipos de distribuio tornam as aplicaes mais escalveis para
suportar futuras implementaes possibilitando um tempo de vida
muito mais longo.

Estrutura de uma Aplicao Delphi


A ferramenta de desenvolvimento Delphi, desde sua primeira verso
j se mostrou preocupada em distribuir a aplicao nas trs
camadas lgicas. Esse esforo inicial tem beneficiado a Borland a
evoluir a ferramenta para as novas geraes da arquitetura
Cliente/Servidor. Muitas outras ferramentas que no possuam essa
filosofia de trabalho esto enfrentando srios problemas para
sensibilizar seus clientes a necessidade de trabalhar dessa forma
para conseguirem migrar e receber os benefcios da nova gerao
Cliente/Servidor.
Para implementar essa filosofia de trabalho no Delphi, a Borland
estabeleceu trs categorias de componentes: componentes visuais,
componentes de acesso base de dados e componentes de ligao.

Fig 6.2: Categorias de componentes do Delphi para acesso a base de dados.

Essa forma de trabalho organizada em camadas distintas permite


tambm uma maior reutilizao de cdigo e portanto um aumento de

Delphi Client/Server

68

produtividade na construo de aplicaes atravs dos recursos do


Delphi de criao de componentes e templates de telas.
Componentes visuais
Componentes responsveis pela interface com o usurio.
Correspondem camada de Apresentao discutida anteriormente.
Esses componentes esto localizados na pgina Data Controls da
paleta de componentes do Delphi.

Componentes de Acesso base de dados


Componentes responsveis em criar toda a estrutura necessria para
acessar e manipular o banco de dados. So os componentes
encarregados em interfacear com os servios de gerenciamento e
manipulao fornecidos pela base de dados. Esses componentes
tambm
possuem
propriedades
e
eventos
destinados
a
implementao da lgica de negcio na aplicao. Esses
componentes esto localizados na pgina Data Access da paleta de
componentes.

Componente de ligao
Componente responsvel pela interface entre as duas camadas
acima. Sua principal caracterstica tornar os componentes visuais
independentes dos componentes de acesso, ou seja, a camada de
Apresentao da Lgica do Negcio. Esse componente o
TDataSource que fica localizado tambm na pgina Data Access da
paleta de componentes.

Delphi Client/Server

69

Construindo Aplicaes
Cliente/Servidor
Esse captulo mostra a construo de uma tela
de manuteno utilizando-se os componentes
do Delphi e como configur-los para trabalhar
com banco de dados.

Captulo

xistem vrias maneiras de se utilizar os componentes do


Delphi para trabalhar com banco de dados. preciso saber
escolher as formas mais adequadas para cada tipo de
implementao. Atravs desse captulo sero mostrada vrias dessas
opes e o que cada uma difere no comportamento da aplicao com
o banco de dados.

Utilizando Data Modules


Em uma aplicao Cliente/Servidor interessante utilizar o recurso
de Data Module fornecido pelo Delphi. Data Module um
componente que fornece uma localizao centralizada para
componentes no-visveis do Delphi. Pode-se ento utilizar o Data
Module para conter os componentes da categoria Data Access,
responsveis pela lgica de negcio da aplicao e interface com o
banco de dados. Assim, toda a lgica de negcio fica concentrada em
um nico ponto da aplicao facilitando a manuteno e evoluo.
Para aplicaes maiores normal que se utilize mais de um Data
Module subdividindo a lgica de negcio.
Para exemplificarmos esse recurso, iremos construir a tela de
cadastro de produtos.. Para se construir essa tela, deve-se
inicialmente criar um DataModule e colocar nele um componente
Database.

Fig 7.1: Componente TDatabase.

Delphi Client/Server

70

Componente
DataModule1

Propriedade
Name

Valor
DMSistVendas

Tabela de Propriedades.

Componente TDatabase
Como foi visto o componente TDatabase responsvel pela conexo
com o banco de dados. Esse componente possui algumas
propriedades que permitem a configurao dessa conexo. Pode-se
utilizar a opo Database Editor do popup menu que exibido ao se
pressionar o boto direito do mouse sobre o componente Database1.

Fig 7.2: Database Editor

Atravs do Database Editor pode-se atribuir os valores para vrias


propriedades como foi mostrado na figura. Alm disso, sobrepor
valores do Alias definidos no BDE atravs do boto Defaults.
USER NAME: Esse parmetro pode ser utilizado para deixar fixo
o usurio que ir acessar o banco de dados. Algumas empresas
adotam essa forma de trabalho ao invs de criar vrios usurios no
banco de dados. Em nosso exemplo, iremos atribuir o valor
SYSDBA para o parmetro.
PASSWORD: Se for fixado o valor de USER NAME, pode-se
tambm j deixar especificada a senha do usurio.
PASSWORD=masterkey
SQLPASSTHRU MODE: Esse parmetro possui trs valores
possveis: SHARED AUTOCOMMIT, SHARED NOAUTOCOMMIT e
NOT SHARED. Deve-se especificar SHARED, quando deseja-se que
Delphi Client/Server

71

todos as atualizaes feitas automaticamente pelo Delphi, as


atualizaes feitas manualmente e o controle de transao usem a
mesma conexo. Quando a opo NOT SHARED utilizada, uma
nova conexo escondida estabelecida com o banco de dados.
Assim, todas as atualizaes feitas manualmente atravs do
mtodo ExecSql de uma Query e o controle de transao feito
atravs dos mtodos do componente TDatabase utilizam essa nova
conexo. Alm disso, se especificada a opo SHARED, pode-se
ainda
escolher
entre
as
opes
AUTOCOMMIT
e
NOAUTOCOMMIT. A primeira efetiva automaticamente qualquer
comando SQL enviado ao servidor, a menos que uma transao
seja aberta explicitamente atravs do comando start transaction.
Na segunda opo, os comandos esperam pela execuo explcita
do comando commit para serem efetivados. Em nosso exemplo,
iremos atribuir o valor SHARED AUTOCOMMIT para o parmetro.

Escolhendo entre TTable e TQuery


O Delphi possui dois componentes que permitem o acesso e
manipulao dos dados do servidor: TTable e TQuery. O primeiro
baseado no acesso a uma determinada tabela do banco de dados e o
segundo baseado em uma sentena SQL (comando select). Por ser
baseado em uma sentena SQL, o componente TQuery permite
trabalhar com mais de uma tabela do banco de dados ao mesmo
tempo. Ambos os componentes utilizam a linguagem SQL para
acessar a base de dados. Quando se trabalha com TTable, o Delphi
gera automaticamente uma sentena SQL de acordo com os
parmetros definidos para o componente. Alm dessa diferena
bsica entre os dois componentes, outros fatores devem ser
observados na escolha.
Abertura
Operao feita quando executado o mtodo Open do componente
TTable ou TQuery, que produz a compilao e execuo do comando
select. Quando esse mtodo executado atravs do componente
TTable, o Delphi realiza uma srie de outros comandos SQLs para
buscar informaes do catlogo da tabela necessrias para as
operaes de seleo e atualizao. Essa busca pode ser otimizada
atravs da opo ENABLE SCHEMA CACHE do BDE, fazendo com
que essas informaes sejam lidas apenas uma vez durante a
execuo da aplicao. Quando o primeiro acesso feito, o BDE
armazena as informaes em um arquivo e qualquer nova
necessidade de abertura da mesma tabela no necessita buscar
novamente os elementos do catlogo.
Por outro lado, utilizando-se o componente TQuery, pode-se desviar
dessa busca desde que no se utilize a propriedade Request Live
que torna o result set da query atualizvel automaticamente pelo
Delphi Client/Server

72

Delphi. Se o valor da propriedade Request Live for TRUE e o


SELECT utilizado obedecer as restries para que o Delphi consiga
atualizar o result set, as mesmas buscas utilizadas para o
componente TTable tero que ser feitas.
Concluindo, para que a busca de elementos do catlogo no seja feita
necessrio utilizar o componente TQuery e controlar as
atualizaes manualmente ou atravs de componentes do tipo
TUpdateSQL.
Filtros
Uma das grandes vantagens de se utilizar SGBDs poder fazer o
filtro no prprio servidor e portanto trafegar um nmero menor de
linhas pela rede. Para se fazer isso, necessrio utilizar a clusula
where do comando select. Quando se envia uma clusula where no
comando SQL, o prprio servidor se encarrega de selecionar os
registros que compem a pesquisa realizada, j observando as
melhores alternativas de acesso tentando utilizar o mximo dos
ndices estabelecidos no banco de dados.
Com o componente TQuery isso feito diretamente no comando SQL
suportando a sintaxe fornecida pela linguagem SQL do banco de
dados utilizado. Entretanto, se for utilizada a propriedade filter do
TQuery para filtrar o result set, o Delphi no utilizar os recursos
do SGBD para selecionar os registros e ir trazer todas as linhas
resultantes da Query para a estao. Somente quando essas linhas
forem trazidas para mquina cliente, que o filtro ser aplicado
localmente, tornando cada linha visvel ou no na tela.
Entretanto, com o componente TTable a propriedade filter e as
funes de seleo como SetRange agem de forma diferente. O
Delphi tenta traduzir as especificaes feitas atravs desses dois
mtodos e coloc-las diretamente na clusula where do select
realizado. Desta forma, consegue-se o mesmo desempenho do
componente TQuery, j que o filtro feito na prpria clusula where.
Entretanto, como o Delphi que realiza a traduo das
especificaes para a clusula where, existe uma certa limitao
dessas especificaes e se essas no conseguirem ser traduzidas, o
filtro ser feito na prpria mquina cliente. Portanto, o componente
TQuery mais abrangente no que se diz respeito a filtros, suportando
de forma mais completa a sintaxe fornecida pelo banco de dados.
Com relao ao evento OnFilterRecord, em ambos os componentes
o filtro aplicado localmente e portanto todas as linhas que
compem o result set precisam ser trazidas para a estao cliente,
no utilizando os recursos do servidor.

Delphi Client/Server

73

Transaes
Como j foi visto o componente TTable possui uma forma mais
inteligente de se comportar do que o componente TQuery quando o
result set est prestes a ser destrudo atravs da realizao de um
comando commit para finalizar a transao.
O componente TQuery necessita que todas as linhas at o final da
seleo sejam trazidas para a estao cliente antes que o commit
seja executado no banco para que o usurio no perca as linhas que
ainda no foram trazidas para aplicao.
J o componente TTable simplesmente fecha o result set sem
nenhum efeito que diminua o desempenho da atualizao e se houver
a necessidade de buscar as linhas restantes da tabela um novo select
feito a partir da ltima linha trazida.
Entretanto, alguns bancos de dados permitem que o commit no
destrua os result set ou pode-se tambm utilizar conexes
separadas para a atualizao e para o result set. Desta forma no
h necessidade do componente TQuery realizar as buscas antes da
real necessidade do usurio.
Por outro lado, o componente TTable, deixa aberta uma transao
que pode, dependendo do banco de dados, estar travando alguma
pgina da tabela.
Nmero de Tabelas Acessadas
Um outro fator relevante na escolha do componente, o nmero de
tabelas que devem ser acessadas para buscar as informaes
necessrias para o usurio em uma mesma tela. At um certo
nmero de tabelas mais interessante utilizar o recurso de joins
dos bancos para trazer em um nico comando SQL, todo o conjunto
de informaes. Nesse caso um componente TQuery deveria ser
utilizado. Quando isso feito atravs de vrios componentes TTable,
as vezes necessrio trazer os dados de todas as tabelas para a
mquina cliente para que a relao entre elas possa ser feita. No
melhor caso, se filtrarmos cada tabela pelo registro selecionado na
outra, teramos que executar vrios comandos SELECTs mais simples
no servidor contra um nico comando um pouco mais complexo do
componente TQuery.

Trabalhando com o TQuery


O componente TQuery pode ser ento utilizado para acessar e
manipular os dados. Como foi visto, a utilizao desse componente
deixa mais flexvel o acesso ao banco, j que trabalha diretamente
com a linguagem SQL. Portanto, manutenes evolutivas como
Delphi Client/Server

74

acessar mais de uma tabela atravs do mesmo componente podem


ser implementadas com mais facilidade.
Afim de continuarmos nossa aplicao, devemos colocar um
componente TQuery no DataModule DMSistVendas.

Fig 7.3: Componente TQuery

Componente
Query1

Propriedade
Name
DatabaseName
SQL

Valor
QProduto
DBVENDAS
Select * from produto
order by produto.prod_cd
TRUE
TRUE

Request Live
Active
Tabela de Propriedades.

A propriedade Request Live ligada, faz com que o Delphi tente


atualizar automaticamente o result set trazido pela query. Desta
forma, o componente TQuery se comporta de forma semelhante ao
componente TTable.
Entretanto, para que isso seja possvel, o comando SQL tem que
obedecer algumas restries como por exemplo:
Conter apenas uma tabela na clusula From;
No possuir agregaes atravs de group by e funes como
sum, max, etc;
Aps termos preenchido o DataModule com os componentes
responsveis pelo acesso ao banco de dados, podemos construir o
form de manuteno da tabela de produto como a figura a seguir:

Delphi Client/Server

75

Fig 7.4: Form de Produto.

Componente
Form1
Panel1
Panel2
DataSource1
DBNavigator1
DBEdit1..DBEdit4

Propriedade
Name
Caption
Align
Caption
Align
Caption
Name
DataSet
DataSource
DataSource
DataField

Valor
frmProduto
Produtos
alTop
alClient
DSMain
DMSistVendas.Qproduto
DSMain
DSMain
pro_cd..pro_estoque

Tabela de Propriedades.

Podemos ento executar a aplicao e verificar que atravs da


propriedade Result Live do Qproduto, foi possvel atualizar os dados
e acrescentar novas linhas tabela. Entretanto, podemos notar que
as linhas inseridas desaparecem do Result Set ao navergarmos
para outro registro. Esta uma restrio que existe no componente
TQuery quando se utiliza a propriedade Result Live para torn-lo
atualizvel automaticamente pelo Delphi. Alm disso, foi visto que
para ser possvel tornar o result set atualizvel, existem algumas
restries quanto ao comando SQL definido.
Por causa desses motivos, talvez seja ento necessrio trabalhar com
a propriedade Result Live igual a False e utilizar um outro artifcio
para tornar o result live atualizvel sem restries no comando
SQL e sem perder as linhas inseridas.

Utilizando Cached Updates


A outra forma de tornar o result set de um componente TQuery
atualizvel utilizando o recurso de cached updates do Delphi.
Delphi Client/Server

76

Quando a propriedade CachedUpdates est ligada, todas as


inseres, alteraes e excluses realizadas sobre o Dataset no so
enviadas diretamente para o banco. Ao invs disso, so armazenadas
em um cache local na memria at que se dispare um comando do
Dataset para aplicar todas as atualizaes atravs de nico processo
no banco de dados.
Com isso, toda vez que for realizado um comando post, as alteraes
sero enviadas para o cache ao invs de irem diretamente para o
banco, seja atravs do DBNavigator, automaticamente quando se
deixa a linha ou atravs da chamada explcita do comando.
Mesmo se quisermos enviar linha a linha os registros para o banco de
dados, talvez seja necessria a utilizao do recurso de cached
updates simplesmente para tornar o result set atualizvel, caso
no seja possvel utilizar a propriedade Result Live do TQuery.
Para utilizar o recurso de cached updates, deve-se ligar a
propriedade de mesmo nome do componente TQuery. Vamos fazer
isso para o QProduto, mas mantendo por enquanto a propriedade
Result Live = True.
Vamos retirar do DBNavigator alguns botes deixando somente os
botes de navegao. A seguir, vamos colocar quatro novos botes do
tipo SpeedButton para implementarmos as operaes de insero,
excluso, salvamento e cancelamento.

Fig 7.5: O form de Produto.

Componente
SppedButton1
SppedButton2
SppedButton3
SppedButton4

Propriedade
Name
Name
Name
Name

Valor
btnAppend
btnDelete
btnSave
btnDiscard

Tabela de Propriedades
Delphi Client/Server

77

Devemos agora, implementar o cdigo para os eventos OnClick dos


botes.
O primeiro boto btnAppend deve apenas criar uma nova linha no
result set atravs do mtodo append do componente QProduto.
Entretanto vamos tentar ser um pouco mais genrico no cdigo para
depois podermos reutilizar o cdigo escrito atravs de templates.
procedure TfrmProduto.btnAppendClick(Sender: TObject);
begin
DSMain.DataSet.Append;
end;

Para o segundo boto, tambm no h nenhuma novidade.


procedure TfrmProduto.btnDeleteClick(Sender: TObject);
begin
DSMain.DataSet.Delete;
end;

Usaremos o terceiro boto btnSave para aplicar no banco de dados


todas as alteraes feitas nos diversos registros e que esto por
enquanto armazenadas no cached updates.
procedure TfrmProduto.btnSaveClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
end;

Nesse caso, foi necessrio fazer um typecast na propriedade


DataSet para que ela possa identificar os mtodos ApplyUpdates e
CommitUpdates. Esses dois mtodos so responsveis em aplicar
as alteraes pendentes no cache no banco de dados.
Alm disso, necessrio utilizar a unit que contm a classe
TBDEDataSet. Podemos fazer isso na clusula use da seo
implementation.
ApplyUpdates: esse mtodo aplica no banco de dados todas as
alteraes pendentes no cached update.
CommitUpdates: esse mtodo limpa do buffer do cached
update os registros que foram aplicados no banco, aps uma
atualizao realizada com sucesso.
Para o ltimo boto (btnDiscard), podemos utilizar o mtodo
CancelUpdates que tambm limpa o buffer, mas descartando
todas as alteraes atualmente contidas no cached update.
procedure TfrmProduto.btnDiscardClick(Sender: TObject);
Delphi Client/Server

78

begin
TBDEDataSet(DSMain.DataSet).CancelUpdates;
end;

Desta forma, temos a tela de produtos funcionando atravs do


recurso cached updates, ou seja, as alteraes sendo enviadas em
conjunto para o banco de dados.
Alm disso, essa uma alternativa para criar result sets
atualizveis sem utilizar a propriedade Request Live, como
verermos a seguir.

Utilizando o Componente TUpdateSQL


Existem duas maneiras de se tornar o result set de uma query
atualizvel, sem as limitaes impostas pela definio da propriedade
Request Live. A primeira utilizando o componente TUpdateSQL.
Atravs desse componente possvel definir os comandos que sero
utilizados para efetuar as operaes de insero, alterao e
excluso. A segunda forma atravs do evento OnUpdateRecord e
ser vista mais adiante nos prximos captulos.
Vamos ento colocar um componente TUpdateSQL no DataModule
da aplicao, como
mostrado na figura, e definir algumas
propriedades atravs da tabela a seguir.

Fig 7.6: Componente USProduto.

Componente
UpdateSQL1
QProduto

Propriedade
Name
Request Live
UpdateObject

Valor
USProduto
False
USProduto

Tabela de Propriedades

Pressionando o boto direito do mouse sobre o componente, pode-se


selecionar a opo Update SQL Editor... para abrir uma tela que ir
permitir a gerao dos comandos SQLs de atualizao simplesmente
definindo alguns parmetros.

Delphi Client/Server

79

Fig 7.7: Update SQL Editor.

Na lista Table Name iro aparecer as tabelas que fazem parte da


clusula From do comando SQL. Deve-se escolher na lista a tabela
que ir sofrer as atualizaes. Aps selecionada a tabela, pode-se
pressionar o boto Get Table Fields para trazer os campos dessa
tabela para as listas direita da tela. Se existir somente uma tabela,
toda esse procedimento no ser necessrio, j que a tabela e seus
campos sero trazidos automaticamente.
As duas listas da direita apresentam todos os campos contidos na
tabela. Deve-se selecionar na primeira lista Key Fields, os campos
que sero utilizados na clusula where dos comandos update e
delete. Pode-se optar somente pela chave, caso se queira deixar a
concorrncia mais livre, ou escolher todos os campos para verificar
se o registro j foi alterado por outro usurio. Desta forma, conseguese simular os valores upWhereKeyOnly e upWhereAll
respectivamente da propriedade UpdateMode do DataSet, j que
essa propriedade s vlida quando se utiliza a propriedade
Request Live igual a True.
Para selecionar apenas a chave primria, pode-se utilizar o boto
Select Primary Keys. Em nosso exemplo, vamos utilizar esse boto
para selecionar apenas a chave primria.
Deve-se selecionar na lista UpdateFields, os campos que sero
alterados e inseridos pelos comandos update e insert.
Finalizando, o ltimo boto serve para gerar os comandos SQLs,
seguindo o que j foi definido.

Delphi Client/Server

80

Depois de gerado, os comandos podem ainda sofrer modificaes.


Pode-se por exemplo retirar a coluna PRO_CD da clusula set do
comando update, j que no devemos deixar o usurio alterar a
chave primria. Isso pode at causar um erro em alguns bancos de
dados.
Pode-se notar que em alguns lugares foram utilizados prefixos OLD
antes do nome das colunas. Isso foi feito para que seja testado o
valor anterior a modificao realizada ao invs do valor atual. Esse
processo necessrio quando utiliza-se todos os campos na clusula
where, para permitir que os campos sejam alterados e a verificao
seja feita a partir de seus valores anteriores.

Gravao Linha a Linha ou em Batch


No incio do exemplo, as gravaes eram feitas linha a linha. Ao sair
de um registro para o outro, as alteraes eram automaticamente
gravadas no banco de dados. Entretanto, este comportamento foi
alterado quando se optou em trabalhar com cached update. Apesar
do recurso cached update apresentar tais caractersticas, no foi
por esse motivo que ns o utilizamos em nosso exemplo. O motivo
principal de termos utilizado o recurso de cached updates foi para
permitir que um comando qualquer SQL, sem limitaes, pudesse
gerar um result set atualizvel.
Qual ser ento a melhor forma de trabalhar ? Fazer as gravaes
linha a linha ou armazen-las em um cache e depois envi-las todas
de uma vez para o servidor. Cada uma das alternativas possuem
vantagens e desvantagens que devem ser observadas antes de se
escolher o processo que ser utilizado.
Pode-se citar como vantagens de se trabalhar com atualizaes em
batch, ou seja, armazenando no cache e enviando o conjunto de
atualizaes de uma nica vez para o banco:
Os pacotes da rede sero melhor dimensionados, alm de diminuir
o nmero de comandos que sero enviados pela rede, tendo no
total um trfego bem menor na rede.
Se um controle de transao estiver sendo utilizado, permite que a
atualizao seja uma operao nica. Se um dos comandos falhar,
nenhum comando efetivado e o banco de dados volta para o
estado original antes do incio da transao.
Porm este mtodo tambm possui algumas desvantagens:
Esse mtodo pode confundir o usurio. O usurio pode perder o
controle do que ele j atualizou. Alm disso, se um erro ocorrer na
Delphi Client/Server

81

gravao, a correo do registro ou dos registros pelo usurio


um processo difcil de ser implementado.
Como o usurio pode levar muito tempo para alterar os registros,
existe uma probabilidade maior de um outro usurio ter alterado o
mesmo registro e portanto bloquear a gravao.
Outro fator importante a ser considerado o tempo de espera do
usurio. Em um sistema, o usurio nunca gosta de esperar muito
tempo por um determinado processo. A gravao linha a linha
distribui o processo total de atualizao em pequenos intervalos
de tempo que so muitas vezes consumidos pelo prprio tempo de
digitao do usurio tornando-os imperceptveis. J a gravao em
batch, dependendo da complexidade do processo que est sendo
realizado, pode demorar muito, j que toda a atualizao feita de
uma nica vez. O tempo total da atualizao em batch at
menor do que os feitos linha a linha, j que um nmero menor de
comandos so enviados ao banco. Entretanto o usurio poder
achar a performance da gravao linha a linha bem melhor do que
a outra, porque o tempo total diludo em diversas operaes de
tempo bem menores.
Atravs da discusso acima, pode-se concluir que as gravaes linha
a linha parecem ser um mtodo mais interessante de se utilizar, a
menos que haja a necessidade da gravao ser realizada dentro de
uma nica transao, como por exemplo, o cadastro de um pedido e
de seus itens.
Para fazer isso no Delphi, podemos continuar utilizando o recurso
cached updates, mas questionando ao usurio o salvando ou
cancelamento das alteraes antes que ele deixe o registro.
Antes de fazer isso, vamos organizar um pouco mais nossa aplicao.
Ao invs de implementarmos a lgica diretamente nos eventos dos
botes, vamos criar procedures separadas para cada evento para
que estes possam depois serem reutilizados mais facilmente.
.
.
public
{ Public declarations }
procedure Save;
procedure NewForInsert;
procedure Delete;
procedure Discard;
end;
.
.

Depois de criar as procedures, vamos transferir o cdigo contido nos


eventos dos botes para as novas procedures:
Delphi Client/Server

82

procedure TfrmProduto.btnAppendClick(Sender: TObject);


begin
NewForInsert;
end;
procedure TfrmProduto.btnDeleteClick(Sender: TObject);
begin
Delete;
end;
procedure TfrmProduto.btnSaveClick(Sender: TObject);
begin
Save;
end;
procedure TfrmProduto.btnDiscardClick(Sender: TObject);
begin
Discard;
end;
procedure TfrmProduto.NewForInsert;
begin
DSMain.DataSet.Append;
end;
procedure TfrmProduto.Delete;
begin
DSMain.DataSet.Delete;
end;
procedure TfrmProduto.Save;
begin
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
end;
procedure TfrmProduto.Discard;
begin
TBDEDataSet(DSMain.DataSet).CancelUpdates
end;

Para facilitar o gerenciamento dos eventos interessante criar uma


varivel que indique se um determinado evento est sendo
executado. Atravs desse mtodo, pode-se verificar dentro de evento,
se ele est sendo executado por um outro evento e assim bloquear
chamadas mltiplas de um mesmo evento.
Vamos criar o seguinte tipo de dado na seo type da interface:
TOperState=(opNone,opNewForInsert,opDelete,opSave,opDiscard);

Na seo public da classe TfrmProduto, podemos criar uma


varivel desse tipo:
.
.
public
{ Public declarations }
OperState: TOperState;
procedure Save;
procedure NewForInsert;
Delphi Client/Server

83

procedure Delete;
procedure Discard;
end;
.

Podemos inicializ-la no evento OnCreate da Form:


procedure TfrmProduto.FormCreate(Sender: TObject);
begin
OperState:=opNone;
end;

Agora, para cada evento devemos controlar o valor da varivel


atribuindo a ela a operao que est sendo executada e retornando
ao valor original no final do evento.
procedure TfrmProduto.NewForInsert;
var OldOperState: TOPerState;
begin
OldOperState:=OperState;
OperState:=opNewForInsert;
try
DSMain.DataSet.Append;
finally
OperState:=OldOperState;
end;
end;

Com isso, j temos o cdigo mais organizado e com mais poder de


gerenciamento. Vamos agora implementar o salvamento linha a linha.
Para isso vamos utilizar o evento OnUpdateData do DataSource
que disparado sempre que se tentar enviar uma linha alterada para
o cache, ou seja, sempre que executado um comando post.
Nesse evento iremos perguntar ao usurio se ele deseja gravar o
registro, descartar as alteraes ou cancelar a tentativa de sair do
registro.
procedure TfrmProduto.DSMainUpdateData(Sender: TObject);
var ret: integer;
begin
If OperState in [opNone,opNewForInsert] then begin
ret:=Application.MessageBox('Deseja salvar as alteraes',
'Confirmao', MB_YESNOCANCEL + MB_ICONQUESTION );
case ret of
idYes: Save;
idNo: Discard;
idCancel: Abort;
end;
end;
end;

A primeira coisa que ser feita no evento checar se nenhuma


operao conhecida est sendo executada. No queremos, por
exemplo, que a pergunta seja feita se o usurio apertar o boto de
salvar.

Delphi Client/Server

84

Depois ser feita a pergunta para o usurio e com a resposta foi


montado um comando case.
Se o usurio quiser salvar, simplesmente chamamos o comando
Save. Se ele no quiser, descartamos a alterao antes de sair do
registro. E se ele quiser cancelar, executamos o comando Abort.
Podemos melhorar o evento Discard para descartar e limpar o
cache apenas se ele conter algum registro. Caso contrrio podemos
apenas descartar as alteraes antes mesmo delas irem para o
cache.
procedure TfrmProduto.Discard;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opDiscard;
try
If TBDEDataSet(DSMain.DataSet).UpdatesPending
TBDEDataSet(DSMain.DataSet).CancelUpdates
else
TBDEDataSet(DSMain.DataSet).Cancel;
finally
OperState:=OldOperState;
end;
end;

then

O mtodo cancel do DataSet descarta as alteraes que ainda no


foram para o cached updates. Atravs da propriedade
UpdatesPending, pode-se verificar se existem registros pendentes
no cache que ainda no foram enviados para o banco.
Para finalizar, devemos acrescentar os comandos que efetivam o
cached updates no banco aps o excluso de um registro no evento
Delete.
procedure TfrmProduto.Delete;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opDelete;
try
DSMain.DataSet.Delete;
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
finally
OperState:=OldOperState;
end;
end;

Trabalhando com o TTable


Como foi visto, fazer uma simples tela de cadastro com o componente
TQuery foi um pouco trabalhoso. O mesmo no acontece com o
componente TTable, que poderia fornecer a mesma funcionalidade
atravs de um processo muito mais simples.
Delphi Client/Server

85

Vamos implementar ento uma outra tela de cadastro, mas agora


utilizando o componente TTable. Para isso vamos colocar um
componente TTable no DataModule DMSistVendas.

Fig 7.8: Componente TTable.

Componente
Table1

Propriedade
Name
DatabaseName
TableName
Active

Valor
TUF
DBVENDAS
UF
TRUE

Tabela de Propriedades.

Utilizaremos este componente TTable para acessar a tabela de UF no


banco de dados.
Vamos montar a tela de maneira parecida com a que montamos para
a tela de Produto.

Fig 7.9: Tela de UF.

Componente
form1
Panel1
Panel2
DataSource1
DBNavigator
Delphi Client/Server

Propriedade
Name
Caption
Caption
Caption
Name
DataSet
DataSource

Valor
frmUF
UF

DSMain
DMSistVendas.TUF
DSMain
86

SppedButton1
SppedButton2
SppedButton3
SppedButton4
DBEdit1..DBEdit2

Name
Name
Name
Name
DataSource
DataField

btnAppend
btnDelete
btnSave
btnDiscard
DSMain
UF_SG..UF_NM

Tabela de Propriedades.

Podemos implementar os eventos OnClick dos botes simplesmente


executando os respectivos mtodos do DataSet associado ao
DataSource DSMain.
procedure TfrmUF.btnAppendClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).Append;
end;
procedure TfrmUF.btnDeleteClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).Delete;
end;
procedure TfrmUF.btnSaveClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).Post;
end;
procedure TfrmUF.btnDiscardClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).Cancel;
end;

Podemos agora alterar a tela principal do projeto para frmUF e


executar a aplicao. Iremos notar que a tela funciona de uma forma
bem semelhante tela de Produto. Porque ento implementar telas
com TQuery, se atravs do componente TTable muito mais simples
e rpida a construo ?
Alm da facilidade, a implementao atravs do componente TTable
mais eficiente quando a transao finalizada j que no busca
todos os registros no banco. Entretanto, teremos que optar pela
implementao atravs do TQuery quando existir um nmero maior
de tabelas envolvidas em uma nica tela. Cada TTable ir executar
um comando select, que poderia talvez, ser executado por um nico
comando atravs do componente TQuery com uma performance bem
melhor. Outra pequena desvantagem do componente TTable que
ele necessita buscar as informaes de catlogo o que faz demorar
mais na primeira abertura de cada tabela. Porm com a opo
ENABLED SCHEMA CACHE do BDE ligada, esse problema pode ser
minimizado.

Delphi Client/Server

87

Captulo

Filtrando Registros
Esse captulo mostra algumas possibilidades
de seleo que podem ser apresentadas ao
usurio

m aplicaes Cliente/Servidor muito comum haver tabelas


com milhares e at milhes de registros. Sendo assim,
preciso implementar consultas de forma a no trazer todos
esses registros desnecessariamente para a aplicao. Em uma
aplicao e principalmente em uma aplicao Cliente/Servidor onde a
informao est localizada em um local fsico diferente do aplicativo
e esse local consegue prover uma inteligncia capaz de manipular os
dados, deve-se ter como regra trazer para aplicao somente as
informaes realmente necessrias para o usurio . Qualquer outra
informao, alm de poluir a tela, gera um trfego adicional e
desnecessrio na rede. Muitas vezes tentamos adivinhar a
informao que o usurio deseja ao invs de deixar que ele mesmo a
pea. As melhores aplicaes Cliente/Servidor so aquelas que
conduzem os usurios atravs das informaes realmente
necessrias detalhando-as a medida do necessrio.
As duas telas que ns construmos j comeam ativas, ou seja,
mostrando as primeiras linhas da tabela. Foi enviado um select para
o banco de dados, algumas linhas foram trazidas para aplicao
atravs da rede e nem sequer sabemos se o usurio as desejava ver.
Imagine que a tabela de produto possua cerca de 1.000.000 de
registros trazidos ordenadamente por nome. Na nossa tela de
produto, o primeiro registro j iria aparecer quando o usurio abrisse
a tela. Mas se o usurio desejasse ver um produto que comeasse
com M, ser que ele iria querer navegar pelos botes de navegao
at encontr-lo?
Delphi Client/Server

88

Alm disso, deve-se lembrar que quando se utiliza o componente


TQuery para criar o result set e um comando commit executado,
todas as linhas restantes do result set so trazidas para aplicao.
Imagine isso com 1.000.000 de registros.
Portanto, em aplicaes Cliente/Servidor, principalmente em result
sets que retornam uma grande quantidade de registro, comum
induzir o usurio a filtrar seu conjunto de registros antes de traz-los
para a aplicao. Existem vrias formas de se implementar esse
mecanismo na aplicao e a seguir vamos apresentar alguns deles.

Delphi Client/Server

89

S Q L

E X P L O R E R

QBE na mesma Tela de Manuteno


Uma forma de implementao permitir o usurio realizar uma
pesquisa atravs dos prprios campos utilizados para manuteno.
Cria-se uma nova linha em branco e o usurio atribui valores para os
campos que sero utilizados na pesquisa. Esse recurso conhecido
como QBE (Query by Exemplo), que o processo de criar um
comando SQL dinamicamente atravs de valores atribudos aos
campos da tabela. Somente os campos que possuem valores entram
no filtro. Portanto o comando SQL select tem que ser montado
dinamicamente durante a execuo da aplicao. Esse recurso
fornece uma boa flexibilidade nas consultas permitindo o usurio
chegar bem prximo do dado antes de utilizar os recursos de
navegao.
Podemos implementar esse recurso na tela de produto. Para isso
vamos acrescentar dois botes para permitir a pesquisa. O primeiro
para criar uma nova linha em branco que permita o usurio atribuir
valores aos campos. O segundo para disparar a pesquisa.
Vamos tambm desligar a propriedade Active do DataSet QProduto
para que inicialmente nenhum registro seja trazido.

Fig 8.1: Tela de Produto.

Componente
SppedButton1
SppedButton2

Propriedade
Name
Name

Valor
btnNewForSearch
btnSearch

Tabela de Propriedades

Devemos ento implementar o evento OnClick dos botes e as


procedures da seguinte maneira:
procedure TfrmProduto.btnNewForSearchClick(Sender: TObject);
Delphi Client/Server

90

S Q L

E X P L O R E R

begin
NewForSearch;
end;
procedure TfrmProduto.btnSearchClick(Sender: TObject);
begin
Search;
end;
procedure TfrmProduto.NewForSearch;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opNewForSearch;
try
If Not TBDEDataSet(DSMain.DataSet).Active then begin
TQuery(DSMain.DataSet).SQL.Text:=CmdSelect + CmdSelectNull;
TBDEDataSet(DSMain.DataSet).Open;
end;
TBDEDataSet(DSMain.DataSet).Append;
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.SearchClick;
var OldOperState: TOPerState;
sQBE: String;
begin
ActiveControl:=Nil;
OldOperState:=OPerState;
OperState:=opSearch;
try
sQBE:=BuildQBE;
TBDEDataSet(DSMain.DataSet).close;
TQuery(DSMain.DataSet).SQL.Text:=CmdSelect+ ' where ' + sQBE +
CmdOrderBy;
TBDEDataSet(DSMain.DataSet).Open;
finally
OperState:=OldOperState;
end;
end;

A procedure NewForInsert deve ser alterada para suportar a


incluso da linha se o DataSet estiver fechado.
procedure TfrmProduto.NewForInsert;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opNewForInsert;
try
If Not TBDEDataSet(DSMain.DataSet).Active then begin
TQuery(DSMain.DataSet).SQL.Text:= CmdSelect + CmdSelectNull;
TBDEDataSet(DSMain.DataSet).Open;
end;
DSMain.DataSet.Append;
finally
OperState:=OldOperState;
end;
end;

Delphi Client/Server

91

S Q L

E X P L O R E R

Devemos cuidar de mais alguns detalhes antes de executar a


aplicao:
Vamos acrescentar duas novas operaes ao tipo TOperState:
TOPerState=(opNone,opNewForInsert,opDelete,opSave,opDiscard,opNewForSearch,op
Search);

Devemos criar mais trs variveis na seo private do form para


controlar o comando select:
.
.
private
{ Private declarations }
CmdSelect: String;
CmdOrderBy: String;
CmdSelectNull: String;
public
{ Public declarations }
.
.

Vamos tambm alterar a propriedade SQL do QProduto tirando a


clusula order by:
Componente
QProduto

Propriedade
Active
SQL

Valor
False
select * from Produto

Tabela de Propriedades

Vamos acrescentar a inicializao das variveis no evento OnCreate


do Form e retornar o valor da propriedade SQL do QProduto para o
valor original no evento OnDestroy.
procedure TfrmProduto.FormCreate(Sender: TObject);
begin
OperState:=opNone;
CmdSelect:=TQuery(DSMain.DataSet).SQL.Text;
CmdOrderBy:=' order by pro_cd';
CmdSelectNull:=' where pro_cd is null ';
end;

procedure TfrmProduto.FormDestroy(Sender: TObject);


begin
TBDEDataSet(DSMain.DataSet).close;
TQuery(DSMain.DataSet).SQL.Text:=CmdSelect;
end;

Finalmente, temos que implementar a funo BuildQBE.


Futuramente, deve-se trabalhar melhor essa funo para suportar
campos datas e tratar os campos calculados e lookups do
DataSet.
Delphi Client/Server

92

S Q L

E X P L O R E R

function TfrmProduto.BuildQBE: String;


var sep:string;
j:integer;
begin
Sep:='';
For j:=0 to DSMain.DataSet.FieldCount-1 do
If (DSMain.DataSet.Fields[j].AsString <> '') then begin
If DSMain.DataSet.Fields[j].DataType = ftString then
Result:= Format('%s %s (%s like ''%s%s'')', [Result, Sep,
DSMain.DataSet.Fields[j].FieldName,
DSMain.DataSet.Fields[j].AsString,'%'])
else
Result:= Format('%s %s (%s = %s)', [Result,Sep,
DSMain.DataSet.Fields[j].FieldName,
DSMain.DataSet.Fields[j].AsString]);
Sep:='And';
end;
end;

Antes ainda de executar, vamos ajustar a abertura dos forms no


Project/Options. Deve-se alterar o Main Form novamente para
frmProduto e arrastar o DMsistVendas para o topo da lista dos
Auto-Create para que ele seja o primeiro a ser criado.

Fig 8.2: Project/Options.

Controlando os Estados da Tela


Para finalizar a tela de produtos, deve-se utilizar os estados da tela
para controlar a habilitao dos botes de edio. O componente
Delphi Client/Server

93

S Q L

E X P L O R E R

Query do Delphi j possui internamente uma mquina de estado


controlada atravs do atributo state, onde os principais estados so:
dsInactive, dsBrowse, dsEdit e dsInsert.

dsInactive

dsInsert

dsBrowse

dsEdit
Fig: 8.3: Estados do DataSet.

Entretanto esses estados no so suficientes para representar a tela


de produto. necessria a incluso de um novo estado na mquina
de estados, ficando da seguinte forma:

dsInactive

dsNewFor
Insert

dsNewFor
Search

dsBrowse

dsEdit
Fig 8.4: Nova mquina de estados.

Em nosso exemplo, podemos criar a mquina de estado no prprio


form atravs da declarao de um tipo e uma varivel.
TRecordState=(rdInactive,rdNewForSearch,rdNewForInsert,rdBrowse,rdEdit);
public
{ Public declarations }
OperState: TOperState;
RecordState: TRecordState;
Delphi Client/Server

94

S Q L

E X P L O R E R

function BuildQBE: String;


procedure Save;
procedure NewForInsert;
procedure Delete;
procedure Discard;
procedure NewForSearch;
procedure Search;
procedure SetRecordState(Value: TRecordState);
end;

Alm disso, uma srie de modificaes nos eventos devem ser feitas e
o cdigo completo e final da tela de produto apresentado abaixo.
Pode-se facilmente transformar a tela de Produtos em um Template
e adicionar no repositrio de objetos do Delphi. Pode-se fazer como
exerccio, a tela de cidades utilizando-se o template construdo. As
nicas dependncias da tela com a tabela de PRODUTO so as
variveis: CmdOrderBy e CmdSelectNull. Essas variveis
precisam ento ser redefinidas no evento OnCreate do Form que
herdar do template original.
unit produto;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ExtCtrls, StdCtrls, Mask, DBCtrls, Buttons;
type
TOperState=(opNone,opNewForInsert,opDelete,opSave,opDiscard,opNewForSearch,op
Search);
TRecordState=(rdInactive,rdNewForSearch,rdNewForInsert,rdBrowse,rdEdit);
TfrmProduto = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
DSMain: TDataSource;
DBNavigator1: TDBNavigator;
Label1: TLabel;
DBEdit1: TDBEdit;
Label2: TLabel;
DBEdit2: TDBEdit;
Label3: TLabel;
DBEdit3: TDBEdit;
Label4: TLabel;
DBEdit4: TDBEdit;
btnAppend: TSpeedButton;
btnDelete: TSpeedButton;
btnSave: TSpeedButton;
btnDiscard: TSpeedButton;
btnNewForSearch: TSpeedButton;
btnSearch: TSpeedButton;
procedure btnAppendClick(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
procedure btnDiscardClick(Sender: TObject);
procedure DSMainUpdateData(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnNewForSearchClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
Delphi Client/Server

95

S Q L

E X P L O R E R

procedure btnSearchClick(Sender: TObject);


procedure DSMainStateChange(Sender: TObject);
private
{ Private declarations }
CmdSelect: String;
public
{ Public declarations }
CmdOrderBy: String;
CmdSelectNull: String;
OperState: TOperState;
RecordState: TRecordState;
function BuildQBE: String;
procedure Save;
procedure NewForInsert;
procedure Delete;
procedure Discard;
procedure NewForSearch;
procedure Search;
procedure SetRecordState(Value: TRecordState);
end;
var
frmProduto: TfrmProduto;
implementation
uses Dm01, dbtables;
{$R *.DFM}
procedure TfrmProduto.FormCreate(Sender: TObject);
begin
OperState:=opNone;
SetRecordState(rdInactive);
CmdSelect:=TQuery(DSMain.DataSet).SQL.Text;
CmdOrderBy:=' order by pro_cd';
CmdSelectNull:=' where pro_cd is null';
end;
procedure TfrmProduto.btnAppendClick(Sender: TObject);
begin
NewForInsert;
end;
procedure TfrmProduto.btnDeleteClick(Sender: TObject);
begin
Delete;
end;
procedure TfrmProduto.btnSaveClick(Sender: TObject);
begin
Save;
end;
procedure TfrmProduto.btnDiscardClick(Sender: TObject);
begin
Discard;
end;
procedure TfrmProduto.btnNewForSearchClick(Sender: TObject);
begin
NewForSearch;
end;
procedure TfrmProduto.btnSearchClick(Sender: TObject);
begin
Delphi Client/Server

96

S Q L

E X P L O R E R

Search;
end;
procedure TfrmProduto.NewForInsert;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opNewForInsert;
try
If Not TBDEDataSet(DSMain.DataSet).Active then begin
TQuery(DSMain.DataSet).SQL.Text:= CmdSelect + CmdSelectNull;
TBDEDataSet(DSMain.DataSet).Open;
end;
DSMain.DataSet.Append;
SetRecordState(rdNewForInsert);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.Delete;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opDelete;
try
DSMain.DataSet.Delete;
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
If TBDEDataSet(DSMain.DataSet).IsEmpty then begin
TBDEDataSet(DSMain.DataSet).close;
SetRecordState(rdInactive);
end else
SetRecordState(rdBrowse);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.Save;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opSave;
try
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
SetRecordState(rdBrowse);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.Discard;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opDiscard;
try
If TBDEDataSet(DSMain.DataSet).UpdatesPending
then
TBDEDataSet(DSMain.DataSet).CancelUpdates
else
TBDEDataSet(DSMain.DataSet).Cancel;
If TBDEDataSet(DSMain.DataSet).IsEmpty then begin
TBDEDataSet(DSMain.DataSet).close;
SetRecordState(rdInactive);
Delphi Client/Server

97

S Q L

E X P L O R E R

end else
SetRecordState(rdBrowse);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.NewForSearch;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opNewForSearch;
try
If Not TBDEDataSet(DSMain.DataSet).Active then begin
TQuery(DSMain.DataSet).SQL.Text:= CmdSelect + CmdSelectNull;
TBDEDataSet(DSMain.DataSet).Open;
end;
TBDEDataSet(DSMain.DataSet).Append;
SetRecordState(rdNewForSearch);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.FormDestroy(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).close;
TQuery(DSMain.DataSet).SQL.Text:=CmdSelect;
end;
procedure TfrmProduto.Search;
var OldOperState: TOPerState;
sQBE: String;
begin
ActiveControl:=Nil;
OldOperState:=OPerState;
OperState:=opSearch;
try
sQBE:=BuildQBE;
TBDEDataSet(DSMain.DataSet).close;
TQuery(DSMain.DataSet).SQL.Text:= CmdSelect;
If sQBE <> '' then
TQuery(DSMain.DataSet).SQL.Text:=
TQuery(DSMain.DataSet).SQL.Text +' where ' + sQBE;
TQuery(DSMain.DataSet).SQL.Text:=TQuery(DSMain.DataSet).SQL.Text +
CmdOrderBy;
TBDEDataSet(DSMain.DataSet).Open;
If TBDEDataSet(DSMain.DataSet).IsEmpty then
SetRecordState(rdNewForSearch)
else
SetRecordState(rdBrowse);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.DSMainUpdateData(Sender: TObject);
var ret: integer;
begin
If OperState = opNone then begin
If RecordState = rdNewForSearch then
Discard
else begin
ret:=Application.MessageBox('Deseja salvar as alteraes',
'Confirmao', MB_YESNOCANCEL + MB_ICONQUESTION );
case ret of
Delphi Client/Server

98

S Q L

E X P L O R E R

idYes: Save;
idNo: Discard;
idCancel: Abort;
end;
end;
end;
end;
procedure TfrmProduto.DSMainStateChange(Sender: TObject);
begin
If DSMain.State=dsEdit then
SetRecordState(rdEdit);
end;
function TfrmProduto.BuildQBE: String;
var sep:string;
j:integer;
begin
Sep:='';
For j:=0 to DSMain.DataSet.FieldCount-1 do
If (DSMain.DataSet.Fields[j].AsString <> '') then begin
If DSMain.DataSet.Fields[j].DataType = ftString then
Result:= Format('%s %s (%s like ''%s%s'')',
[Result,Sep,DSMain.DataSet.Fields[j].FieldName,
DSMain.DataSet.Fields[j].AsString,'%'])
else
Result:= Format('%s %s (%s = %s)',
[Result,Sep,DSMain.DataSet.Fields[j].FieldName,
DSMain.DataSet.Fields[j].AsString]);
Sep:='And';
end;
end;
procedure TfrmProduto.SetRecordState(Value: TRecordState);
begin
RecordState:=Value;
Case RecordState of
rdInactive:
begin
btnNewForSearch.Enabled:=TRUE;
btnAppend.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
end;
rdNewForSearch:
begin
btnNewForSearch.Enabled:=FALSE;
btnAppend.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
end;
rdNewForInsert:
begin
btnNewForSearch.Enabled:=FALSE;
btnAppend.Enabled:=FALSE;
btnSearch.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
end;
rdBrowse:
begin
Delphi Client/Server

99

S Q L

E X P L O R E R

btnNewForSearch.Enabled:=TRUE;
btnAppend.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnDelete.Enabled:=TRUE;
end;
rdEdit:
begin
btnNewForSearch.Enabled:=FALSE;
btnAppend.Enabled:=FALSE;
btnSearch.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
end;
end;
end;
end.

Tela de Consulta Especfica


Uma outra forma de filtrar a pesquisa que ser feita no banco de
dados para buscar o registro que o usurio deseja, criar uma tela
de consulta separada da tela de manuteno. Com isso a tela de
manuteno no precisa conter elementos de navegao, j que pode
trabalhar com apenas um registro de cada vez. O usurio utiliza a
tela de consulta para filtrar, pesquisar e selecionar o registro
desejado que transferido para a tela de manuteno, onde
normalmente informaes mais detalhadas so apresentadas,
permitindo ao usurio realizar as manutenes necessrias sobre
esse registro.
Com esse tipo de padro de cadastro possvel tambm, fazer um
melhor balanceamento entre os dados necessrios para consulta e os
dados necessrios para a manuteno, j que estes esto em telas
diferentes. Para discutir esses conceitos, vamos implementar a tela
de cliente na aplicao exemplo.
Tela de Consulta
Na tela de consulta explora-se a tabela ou as tabelas envolvidas, no
sentido vertical, ou seja, um nmero maior de registros devem ser
trazidos para aplicao para permitir que o usurio faa a escolha
navegando entre eles. Entretanto, a seleo pode ser reduzida
horizontalmente, j que a escolha do usurio pode ser feita exibindose um nmero pequeno de colunas, e conseqentemente de tabelas,
que o auxiliem na seleo. Para isso preciso aplicar o seguinte
conceito: informaes de tabelas relacionadas tabela principal no
devem participar da seleo e sim dos filtros. Por exemplo, se o
usurio necessitar escolher clientes de uma determinada cidade, ao
invs de mostrar na consulta as cidades que o cliente pertence, faao informar antes da consulta a cidade da qual os clientes ele deseja.
Atravs dessa tcnica, elimina-se o nmero de tabelas necessrias na
Delphi Client/Server

100

S Q L

E X P L O R E R

clusula from, tornando o comando select bem mais eficiente com o


mesmo resultado para o usurio que desejava selecionar um cliente
de uma determinada cidade.
Iremos em nosso exemplo construir uma tela de consulta especfica
para selecionarmos o cliente. Antes porm, iremos criar alguns
componentes DataSets no DataModule.

Fig 8.5: DataModule DMSistVendas

Componente
Query1

Table1
Query2

Propriedade
Name
DatabaseName
SQL

Valor
QConsCliente
DBVENDAS

Name
DatabaseName
TableName
Name
DatabaseName
SQL

TConsCliUF
DBVENDAS
UF
QConsCliCidade
DBVENDAS

Params

UF_SG | String

SELECT PESSOA.PES_CD ,
PESSOA.PES_NM , PESSOA.PES_TP ,
PESSOA.PES_CGCCPF
FROM CLIENTE CLIENTE , PESSOA PESSOA
WHERE ( CLIENTE.PES_CD =
PESSOA.PES_CD )
ORDER BY PESSOA.PES_NM

SELECT * FROM CIDADE


WHERE UF_SG = :UF_SG

Tabela de Propriedades

A figura a seguir mostra a tela de consulta de clientes. Note que so


colocados na parte de cima da tela componentes TEdit para que o
usurio possa fazer a pesquisa antes de trazer os dados para tela.
Nessa seleo, encontram-se os campos UF e CIDADE que
representam o relacionamento da tabela principal PESSOA com as
tabelas UF e CIDADE. Portanto estas tabelas sero utilizadas para
permitir o usurio fazer o filtro, mas no entram no select principal
da tela.

Delphi Client/Server

101

S Q L

E X P L O R E R

Fig 8.6: Tela de Consulta de Clientes.

Componente
form1
Panel1
Label1
Label2
Label3
Label4
Edit1
Edit2
Edit3
Edit4
Button1
button2
DataSource1
DataSource2
DataSource3
Panel2
DbGrid1
Panel3
Bitbtn1
Bitbtn2

Propriedade
Name
Caption
Align
Caption
Caption
Caption
Caption
Name
Name
Name
Name
Name
Caption
Name
Caption
Name
DataSet
Name
DataSet
Name
DataSet
Align
DataSource
Align
Kind
Kind

Valor
frmConsCliente
Clientes
alTop
Cdigo
Nome
UF
Cidade
edtCodigo
edtNome
edtUF
edtCidade
btnPesquisa
Pesquisa
btnNovo
Novo
DSMain
DMSistVendas.QConsCliente
DSUF
DMSistVendas.TConsCliUF
DSCidade
DMSistVendas.QConsCliCidade
alClient
DSMain
alBottom
bkOk
bkCancel

Tabela de Propriedades

Delphi Client/Server

102

S Q L

E X P L O R E R

Para o usurio poder selecionar uma cidade e ou uma UF


utilizaremos um Grid que ser exibido quando ele posicionar no
campo de UF ou de Cidade.

Fig 8.6: Grid para permitir a seleo de um UF pelo usurio.

Para fazermos essa consulta, iremos criar uma tela com um Grid
dentro como mostrado na figura seguinte:

Fig 8.7: Grid para consulta.

Componente
Form1
DBGrid1

Propriedade
Name
BorderSyle
Align
Options

Valor
frmGrid
bsNone
alClient
[dgTitles, dgColumnResize, dgColLines,
dgTabs, dgRowSelect, dgConfirmDelete,
dgCancelOnExit]

Tabela de Propriedades

Devemos implementar tambm os seguintes eventos:


procedure TfrmGrid.DBGrid1KeyPress(Sender: TObject; var Key: Char);
begin
If Key = #13 then begin
Delphi Client/Server

103

S Q L

E X P L O R E R

ModalResult:=mrOk;
end else if key = #27 then begin
ModalResult:=mrCancel;
end;
end;
procedure TfrmGrid.FormClose(Sender:TObject; var Action:TCloseAction);
begin
Action:=caFree;
end;
procedure TfrmGrid.DBGrid1DblClick(Sender: TObject);
begin
ModalResult:=mrOk;
end;
procedure TfrmGrid.FormShow(Sender: TObject);
var p1:TPoint;
begin
P1.x:=TForm(Owner).ActiveControl.Left;
P1.y:=(TForm(Owner).ActiveControl.Top + TForm(Owner).ActiveControl.Height);
P1:=TControl(TForm(Owner).ActiveControl.Parent).ClientToScreen(P1);
If (P1.y + 150) > Screen.Height then
P1.y:=P1.y - 150 - TForm(Owner).ActiveControl.Height;
SetBounds(P1.x,P1.y,250,150);
end;

A implementao do evento FormShow faz com que a Form seja


aberta exatamente embaixo dos componentes edits da tela de
consulta.
O cdigo da tela de consulta fica ento da seguinte forma:
unit conscliente;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, Buttons, StdCtrls, Grids, DBGrids, ExtCtrls, DBCtrls;
type
TfrmConsCliente = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
DBGrid1: TDBGrid;
edtCodigo: TEdit;
Label1: TLabel;
Label2: TLabel;
edtNome: TEdit;
btnPesquisa: TButton;
Panel3: TPanel;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
DSMain: TDataSource;
btnNovo: TButton;
DSUF: TDataSource;
DSCidade: TDataSource;
edtUF: TEdit;
edtCidade: TEdit;
Label3: TLabel;
Label4: TLabel;
procedure btnNovoClick(Sender: TObject);
Delphi Client/Server

104

S Q L

E X P L O R E R

procedure btnPesquisaClick(Sender: TObject);


procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure edtUFEnter(Sender: TObject);
procedure edtCidadeEnter(Sender: TObject);
private
{ Private declarations }
CmdSelect: String;
public
{ Public declarations }
sCodCidade: String;
end;
var
frmConsCliente: TfrmConsCliente;
implementation
uses Dm01, dbtables, ConsGrid;
{$R *.DFM}
procedure TfrmConsCliente.btnNovoClick(Sender: TObject);
begin
edtCodigo.Clear;
edtNome.Clear;
edtUF.Clear;
edtCidade.Clear;
end;
procedure TfrmConsCliente.btnPesquisaClick(Sender: TObject);
var sWhere,sSelect,sep:string;
nPosOrderBy:integer;
begin
TQuery(DSMain.DataSet).Close;
Sep:='';
sWhere:=' And ';
sSelect:=CmdSelect;
If (edtCodigo.Text <> '') then begin
sWhere:=Format('%s %s (%s = %s)',[sWhere,Sep,'PES_CD',edtCodigo.Text]);
Sep:='And';
end;
If (edtNome.Text <> '') then begin
sWhere:=Format('%s %s (%s like ''%s%s'') ',
[sWhere,Sep,'PES_NM',edtNome.Text,'%']);
Sep:='And';
end;
If (edtUF.Text <> '') then begin
sWhere:=Format('%s %s (%s = ''%s'') ',[sWhere,Sep,'UF_SG',edtUF.Text]);
Sep:='And';
end;
If (edtCidade.Text <> '') then begin
sWhere:=Format('%s %s (%s = %s) ',[sWhere,Sep,'CID_CD',sCodCidade]);
Sep:='And';
end;
If Sep <> '' then begin
nPosOrderBy:=Pos('ORDER BY', UpperCase(sSelect));
if nPosOrderBy = 0 then
sSelect:=sSelect + sWhere
else
Insert(sWhere,sSelect,nPosOrderBy);
end;
Delphi Client/Server

105

S Q L

E X P L O R E R

TQuery(DSMain.DataSet).SQL.Text:=sSelect;
TQuery(DSMain.DataSet).Open;
end;
procedure TfrmConsCliente.FormCreate(Sender: TObject);
begin
CmdSelect:=TQuery(DSMain.DataSet).SQL.TExt;
DSUF.DataSet.Open;
end;
procedure TfrmConsCliente.FormDestroy(Sender: TObject);
begin
DSUF.DataSet.Close;
DSCidade.DataSet.close;
TQuery(DSMain.DataSet).SQL.TExt:=CmdSelect;
end;
procedure TfrmConsCliente.edtUFEnter(Sender: TObject);
begin
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSUF;
If (frmGrid.ShowModal=mrOk) and (Not DSUF.DataSet.IsEmpty) then
begin
TEdit(Sender).Text:=DSUF.DataSet['UF_SG'];
edtCidade.Clear;
end;
end;
procedure TfrmConsCliente.edtCidadeEnter(Sender: TObject);
begin
DSCidade.DataSet.Close;
TQuery(DSCidade.DataSet).Params[0].value:=edtUF.Text;
DSCidade.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSCidade;
If (frmGrid.ShowModal=mrOk) and (Not DSCidade.DataSet.IsEmpty) then
begin
TEdit(Sender).Text:=DSCidade.DataSet['CID_NM'];
sCodCidade:=DSCidade.DataSet.FieldByName('CID_CD').AsString;
end;
end;
end.

Tela de Manuteno
Como somente um registro trazido na tela de manuteno, pode-se
explorar mais o sentido horizontal, trazendo informaes mais
detalhadas. Porm, respeitando ainda a regra de no trazer
informaes ainda no requisitadas pelo usurio. Por exemplo, pode
ser que a tela da manuteno apresente-se dividida em fichas ou
pgina. Se a informao de uma pgina tem que acessar uma outra
tabela que no seja a tabela principal, pode-se esperar o usurio
selecionar a pgina para ento buscar essas informaes no banco de
dados.

Delphi Client/Server

106

S Q L

E X P L O R E R

Para implementarmos a tela de manuteno de clientes, devemos


primeiro criar o DataSet responsvel pela busca dos dados no banco.
Utilizaremos um componente TQuery com um comando SQL
selecionando dados de quatro tabelas do banco de dados: PESSOA,
CLIENTE, CIDADE, UF. Alm disso ser feito um filtro pelo cdigo da
pessoa para que seja selecionado apenas um registro no banco de
dados. Desta forma reduziremos o nmero de linhas e aumentaremos
a quantidade de informaes sobre um determinado cliente.

Fig 8.8: Data Module.

Componente
Query1

UpdateSQL1

Propriedade
Name
DatabaseName
CachedUpdate
UpdateObject
SQL

Valor
Qcliente
DBVENDAS
TRUE
USPessoa

Params
Name
TableName

PES_CD | Integer
USPessoa
PESSOA

SELECT PESSOA.PES_CD,
PESSOA.PES_NM,
PESSOA.PES_TP , PESSOA.PES_CGCCPF ,
PESSOA.PES_LOGRADOURO ,
PESSOA.PES_NUMERO ,
PESSOA.PES_COMPLEMENTO ,
PESSOA.PES_BAIRRO , PESSOA.CID_CD ,
PESSOA.UF_SG , PESSOA.PES_DT_NASC ,
PESSOA.CID_CD_NASC ,
PESSOA.UF_SG_NASC ,
CLIENTE.CLI_LIMITECREDITO ,
CLIENTE.CLI_DEBITO , CIDADE.CID_NM ,
UF.UF_NM
FROM PESSOA PESSOA, CLIENTE CLIENTE,
CIDADE CIDADE, UF UF
WHERE PESSOA.PES_CD =
CLIENTE.PES_CD
AND PESSOA.CID_CD = CIDADE.CID_CD
AND PESSOA.UF_SG = CIDADE.UF_SG
AND CIDADE.UF_SG = UF.UF_SG
AND PESSOA.PES_CD= :PES_CD
ORDER BY PESSOA.PES_NM

Tabela de Propriedades

Podemos agora, construir a tela de manuteno como mostra as


figuras seguintes:

Delphi Client/Server

107

S Q L

E X P L O R E R

Fig 8.9: Tela de Manuteno de Clientes (Pg 1).

Fig 8.10: Tela de Manuteno de Clientes (Pg 2).

Delphi Client/Server

108

S Q L

E X P L O R E R

Fig 8.11: Tela de Manuteno de Clientes (Pg 3).

Componente
Form1
Panel1
SpeedButton1
SpeedButton2
SpeedButton3
SpeedButton4
SpeedButton5
PageControl1
TabSheet1
TabSheet2
TabSheet3
DataSource1
DBControls

Propriedade
Name
Caption
Align
Name
Name
Name
Name
Name
Align
Caption
Caption
Caption
Name
DataSet
.....

Valor
frmCliente
Clientes
alTop
btnAppend
btnDelete
btnSave
btnDiscard
btnSearch
alClient
Informaes Gerais
Pessoa Fsica
Crditos
DSMain
DMSistVendas.Qcliente
.....

Tabela de Propriedades

Os eventos dos botes de edio sero definidos de maneira similar


ao padro utilizado pela tela de produto, ficando cada um da seguinte
forma:
procedure TfrmCliente.btnAppendClick(Sender: TObject);
begin
If Not DSMain.DataSet.Active then begin
DSMain.DataSet.Open;
end;
DSMain.DataSet.Append;
end;
procedure TfrmCliente.btnDeleteClick(Sender: TObject);
begin
DSMain.DataSet.Delete;
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
Delphi Client/Server

109

S Q L

E X P L O R E R

DSMain.DataSet.close;
end;
procedure TfrmCliente.btnSaveClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
end;
procedure TfrmCliente.btnDiscardClick(Sender: TObject);
begin
If TBDEDataSet(DSMain.DataSet).UpdatesPending
then
TBDEDataSet(DSMain.DataSet).CancelUpdates
else
TBDEDataSet(DSMain.DataSet).Cancel;
If TBDEDataSet(DSMain.DataSet).IsEmpty then begin
TBDEDataSet(DSMain.DataSet).close;
end;
end;

O boto de Pesquisa ir chamar a tela de consulta de clientes para


que o usurio possa escolher um determinado cliente para trabalhar.
Depois de escolhido, informaes mais detalhadas desse cliente so
trazidas para a tela de manuteno para que possam ser visualizadas
e editadas pelo usurio.
procedure TfrmCliente.btnSearchClick(Sender: TObject);
begin
DSMain.DataSet.Close;
frmConsCliente:=TfrmConsCliente.Create(Self);
If (frmConsCliente.ShowModal=mrOk) and
(Not (frmConsCliente.DSMain.DataSet['PES_CD'] = Null)) then
begin
TQuery(DSMain.DataSet).Params[0].value:=
frmConsCliente.DSMain.DataSet['PES_CD'];
DSMain.DataSet.Open;
end;
frmConsCliente.free;
end;

Podemos utilizar a prpria mquina de estado do DataSource


fornecida pelo Delphi para controlar a habilitao dos botes:
procedure TfrmCliente.DSMainStateChange(Sender: TObject);
begin
Case DSMain.State of
dsInactive:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
dsInsert:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
Delphi Client/Server

110

S Q L

E X P L O R E R

btnSearch.Enabled:=FALSE;
end;
dsEdit:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsBrowse:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
end;
end;

Para finalizar devemos fechar o DataSet antes de sairmos da tela de


manuteno, implementando o evento OnClose da seguinte forma:
procedure TfrmCliente.FormClose(Sender: TObject; var Action: TCloseAction);
begin
DSMain.DataSet.Close;
end;

Recursos de LookUp
Existem vrias formas de fornecer para o usurio uma pesquisa em
registros de tabelas secundrias relacionadas com a tabela principal.
Esse recurso, muitas vezes chamado de lookup deve prover
implementaes para as trs fases seguintes:
Preencher e exibir uma lista ou tabela de registros para permitir a
escolha do usurio;
Ao se escolher um registro, deve-se atualizar o cdigo relacionado
na tabela principal;
Quando for selecionado um registro na tabela principal, deve-se
selecionar o registro correspondente na tabela secundria,
permitindo a exibio de alguns campos como a descrio.
Campo LookUp do Delphi

O Delphi permite a criao de campos lookups dentro de um


dataset para apresentar uma lista com os registros de uma outra
tabela relacionada. Esse recurso implementa as trs fases discutidas
acima da seguinte forma:
Fase 1.
A lista preenchida atravs de um DataSet que
seleciona os registros da tabela relacionada. utilizado
componentes combo box ou list box para apresentar a lista.
Delphi Client/Server

111

S Q L

E X P L O R E R

Quando utilizado combo box, a parte edit no habilitada para


edio, forando o usurio a realmente escolher um elemento
vlido da lista. Como utilizado um componente DataSet para
prover a seleo, possvel atribuir filtros ao result set,
desde que no inviabilize a fase 3. Quando o recurso
implementado diretamente atravs dos componentes (combo
box e list box) permite que o usurio visualize mais de um
campo ao mesmo tempo para seleo. Para se abrir a lista de
opes necessrio obviamente que o DataSet esteja aberto.
Fase 2.
Quando selecionado
um registro
da
lista,
automaticamente feita a atualizao do campo da tabela
principal relacionado com a chave primria da tabela utilizada
para seleo.
Fase 3.
No necessria a participao da descrio ou de
qualquer outro campo que se deseje visualizar da tabela
secundria no comando select da tabela principal. Quando o
campo (chave estrangeira) da tabela principal que se relaciona
com a tabela secundria trazido para a tela,
automaticamente o Delphi procura no DataSet secundrio o
registro e mostra os campos descritivos da tabela secundria.
Portanto, todas as linhas da tabela secundria devem estar
disponveis na estao cliente para que qualquer cdigo seja
encontrado. Por isso preciso tomar bastante cuidado na
atribuio de filtros para permitir que os registros necessrios
sejam encontrados pelo Delphi.
A principal vantagem desse mtodo a facilidade de implement-lo
no Delphi. Alm disso ele simplifica o comando SQL utilizado para a
tabela principal, j que no necessita da participao de outras
tabelas no select.
Mas deve-se tomar cuidado com sua utilizao quando as tabelas
relacionadas possuem muitos registros porque basicamente todos os
registros tero que ser trazidos para a mquina do usurio pela rede.
Imagine selecionar atravs desse recurso uma tabela de clientes com
cerca de 1.000.000 de registros. Apesar da possibilidade de se
atribuir filtros ao DataSet responsvel em popular a lista, isto difcil
devido a necessidade da fase trs de encontrar o registro atravs do
cdigo. Se filtros forem colocados, pode ser que esse registro no
seja encontrado.
Outro fator que deve ser observado a quantidade de tabelas
relacionadas com a tabela principal. Se a tabela principal possuir
muitas tabelas relacionadas, para abrir a tabela principal
necessrio abrir tambm todas as tabelas secundrias o que pode
prejudicar muito a performance nesse ponto.

Delphi Client/Server

112

S Q L

E X P L O R E R

Outros Mtodos

Apesar do nosso exemplo da tela de cliente tratar tabelas pequenas


como UF e cidade, tentaremos mostrar a utilizao de recursos
alternativos quando o mtodo lookup do Delphi no for satisfatrio.
Fase 1.
Utilizaremos o mesmo recurso utilizado na tela de
consulta de clientes para propiciar a seleo de um registro
secundrio na tela de manuteno de clientes. Quando o
usurio posicionar nos campos da tabela principal que se
relacionam com as tabelas secundrias, um grid ser exibido
para permitir a escolha.
Fase 2.
A atualizao dos campos da tabela principal feita
quando o usurio fecha a tela de pesquisa escolhendo uma das
opes.
Fase 3.
Para evitar que os DataSets responsveis pelas tabelas
secundrios necessitem ser abertos para a seleo de um
registro da tabela principal, inclumos os campos de descrio
no prprio comando select.
Mais adiante, nos prximos captulos, tentaremos melhorar ainda
mais esse recurso, permitindo ao usurio a atribuio de filtros.
Portanto os eventos OnEnter dos campos relacionados UF_SG e
CID_CD ficam da seguinte forma:
procedure TfrmCliente.DBEdit9Enter(Sender: TObject);
begin
If DSMain.State <> dsInactive then begin
If Not DSUF.DataSet.Active then DSUF.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSUF;
If (frmGrid.ShowModal=mrOk) and (Not DSUF.DataSet.IsEmpty) then begin
If DSMain.State = dsBrowse then DSMain.DataSet.Edit;
DSMain.DataSet['UF_SG']:=DSUF.DataSet['UF_SG'];
DSMain.DataSet['UF_NM']:=DSUF.DataSet['UF_NM'];
DSMain.DataSet['CID_NM']:='';
DSMain.DataSet['CID_CD']:=Null;
end;
end;
end;
procedure TfrmCliente.DBEdit8Enter(Sender: TObject);
begin
If DSMain.State <> dsInactive then begin
DSCidade.DataSet.Close;
TQuery(DSCidade.DataSet).Params[0].AsString:=
DSMain.DataSet.FieldByName('UF_SG').AsString;
DSCidade.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSCidade;
If (frmGrid.ShowModal=mrOk) and (Not DSCidade.DataSet.IsEmpty) then begin
If DSMain.State = dsBrowse then DSMain.DataSet.Edit;
DSMain.DataSet['CID_NM']:=DSCidade.DataSet['CID_NM'];
DSMain.DataSet['CID_CD']:=DSCidade.DataSet['CID_CD'];
end;
end;
Delphi Client/Server

113

S Q L

E X P L O R E R

end;

Buscando Registros de outras TabSheets


Para no se pesar muito o comando select da tabela principal, no
foi includa a busca da descrio da UF e CIDADE de nascimento que
esto localizados em uma outra TabSheet. No vale a pena
acrescentar mais duas tabelas na clusula from do comando, sendo
que o usurio pode nem se quer entrar na pgina onde se encontram
os dados. Deve-se sempre seguir o lema de no trazer para a
aplicao informaes que o usurio ainda no pediu para ver ou
editar. Se dados de uma tabela secundria s sero vistos se o
usurio selecionar determinada TabSheet, pode-se postergar sua
busca para quando a TabSheet for selecionada. Entretanto, quando
as informaes esto em uma mesma tabela j utilizada no comando
select para trazer os dados da TabSheet inicial, no vale a pena
enviar outro select para buscar o restante das informaes de uma
mesma tabela.
A implementao do cdigo fica ento como a seguir:
procedure TfrmCliente.PageControl1Change(Sender: TObject);
begin
If (PageControl1.ActivePage = TabSheet2) and
(DSMain.State = dsBrowse) then begin
If DSMain.DataSet['UF_SG_NASC'] <> Null then begin
DMSistVendas.QGeral.SQL.Text:=
'select UF_NM from UF where UF_SG = ''' +
DSMain.DataSet['UF_SG_NASC'] + '''';
DMSistVendas.QGeral.Open;
EdtUFNmNasc.Text:=DMSistVendas.QGeral['UF_NM'];
DMSistVendas.QGeral.close;
end else begin
EdtUFNmNasc.Text:='';
end;
If DSMain.DataSet['CID_CD_NASC'] <> Null then begin
DMSistVendas.QGeral.SQL.Text:=
'select CID_NM from Cidade where CID_CD = ' +
DSMain.DataSet.FieldByName('CID_CD_NASC').AsString +
' and UF_SG = ' +
DSMain.DataSet.FieldByName('UF_SG_NASC').AsString;
DMSistVendas.QGeral.Open;
EdtCidNmNasc.Text:=DMSistVendas.QGeral['CID_NM'];
DMSistVendas.QGeral.close;
end else begin
EdtCidNmNasc.Text:='';
end;
end;
end;
procedure TfrmCliente.DBEdit14Enter(Sender: TObject);
begin
If DSMain.State <> dsInactive then begin
If Not DSUF.DataSet.Active then DSUF.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSUF;
If (frmGrid.ShowModal=mrOk) and (Not DSUF.DataSet.IsEmpty) then begin
If DSMain.State = dsBrowse then DSMain.DataSet.Edit;
DSMain.DataSet['UF_SG_NASC']:=DSUF.DataSet['UF_SG'];
Delphi Client/Server

114

S Q L

E X P L O R E R

DSMain.DataSet['CID_CD_NASC']:=Null;
edtUFNmNasc.Text:=DSUF.DataSet['UF_NM'];
edtCidNmNasc.Text:='';
end;
end;
end;
procedure TfrmCliente.DBEdit13Enter(Sender: TObject);
begin
If DSMain.State <> dsInactive then begin
DSCidade.DataSet.Close;
TQuery(DSCidade.DataSet).Params[0].AsString:=
DSMain.DataSet.FieldByName('UF_SG_NASC').AsString;
DSCidade.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSCidade;
If (frmGrid.ShowModal=mrOk)
and (Not DSCidade.DataSet.IsEmpty) then begin
if DSMain.State = dsBrowse then DSMain.DataSet.Edit;
edtCidNmNasc.Text:=DSCidade.DataSet['CID_NM'];
DSMain.DataSet['CID_CD_NASC']:=DSCidade.DataSet['CID_CD'];
end;
end;
end;

Controlando Transaes
Quando se trabalha com banco de dados relacionais, pode-se utilizar
o recurso de transao para efetivar o processo como um todo ou
voltar todas as atualizaes desde o incio do processo caso algum
erro ocorra.
Em nosso exemplo, por enquanto, a manuteno de clientes est
sendo feita somente na tabela de PESSOA. Para que ele fique correto
de acordo com a lgica de negcio da aplicao, devemos atualizar
tambm a tabela de CLIENTE nas manutenes feitas pelo usurio.
Portanto, quando inserimos um registro devemos inser-lo na tabela
de PESSOA e tambm na tabela de CLIENTE. E essa insero deve
estar dentro de uma transao, para que esse processo seja feito
como um todo. Ou inserimos nas duas tabelas ou em nenhuma,
mantendo assim a integridade dos dados no banco. A mesma coisa
deve ser feita para as alteraes e excluses.
Para executarmos as operaes de insero, alterao e excluso nas
duas tabelas podemos utilizar dois componentes UpdateSQL. Assim,
no ser mais possvel utilizar a propriedade UpdateObject do
componente TQuery para definir qual dos dois componentes ser
utilizado, j que queremos utilizar os dois.
Devemos ento utilizar, ao invs dessa propriedade, um evento do
componente TQuery chamado OnUpdateRecord. Atravs desse
evento, podemos chamar cada um dos comandos fornecidos pelos
componentes UpdateSQL e fazer todas as atualizaes necessrias
nas tabelas.
Delphi Client/Server

115

S Q L

E X P L O R E R

Fig 8.12: Data Module.

procedure TDMSistVendas.QClienteUpdateRecord(DataSet: TDataSet;


UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
If Not Assigned(USPessoa.DataSet) then begin
USPessoa.DataSet:=TBDEDataSet(DataSet);
USCliente.DataSet:=TBDEDataSet(DataSet);
end;
if UpdateKind = ukDelete then begin
USCliente.Apply(UpdateKind);
USPessoa.Apply(UpdateKind);
end else begin
USPessoa.Apply(UpdateKind);
USCliente.Apply(UpdateKind);
end;
UpdateAction:=uaApplied;
end;

Na implementao, devemos primeiramente inicializar a propriedade


DataSet dos componentes UpdateSQL caso seja a primeira vez que o
cdigo esteja sendo executado.
A seguir utilizado o mtodo Apply dos componentes UpdateSQL
para executar o comando SQL de acordo com a informao passada
pelo BDE de qual operao est sendo realizada. Se a operao for
de excluso, iremos excluir primeiro na tabela de CLIENTE e depois
na tabela de PESSOA para manter a integridade. E o contrrio feito
no caso de inseres e alteraes de registros.
Se a operao for realizada como sucesso, iremos definir o parmetro
UpdateAction como sendo uaApplied que permite que mais tarde
o registro seja retirado do cached update atravs do comando
CommitUpdates. Se algum erro ocorrer define o parmetro como
usFail e continua a propagao do erro para que o controle de
transao feito na tela de cliente possa fazer o rollback no banco
de dados.
Devemos agora fazer algumas modificaes na tela de manuteno
de clientes para implementar o controle de transao. Primeiro
vamos criar uma procedure nova para cuidar da habilitao dos
Delphi Client/Server

116

S Q L

E X P L O R E R

botes. Retira-se o cdigo que estava no evento OnStateChange do


DataSource e o coloca nessa nova procedure com algumas
modificaes.
procedure TfrmCliente.EnableBtnControls;
begin
Case DSMain.State of
dsInactive:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
dsInsert:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsEdit:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsBrowse:
If TQuery(DSMain.DataSet).UpdatesPending then begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end else begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
end;
end;

Agora podemos modificar os eventos de gravao e excluso:


procedure TfrmCliente.btnDeleteClick(Sender: TObject);
begin
DMSistVendas.Database1.StartTransaction;
try
DSMain.DataSet.Delete;
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
DMSistVendas.Database1.Commit;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
DSMain.DataSet.close;
except
DMSistVendas.Database1.Rollback;
TBDEDataSet(DSMain.DataSet).CancelUpdates;
end;
Delphi Client/Server

117

S Q L

E X P L O R E R

end;
procedure TfrmCliente.btnSaveClick(Sender: TObject);
begin
DMSistVendas.Database1.StartTransaction;
try
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
DMSistVendas.Database1.Commit;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
EnableBtnControls;
except
DMSistVendas.Database1.Rollback;
end;
end;

Mantendo o Result Set da Consulta


Toda vez que a tela de consulta de clientes aberta o DataSet est
fechado para permitir uma nova pesquisa do usurio. No entanto,
pode-se optar em manter o ltimo result set feito quando se abrir a
tela novamente, supondo que o usurio tenha uma grande chance de
encontrar o prximo registro para manuteno no prprio conjunto
de linhas retornado pela pesquisa anterior.
Para fazer isso devemos alterar o cdigo da tela de consulta para que
ela no feche o DataSet quando o form for destrudo. Alm disso
algumas outras alteraes devem ser feitas.
Devemos alterar os seguintes eventos do form:
procedure TfrmConsCliente.FormCreate(Sender: TObject);
begin
// CmdSelect:=TQuery(DSMain.DataSet).SQL.TExt;
CmdSelect:=DMSistVendas.CmdSelectQCliente;
If Not DSUF.DataSet.Active then DSUF.DataSet.Open;
end;
procedure TfrmConsCliente.FormDestroy(Sender: TObject);
begin
//DSCidade.DataSet.close;
//TQuery(DSMain.DataSet).SQL.TExt:=CmdSelect;
end;

Devemos
tambm
declarar
e
inicializar
a
varivel
CmdSelectQCliente no DataModule para armazenar o comando
original da query.
private
{ Private declarations }
public
{ Public declarations }
CmdSelectQCliente: String;
end;

procedure TDMSistVendas.DMSistVendasCreate(Sender: TObject);


begin
Delphi Client/Server

118

S Q L

E X P L O R E R

CmdSelectQCliente:=QConsCliente.SQL.Text;
end;

Fazendo essas alteraes, quando se abre novamente a tela a ltima


consulta feita pelo usurio permanece ativa. Entretanto, deve-se ter
cuidado com essa abordagem. Se o result set permanecer aberto e
esse possuir muitas linhas que ainda no foram trazidas para
aplicao, essas podem ser trazidas quando o usurio realizar uma
manuteno no registro e salvar as alteraes. Como foi visto, para
alguns bancos de dados o Delphi realiza a busca prematura dos
registro de um result set pendente antes de realizar o commit.
Portanto, deve-se optar pelo melhor desempenho e funcionalidade
para o usurio de acordo com o banco de dados. As opes para os
bancos que no suportam manter um cursor aberto aps o
fechamento da transao seriam:
Tentar minimizar o nmero de registros que sero trazidos na
consulta forando o usurio a atribuir algum tipo de filtro.
Utilizar uma outra conexo, atravs de um outro componente
Database para se fazer a consulta, desta forma a finalizao da
transao de uma conexo no interfere na outra. Entretanto, essa
opo costuma consumir um nmero maior de licenas de usurios
do banco de dados. Normalmente, cada conexo corresponde ao
uso de uma licena.
Tentar utilizar na tela de consulta um componente TTable ao invs
do componente TQuery. Como foi visto o componente TTable
possui um comportamento mais inteligente no caso do fechamento
da transao, j que permite que o cursor seja fechado e quando
houver a necessidade de caminhar por linhas ainda no trazidas
dispara um novo comando select. Entretanto, esse componente
costuma abrir uma transao e pode estar mantendo algum
registro travado, dependendo do banco de dados utilizado.

Delphi Client/Server

119

S Q L

E X P L O R E R

Delphi Client/Server

120

S Q L

E X P L O R E R

Controlando Transaes
Master/Detail
Esse captulo mostra como implementar telas
que exigem um controle de transao do tipo
Master/Detail.

Captulo

travs do recurso de controle de transaes disponveis nos


servidores de banco de dados, possvel construir aplicaes
que mantenham a unicidade lgica de um conjunto de
operaes. Muitas vezes necessrio que as atualizaes em vrias
tabelas sejam feitas em conjunto para que a integridade dos dados
seja preservada. Se qualquer problema for encontrado durante a
transao, os dados devem retornar aos seus valores originais antes
do incio da transao.

Em algumas aplicaes, para manter a integridade dos dados atravs


do controle de transao necessrio inserir um registro de uma
tabela principal juntamente com vrios outros registros relacionados
de uma segunda tabela. o caso por exemplo de uma tela de entrada
de pedidos. Em um cadastro de pedido inserido dados do cabealho
(nmero, cliente, vendedor, data) e dados de cada item que est
sendo vendido. comum que as regras de negcio que a aplicao
deva seguir, imponha que o pedido deva ser inserido como um todo
(cabealho e seus itens), ou seja, em um nica transao.
A esse tipo de tela dado o nome de Master/Detail, ou seja, a tabela
mestre e seus detalhes. No exemplo de pedido, a entidade master
representada pela tabela de PEDIDO e a entidade detalhe
representada pela tabela de ITEM.

Tela de Consulta
Podemos comear definindo nossa tela de consulta, que o usurio ir
utilizar para pesquisar um pedido, assim como foi feito para a tela de
clientes.
Utilizaremos mais trs componentes TQuery que sero colocados no
Data Module.

Delphi Client/Server

121

Fig: 9.1: Data Module.

Componente
Query1

Query2

Propriedade
Name
DatabaseName
SQL

Valor
QConsPed
DBVENDAS

Name
DatabaseName
SQL

QConsPedCliente
DBVENDAS

SELECT PEDIDO.PED_CD , PEDIDO.PED_DT ,


PEDIDO.PED_VALOR , PEDIDO.PED_TIPO
FROM PEDIDO PEDIDO
ORDER BY PEDIDO.PED_CD

SELECT PESSOA.PES_CD , PESSOA.PES_NM


FROM CLIENTE CLIENTE , PESSOA PESSOA
WHERE ( CLIENTE.PES_CD = PESSOA.PES_CD )
ORDER BY PESSOA.PES_NM
*Obs: Deixe a quarta linha em branco

Query3

Name
DatabaseName
SQL

QConsPedVend
DBVENDAS
SELECT PESSOA.PES_CD , PESSOA.PES_NM
FROM VENDEDOR VENDEDOR , PESSOA PESSOA
WHERE ( VENDEDOR.PES_CD = PESSOA.PES_CD )
ORDER BY PESSOA.PES_NM

Tabela de Propriedades

Devemos, agora, montar a tela como a figura a seguir:

Delphi Client/Server

122

Fig 9.2: Tela de Consulta de Pedidos.

Componente
form1
DataSource1
DataSource2
DataSource3
Panel1
Edit...
button1
button2
Panel2
DBGrid1
Panel3
bitbtn1
bibtn2

Propriedade
Name
Caption
Name
DataSet
Name
DataSet
Name
DataSet
Align
Name
Name
Caption
Name
Caption
Align
Align
DataSource
Align
Kind
Kind

Valor
frmConsPedido
Pedidos
DSMain
DMSistVendas.QConsPed
DSCliente
DMSistVendas.QConsPedCliente
DSVend
DMSistVendas.QConsPedVend
alTop
edtNumero, edtCodCliente, edtNomeCliente,
edtCodVend, edtNomeVend
btnPesquisa
Pesquisa
btnNovo
Novo
alClient
alClient
DSMain
alBottom
bkOk
bkCancel

Tabela de Propriedades

Iremos propor aqui uma consulta um pouco diferente da apresentada


na tela de consulta de clientes e vendedores. Para escolher um
cliente, o usurio poder digitar as primeiras letras do nome, antes
de abrir o Grid para consulta, tornando a pesquisa mais seletiva.
Desta forma evitaremos trazer todos os registros da tabela de cliente
a estao de trabalho, alm de permitir uma localizao mais rpida
e precisa do registro pelo usurio. Com isso o usurio utiliza melhor
e se beneficia dos recursos fornecidos pelos servidores de banco de
dados.
Delphi Client/Server

123

Fig 9.3: Pesquisa de cliente filtrada pelas letras digitadas pelo usurio.

A seguir apresentamos o cdigo da unit da tela de consulta de


pedidos:
unit conspedido;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Grids, DBGrids, ExtCtrls, Db, Buttons;
type
TfrmConsPedido = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
edtNumero: TEdit;
Label1: TLabel;
Label4: TLabel;
edtCodCliente: TEdit;
edtNomeCliente: TEdit;
Label5: TLabel;
edtNomeVend: TEdit;
DBGrid1: TDBGrid;
btnPesquisa: TButton;
btnNovo: TButton;
edtCodVend: TEdit;
Panel3: TPanel;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
DSMain: TDataSource;
DSCliente: TDataSource;
DSVend: TDataSource;
procedure btnNovoClick(Sender: TObject);
procedure btnPesquisaClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure edtNomeClienteKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
Delphi Client/Server

124

procedure edtNomeVendKeyUp(Sender: TObject; var Key: Word;


Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
CmdSelect:String;
end;
var
frmConsPedido: TfrmConsPedido;
implementation
uses Dm01,dbtables, ConsGrid;
{$R *.DFM}
procedure TfrmConsPedido.btnNovoClick(Sender: TObject);
var i:integer;
begin
For i:=0 to Panel1.ControlCount - 1 do
If Panel1.Controls[i] is TEdit then
(Panel1.Controls[i] as TEdit).Clear;
end;
procedure TfrmConsPedido.btnPesquisaClick(Sender: TObject);
var sWhere,sSelect,sep:string;
nPosOrderBy:integer;
begin
TQuery(DSMain.DataSet).Close;
Sep:='';
sWhere:=' where ';
sSelect:=CmdSelect;
If (edtNumero.Text <> '') then begin
sWhere:=Format('%s %s (%s = %s) ',[sWhere,Sep,'PED_CD',edtNumero.Text]);
Sep:='And';
end;
If (edtCodCliente.Text <> '') then begin
sWhere:=Format('%s %s (%s = %s)',
[sWhere,Sep,'PES_CD_CLI',edtCodCliente.Text]);
Sep:='And';
end;
If (edtCodVend.Text <> '') then begin
sWhere:=Format('%s %s (%s = %s)',
[sWhere,Sep,'PES_CD_VEN',edtCodVend.Text]);
Sep:='And';
end;
If Sep <> '' then begin
nPosOrderBy:=Pos('ORDER BY', UpperCase(sSelect));
if nPosOrderBy = 0 then
sSelect:=sSelect + sWhere
else
Insert(sWhere,sSelect,nPosOrderBy);
end;
TQuery(DSMain.DataSet).SQL.Text:=sSelect;
TQuery(DSMain.DataSet).Open;
end;
procedure TfrmConsPedido.FormCreate(Sender: TObject);
begin
CmdSelect:=TQuery(DSMain.DataSet).SQL.TExt;
end;
procedure TfrmConsPedido.FormDestroy(Sender: TObject);
begin
Delphi Client/Server

125

DSMain.DataSet.Close;
TQuery(DSMain.DataSet).SQL.TExt:=CmdSelect;
end;
procedure TfrmConsPedido.edtNomeClienteKeyUp(Sender: TObject;
var Key: Word; Shift: TShiftState);
begin
If Key = 113 then begin
DSCliente.DataSet.Close;
If edtNomeCliente.Text <> '' then
TQuery(DSCliente.DataSet).SQL[3]:=' AND PESSOA.PES_NM LIKE '''
+ edtNomeCliente.TExt + '%'''
Else
TQuery(DSCliente.DataSet).SQL[3]:='';
DSCliente.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSCliente;
If (frmGrid.ShowModal=mrOk) and (Not DSCliente.DataSet.IsEmpty)
then begin
TEdit(Sender).Text:=DSCliente.DataSet['PES_NM'];
edtCodCliente.Text:=DSCliente.DataSet.FieldByName('PES_CD').AsString;
end;
end;
end;
procedure TfrmConsPedido.edtNomeVendKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
If Key = 113 then begin
If Not DSVend.DataSet.Active then
DSVend.DataSet.Open;
frmGrid:=TFrmGrid.Create(Self);
frmGrid.DBGrid1.DataSource:=DSVend;
If (frmGrid.ShowModal=mrOk) and (Not DSVend.DataSet.IsEmpty)
then begin
TEdit(Sender).Text:=DSVend.DataSet['PES_NM'];
edtCodVend.Text:=DSVend.DataSet.FieldByName('PES_CD').AsString;
end;
end;
end;
end.

Tela de Manuteno
Para fazer a manuteno do pedido, vamos acrescentar no Data
Module os seguintes componentes:

Delphi Client/Server

126

Fig. 9.4: Data Module

Componente
Query1

Query2

UpdateSQL1
UpdateSQL2
Query3

Propriedade
Name
DatabaseName
SQL

Valor
Qpedido
DBVENDAS

UpdateObject
CachedUpdate
Name
DatabaseName
SQL

USPedido
TRUE
Qitem
DBVENDAS

UpdateObject
CachedUpdate
Name
Name
Name
DatabaseName
SQL

USItem
TRUE
USPedido
USItem
QConsPedProd
DBVENDAS

SELECT PEDIDO.PED_CD , PEDIDO.PED_DT ,


PEDIDO.PED_VALOR , PEDIDO.PED_TIPO ,
PEDIDO.PES_CD_CLI , PEDIDO.PES_CD_VEN ,
PESSOA.PES_NM
FROM PEDIDO PEDIDO , CLIENTE CLIENTE ,
PESSOA PESSOA
WHERE ( CLIENTE.PES_CD = PESSOA.PES_CD )
AND ( PEDIDO.PES_CD_CLI = CLIENTE.PES_CD )
AND (PEDIDO.PED_CD = :PED_CD)
ORDER BY
PEDIDO.PED_CD

SELECT ITEM.PED_CD , ITEM.PRO_CD ,


ITEM.ITE_VALOR , ITEM.ITE_QUANT ,
PRODUTO.PRO_NM
FROM ITEM ITEM , PRODUTO PRODUTO
WHERE ( ITEM.PRO_CD = PRODUTO.PRO_CD )
and ( ITEM.PED_CD = :PED_CD)
ORDER BY ITEM.PRO_CD

SELECT * FROM PRODUTO


ORDER BY PRO_NM
Obs: Deixar a segunda linha em branco.

Tabela de Propriedades

Deve-se criar tambm, um novo campo do tipo lookup em QPedido


para permitir a escolha do vendedor atravs de seu nome. Iremos
Delphi Client/Server

127

nessa tela, utilizar o prprio recurso de lookup do Delphi para a


seleo do vendedor.
A tela de manuteno de pedidos dever ser construda como a figura
abaixo. Pode-se notar que sero apresentadas na mesma tela os
dados das tabelas PEDIDO e ITEM para permitir a manuteno
integral dos dados do pedido.

Fig 9.5: Tela de Manuteno de Pedido.

Componente
form1
DataSource1
DataSource2
DataSource3
Panel1
SpeedButton1
SpeedButton2
SpeedButton3
SppedButton4
SpeedButton5
DBEdit...,
DBRadioGroup
DBGrid1
SpeedButton6
SpeedButton7
SpeedButton8
Delphi Client/Server

Propriedade
Name
Caption
Name
DataSet
Name
DataSet
Name
DataSet
Align
Name
Name
Name
Name
Name
DataSource

Valor
frmPedido
Pedidos
DSMain
DMSistVendas.Qpedido
DSDetail
DMSistVendas.QItem
DSCliente
DMSistVendas.QConsPedCliente
alTop
btnAppend
btnDelete
btnSave
btnDiscard
btnSearch
DSMain

DataSource
Name
Name
Name

DSDetail
btnDetailAppend
btnDetailDelete
btnDetailDiscard
128

Tabela de Propriedades

Lgica visual
Antes de comear a definir os eventos dos botes, iremos criar um
procedimento para controlar a habilitao desses botes.
procedure TfrmPedido.EnableBtnControls;
begin
Case DSMain.State of
dsInactive:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
dsInsert:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsEdit:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsBrowse:
If TQuery(DSMain.DataSet).UpdatesPending then begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end else begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
end;
end;

Podemos tambm definir alguns eventos de comportamento geral:


procedure TfrmPedido.FormClose(Sender: TObject; var Action: TCloseAction);
begin
DSDetail.DataSet.Close;
DSMain.DataSet.Close;
DSCliente.DataSet.close;
end;

Delphi Client/Server

129

O evento OnClose do Form pode ser utilizado para fechar os


DataSets que sero abertos durante o uso da tela.
Atravs do evento OnStateChange, podemos chamar a procedure
EnableBtnControls para controlar a habilitao dos botes.
procedure TfrmPedido.DSMainStateChange(Sender: TObject);
begin
EnableBtnControls;
end;

Agora vamos implementar os eventos de cada boto da tela, a


comear pelos da Detail (tabela de itens).
No evento OnClick do boto btnDetailAppend da Detail, podemos
implementar a abertura de uma nova linha no Grid atravs do
comando Append.
procedure TfrmPedido.btnDetailAppendClick(Sender: TObject);
begin
DSDetail.DataSet.Append;
end;

Alm disso, devemos implementar o evento OnStateChange do


DataSource Detail para podermos notificar a Master de qualquer
alterao feita na Detail:
procedure TfrmPedido.DSDetailStateChange(Sender: TObject);
begin
If DSDetail.State in [dsInsert,dsEdit] then
DSMain.DataSet.Edit;
end;

O boto de Delete pode ser implementado da seguinte forma:


procedure TfrmPedido.btnDetailDeleteClick(Sender: TObject);
begin
If TBDEDataSet(DSDetail.DataSet).UpdateStatus <> usDeleted then
begin
DSMain.DataSet.Edit;
DSDetail.DataSet.Delete;
end;
end;

A inteno do boto btnDetailDelete simplesmente marcar a linha


para excluso. A excluso no banco de dados s ser feita quando o
usurio mandar salvar todas as alteraes.
A propriedade UpdateStatus indica o estado da linha no cached
updates. Os estados podem ser: no modificada, modificada,
inserida ou alterada. No evento acima estamos verificando se a linha
j no est marcada para excluso, para evitarmos de tentar excluir a
linha novamente.
Delphi Client/Server

130

Para sensibilizar a Master da alterao feita na Detail, podemos


chamar o mtodo Edit do DataSet. Desta forma os botes de
salvamento e cancelamento associados a Master sero habilitados.
E por ltimo devemos chamar o mtodo Delete do DataSet para
marcar a linha para excluso mandando-a para o cached updates.
O boto btnDetailDiscard serve para descartar as alteraes feitas
em uma determinada linha. Se as alteraes ainda no foram
enviadas para o cached updates, a operao de Discard
simplesmente cancela as alteraes atravs do mtodo Cancel. Mas,
se a linha j foi enviada, esta dever ser revertida a seu estado
original atravs do mtodo RevertRecord, sendo portanto retirada
do cached updates.
procedure TfrmPedido.btnDetailDiscardClick(Sender: TObject);
begin
If DSDetail.State in [dsEdit,dsInsert] then begin
DSDetail.DataSet.Cancel
end else begin
TBDEDataSet(DSDetail.DataSet).RevertRecord;
end;
end;

Podemos fornecer um efeito visual para possibilitar o usurio


identificar as linhas alteradas, excludas e inseridas. Para fazer isso,
podemos implementar o evento OnDrawColumnCell do Grid e
alterar as cores do fonte de cada uma dessas situaes. Para isso
podemos utilizar a propriedade UpdateStatus do DataSet.
procedure TfrmPedido.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
var OldColor:Tcolor;
begin
with TDBGrid(Sender) do
//Atribui cores diferentes para cada linha da tabela de acordo com seu
status
case TQuery(DSDetail.DataSet).UpdateStatus of
usDeleted:
begin
OldColor:=Canvas.Font.Color;
Canvas.Font.Color:=clred;
Canvas.Font.Style:=Canvas.Font.Style + [fsStrikeOut];
DefaultDrawColumnCell(Rect,DataCol,Column,State);
Canvas.Font.Color:=OldColor;
Canvas.Font.Style:=Canvas.Font.Style - [fsStrikeOut];
end;
usModified:
begin
OldColor:=Canvas.Font.Color;
Canvas.Font.Color:=clBlue;
DefaultDrawColumnCell(Rect,DataCol,Column,State);
Canvas.Font.Color:=OldColor;
end;
Delphi Client/Server

131

usInserted:
begin
OldColor:=Canvas.Font.Color;
Canvas.Font.Color:=clGreen;
DefaultDrawColumnCell(Rect,DataCol,Column,State);
Canvas.Font.Color:=OldColor;
end;
end;
end;

Antes de implementarmos os eventos da Master, vamos criar um


procedimento que nos permita pesquisar os registro da Detail de
acordo com o registro selecionado na Master.
procedure TfrmPedido.DetailSearch;
begin
DSDetail.DataSet.close;
If DSMain.DataSet.FieldByName('PED_CD').IsNull then
TQuery(DSDetail.DataSet).Params[0].clear
else
TQuery(DSDetail.DataSet).Params[0].value:=
DSMain.DataSet['PED_CD'];
DSDetail.DataSet.Open;
TBDEDataSet(DSDetail.DataSet).UpdateRecordTypes:=
TBDEDataSet(DSDetail.DataSet).UpdateRecordTypes + [rtDeleted];
end;

A ltima linha do cdigo permite que os registros excludos


continuem visveis no DataSet para que possam ser revertidos
conforme a necessidade do usurio.
Podemos comear a implementao dos eventos da Master atravs
do boto btnAppend.
procedure TfrmPedido.btnAppendClick(Sender: TObject);
begin
If Not DSMain.DataSet.Active then begin
DSMain.DataSet.Open;
end;
DSMain.DataSet.Append;
DetailSearch;
end;

Esse evento verifica se o DataSet Master est aberto e a seguir


insere uma nova linha atravs do comando Append. Depois feita
uma pesquisa na tabela Detail com o parmetro nulo para que
nenhum registro seja encontrado e permita assim a insero de
novos registros no Grid de Itens.
A pesquisa da Master realizada pelo boto btnSearch pode ser
implementada da seguinte forma
procedure TfrmPedido.btnSearchClick(Sender: TObject);
begin
DSMain.DataSet.Close;
frmConsPedido:=TfrmConsPedido.Create(Self);
Delphi Client/Server

132

If (frmConsPedido.ShowModal=mrOk)
and (Not (frmConsPedido.DSMain.DataSet['PED_CD'] = Null)) then
begin
TQuery(DSMain.DataSet).Params[0].value:=
frmConsPedido.DSMain.DataSet['PED_CD'];
DSMain.DataSet.Open;
DetailSearch;
end;
frmConsPedido.free;
end;

O boto btnDiscard responsvel pelo cancelamento das alteraes


da Master. Apesar da Master estar tratando uma nica linha de cada
vez, no suficiente verificarmos o estado do DataSource para
sabermos se a linha possui alteraes. Quando uma tentativa no
bem sucedida de salvar o registro acontece, o registro foi colocado
no cached updates, portanto seu estado ser dsBrowse. Por isso
necessrio verificarmos a propriedade UpdateStatus para
sabermos como descartar as alteraes.
procedure TfrmPedido.btnDiscardClick(Sender: TObject);
begin
If TBDEDataSet(DSMain.DataSet).UpdateStatus in [usModified,
usInserted, usDeleted] then
TBDEDataSet(DSMain.DataSet).RevertRecord
else
TBDEDataSet(DSMain.DataSet).Cancel;
If TBDEDataSet(DSMain.DataSet).IsEmpty then begin
DSMain.DataSet.close;
DSDetail.DataSet.Close;
end else begin
DetailSearch;
end;
EnableBtnControls;
end;

Controle visual da Transao


O evento de salvamento feito pelo boto btnSave pode ser
implementado da seguinte forma para manter em uma nica
transao a insero do pedido e de seus itens.
procedure TfrmPedido.btnSaveClick(Sender: TObject);
begin
If DSDetail.DataSet.State in [dsInsert,dsEdit] then
DSDetail.DataSet.Post;
If DSMain.DataSet.State in [dsInsert,dsEdit] then
DSMain.DataSet.Post;
DMSistVendas.Database1.StartTransaction;
try
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
DMSistVendas.Database1.Commit;
EnableBtnControls;
DetailSearch;
except
On E:Exception do begin
TBDEDataSet(DSMain.DataSet).CommitUpdates;
DMSistVendas.Database1.Rollback;
Delphi Client/Server

133

DSMain.DataSet.Edit;
if Not ((E is EDBEngineError) and
(EDBEngineError(E).Errors[0].NativeError = 0)) then
raise;
end;
end;
end;

Desta forma se algum problema ocorrer na insero do pedido ou de


seus itens executado um rollback no banco de dados para desfazer
toda a transao e a tela fica intacta para que o usurio possa
corrigir o problema e refazer a gravao.
A primeira parte do cdigo cuida de enviar para o cached update
qualquer linha que esteja pendente na Detail ou na Master. A linha
da Detail enviada primeiro, porque ela pode causar modificaes no
registro corrente da Master.
If DSDetail.DataSet.State in [dsInsert,dsEdit] then
DSDetail.DataSet.Post;
If DSMain.DataSet.State in [dsInsert,dsEdit] then
DSMain.DataSet.Post;

A seguir comea a transao e toda a operao de salvamento feita


dentro do comando try..except para que a finalizao da transao
possa ser controlada, tanto no caso de sucesso como no caso de
ocorrer alguma falha. O salvamento feito atravs do comando
ApplyUpdates, responsvel por enviar as pendncias do cached
update para o banco. Aps o envio com sucesso, necessrio
executar o comando CommitUpdates, responsvel em retirar do
cached update as linhas salvas com sucesso. Entretanto, para que o
Delphi atualize alguns flags, esse comando necessrio mesmo se a
atualizao falhar.
Se essas operaes ocorrerem com sucesso, executado o comando
commit no banco de dados e todo o conjunto de atualizaes
finalmente aplicado no banco, caso contrrio toda a transao
desfeita atravs do comando rollback.
O cdigo ainda apresenta alguns detalhes como: chamar o controle
de habilitao dos botes e fazer a busca novamente dos dados da
Detail para que os registros possam ser atualizados na tela.
Transao - Lgica de negcio
Alguns eventos devem ser implementados no DataModule para que o
controle da transao Master/Detail seja feito por completo.
Vamos comear pelo evento que deve ser utilizado para fornecer
valores default para os campos, quando o usurio criar um novo
registro:
procedure TDMSistVendas.QPedidoNewRecord(DataSet: TDataSet);
Delphi Client/Server

134

begin
DataSet['PED_VALOR']:=0;
DataSet['PED_TIPO']:='V';
end;

Agora vamos implementar o evento UpdateRecord do DataSet


QPedido.
procedure TDMSistVendas.QPedidoUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
USPedido.Apply(UpdateKind);
try
QItem.ApplyUpdates;
finally
QItem.CommitUpdates;
end;
UpdateAction:=uaApplied;
end;

Esse evento simplesmente cuida de executar o comando de


atualizao atravs do componente UpdateSQL e fazer a chamada
para a efetivao do cached update da Detail. Desta forma se
algum erro acontecer na Master ou na Detail, o valor do flag
retornado atravs do parmetro UpdateAction uaAbort, o que
faz a linha da Master continuar no cache para novas tentativas de
atualizao pelo usurio. Quando se retorna o valor uaApplied no
parmetro UpdateAction, o Delphi reverte o status da linha para
no modificada como se ela tivesse sido aplicada no banco. Isso s
pode ser feito se toda a transao foi realizada com sucesso e
tivermos certeza que o comando commit ser executado.
Devemos tambm implementar o evento da Detail.
procedure TDMSistVendas.QItemUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
USItem.Apply(UpdateKind);
UpdateAction:=uaSkip;
end;

Para evitarmos que os flags dos registros sejam revertidos para no


modificado, foi retornado atravs do parmetro UpdateAction o
valor uaSkip que no produz nenhuma exceo e no modifica o
flag dos registros. Quando a transao finalizada com sucesso,
feita uma nova pesquisa no prprio evento do boto btnSave para
que todos os flags sejam atualizados.
Desta forma conseguimos manter a integridade da transao e
permitir a correo dos registros pelo usurio para que possam ser
reenviados para o banco de dados.

Delphi Client/Server

135

Calculando o Valor Total do Pedido


Voltando a tela de pedido, podemos calcular o valor do pedido
sempre que houver uma atualizao no valor do item. O valor do item
tambm funo de dois outros valores: o preo do produto e a
quantidade do item.
Vamos utilizar o evento OnDataChange do DataSource Detail para
fazermos os clculos.
procedure TfrmPedido.DSDetailDataChange(Sender: TObject; Field: TField);
begin
If (Field = DSDetail.DataSet.FieldByName('PRO_CD')) or
(Field = DSDetail.DataSet.FieldByName('ITE_QUANT')) then begin
If DSMain.State = dsBrowse then DSMain.DataSet.Edit;
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloatDSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
DSDetail.DataSet['ITE_VALOR']:=
DSDetail.DataSet['PRO_PRECO'] * DSDetail.DataSet['ITE_QUANT'];
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat +
DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
end;
end;

Alm disso devemos recalcular o valor total em vrias situaes de


alterao que podem acontecer na Detail. Por isso, iremos adicionar
algumas linhas de cdigo em alguns dos eventos j implementados
anteriormente.
O primeiro o evento do boto btnDetailDelete que deve subtrair o
valor do item do total do pedido.
procedure TfrmPedido.btnDetailDeleteClick(Sender: TObject);
begin
If TBDEDataSet(DSDetail.DataSet).UpdateStatus <> usDeleted then begin
DSMain.DataSet.Edit;
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
DSDetail.DataSet.Delete;
end;
end;

Alm disso, devemos tambm recalcular o valor do pedido no evento


do boto btnDetailDiscard.
procedure TfrmPedido.btnDetailDiscardClick(Sender: TObject);
begin
If DSDetail.State in [dsEdit,dsInsert] then begin
If DSDetail.State = dsInsert then begin
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
DSDetail.DataSet.Cancel;
end else begin
Delphi Client/Server

136

DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
DSDetail.DataSet.Cancel;
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat +
DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
end;
end else begin
if TBDEDataSet(DSDetail.DataSet).UpdateStatus = usInserted then begin
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
TBDEDataSet(DSDetail.DataSet).RevertRecord;
end else if TBDEDataSet(DSDetail.DataSet).UpdateStatus = usDeleted
then begin
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat +
DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
TBDEDataSet(DSDetail.DataSet).RevertRecord;
end else if TBDEDataSet(DSDetail.DataSet).UpdateStatus = usModified
then begin
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
TBDEDataSet(DSDetail.DataSet).RevertRecord;
DSMain.DataSet.FieldByName('PED_VALOR').AsFloat:=
DSMain.DataSet.FieldByName('PED_VALOR').asfloat +
DSDetail.DataSet.FieldByName('ITE_VALOR').asfloat;
end;
end;
end;

Regras de Negcio
Alm de inserir os registros do pedido e de seus itens, devemos
implementar algumas regras de negcio em torno dessas inseres.
Como exemplo iremos implementar duas regras de negcio: a
verificao do limite de crdito do cliente em compras a prazo e a
baixa do estoque do produto.
Essas regras de negcio devem ser implementadas dentro da
transao para que sejam efetivadas juntamente com a insero dos
registros. Para simplificar o processo iremos fazer os eventos apenas
para insero do pedido e dos itens. No iremos tratar portanto a
atualizao e excluso de itens ou do pedido.
Vamos implementar duas procedures dentro do DataModule:
public
{ Public declarations }
procedure VerificaLimiteCreditoCliente(Cliente: integer; Valor: double);
procedure UpdateEstoque(Produto: integer; Quant: integer);
end;

procedure TDMSistVendas.VerificaLimiteCreditoCliente(Cliente: integer; Valor:


double);
begin
QGeral.close;
Delphi Client/Server

137

QGeral.SQL.Text:='update cliente set cli_debito = cli_debito + :p1 ' +


'where pes_cd = :p2';
QGeral.ParamByName('p1').AsFloat:=Valor;
QGeral.ParamByName('p2').AsInteger:=Cliente;
QGeral.ExecSQL;
QGeral.SQL.Text:='select cli_limitecredito - cli_debito from cliente ' +
'where pes_cd = :p1';
QGeral.ParamByName('p1').AsInteger:=Cliente;
QGeral.Open;
if QGeral.Fields[0].AsFloat < 0 then begin
ShowMessage('Limite de Crdito do Cliente insuficiente para a compra');
QGeral.Close;
Abort;
end;
QGeral.Close;
end;

Esse mtodo incrementa o dbito do cliente e verifica se ele


ultrapassou o limite. Caso isso acontea uma mensagem mostrada
ao usurio e uma exceo levantada. A alterao deve ser feita
primeiro para que o registro se mantenha travado e outro usurio
no altere seus valores.
O evento de baixa no estoque bem similar e mostrado abaixo.
procedure TDMSistVendas.UpdateEstoque(Produto: integer; Quant: integer);
begin
QGeral.close;
QGeral.SQL.Text:='update produto set pro_estoque = pro_estoque - :p1 ' +
'where pro_cd = :p2';
QGeral.ParamByName('p1').AsFloat:=Quant;
QGeral.ParamByName('p2').AsInteger:=Produto;
QGeral.ExecSQL;
QGeral.SQL.Text:='select pro_estoque from produto ' +
'where pro_cd = :p1';
QGeral.ParamByName('p1').AsInteger:=Produto;
QGeral.Open;
if QGeral.Fields[0].AsFloat < 0 then begin
ShowMessage('Estoque insuficiente do Produto: ' + inttostr(Produto));
QGeral.Close;
Abort;
end;
QGeral.Close;
end;

Devemos ento, acrescentar as chamadas desses mtodos nos


eventos de atualizao:
procedure TDMSistVendas.QPedidoUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
If UpdateKind = ukInsert then begin
If QPedidoPED_TIPO.NewValue = 'P' then
VerificaLimiteCreditoCliente(QPedidoPES_CD_CLI.NewValue,
QPedidoPED_VALOR.NewValue);
end;
USPedido.Apply(UpdateKind);
try
QItem.ApplyUpdates;
finally
Delphi Client/Server

138

QItem.CommitUpdates;
end;
UpdateAction:=uaApplied;
end;
procedure TDMSistVendas.QItemUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
If UpdateKind = ukInsert then begin
QItemPED_CD.NewValue:=QPedidoPED_CD.NewValue;
UpdateEstoque(QItemPRO_CD.NewValue, QItemITE_QUANT.NewValue );
end;
USItem.Apply(UpdateKind);
UpdateAction:=uaSkip;
end;

Delphi Client/Server

139

You might also like