Você está na página 1de 133

Delphi

Autor:
Thiago Sousa Santos

DELPHI

Delphi

ndice Analtico
INTRODUO AO MODELO CLIENTE/SERVIDOR ................................................................1
MUDANAS DE PARADIGMAS................................................................................................................1
Paradigma Computacional...............................................................................................................1
Paradigma do Negcio .....................................................................................................................3
EVOLUO DA ARQUITETURA ..............................................................................................................4
Arquitetura Time-Sharing.................................................................................................................4
Arquitetura Resource-Sharing ..........................................................................................................5
Arquitetura Cliente/Servidor ............................................................................................................6
PRIMEIRA GERAO CLIENTE/SERVIDOR..............................................................................................8
SEGUNDA GERAO CLIENTE/SERVIDOR..............................................................................................8
SGDB - SISTEMAS GERENCIADORES DE BANCO DE DADOS ...........................................10
MODELOS DE BANCO DE DADOS.........................................................................................................10
Sistema de Gerenciamento de Arquivos..........................................................................................11
Banco de Dados Hierrquico .........................................................................................................11
Banco de Dados de Rede ................................................................................................................12
Banco de Dados Relacional............................................................................................................13
BANCOS DE DADOS RELACIONAIS..........................................................................................15
CLASSIFICAO ..................................................................................................................................15
Corporativos ...................................................................................................................................16
Departamentais...............................................................................................................................16
Locais ou Mveis ............................................................................................................................16
MODELAGEM DE DADOS .....................................................................................................................17
Normalizao..................................................................................................................................17
Propagao de chaves primrias ...................................................................................................22
Ferramentas....................................................................................................................................24
Criao da Base de Dados .............................................................................................................24
Utilizando Interbase Windows ISQL...............................................................................................24
LINGUAGEM SQL................................................................................................................................26
Categorias da Linguagem SQL.......................................................................................................27
Utilizando o Windows ISQL para definir o Banco de Dados .........................................................27
Utilizando o Windows ISQL para acessar o Banco de Dados........................................................27
CONSISTNCIA E INTEGRIDADE DOS DADOS ........................................................................................29
Integridade Referencial ..................................................................................................................29
Domnio dos dados .........................................................................................................................30
Regras de Negcio ..........................................................................................................................30

Utilizando o Windows ISQL para definir integridades e consistncias..........................................31


Utilizando o Windows ISQL para testar as consistncias. .............................................................31
Distribuio da Consistncia e Integridade dos Dados .................................................................33
SQL EXPLORER .............................................................................................................................34
CRIAO DE ALIAS .............................................................................................................................34
VISUALIZAO E EDIO DE DADOS ..................................................................................................35
DEFINIO DE NOVOS ELEMENTOS......................................................................................................37
DEFINIO DE DICIONRIOS DE DADOS...............................................................................................37
Criao de um novo Dicionrio .....................................................................................................37
Importao das definies do Banco de Dados..............................................................................38
Definio das propriedades dos Attribute Sets...............................................................................38
Utilizao do Dicionrio no Delphi ...............................................................................................39
TRABALHANDO COM BANCOS DE DADOS RELACIONAIS...............................................41
COMPONENTES DA ARQUITETURA CLIENTE/SERVIDOR .......................................................................41
CONEXES E CONTEXTOS ...................................................................................................................42
Conexes e Contextos no Delphi ....................................................................................................43
CURSORES E RESULT SETS ..................................................................................................................45
Cursores e Result Sets no Delphi....................................................................................................46
TRANSAES ......................................................................................................................................46
Transaes no Delphi .....................................................................................................................47
CONCORRNCIA ..................................................................................................................................51
Tipos de travamentos (locks) ..........................................................................................................51
Nveis de isolamento .......................................................................................................................52
Optimistic Lock ...............................................................................................................................53
Concorrncia no Delphi .................................................................................................................54
PROJETANDO APLICAES CLIENTE/SERVIDOR .............................................................61
ESTRUTURA DE UMA APLICAO ........................................................................................................61
Apresentao ..................................................................................................................................61
Lgica do Negcio ..........................................................................................................................61
Gerenciamento de Dados................................................................................................................62
VANTAGENS DA ORGANIZAO DA APLICAO EM CAMADAS ...........................................................62
ESTRUTURA DE UMA APLICAO DELPHI ...........................................................................................63
Componentes visuais.......................................................................................................................63
Componentes de Acesso base de dados .......................................................................................64
Componente de ligao ..................................................................................................................64
CONSTRUINDO APLICAES CLIENTE/SERVIDOR ...........................................................65
UTILIZANDO DATA MODULES .............................................................................................................65
COMPONENTE TDATABASE.................................................................................................................66
ESCOLHENDO ENTRE TTABLE E TQUERY ...........................................................................................67
Abertura..........................................................................................................................................67
Filtros .............................................................................................................................................67
Transaes......................................................................................................................................68
Nmero de Tabelas Acessadas........................................................................................................68
TRABALHANDO COM O TQUERY .........................................................................................................69
UTILIZANDO CACHED UPDATES ..........................................................................................................70
UTILIZANDO O COMPONENTE TUPDATESQL ......................................................................................72
GRAVAO LINHA A LINHA OU EM BATCH .........................................................................................74
TRABALHANDO COM O TTABLE ..........................................................................................................78
FILTRANDO REGISTROS.............................................................................................................81
QBE NA MESMA TELA DE MANUTENO ...........................................................................................82
CONTROLANDO OS ESTADOS DA TELA ................................................................................................85
TELA DE CONSULTA ESPECFICA .........................................................................................................92
Tela de Consulta .............................................................................................................................92

Tela de Manuteno .......................................................................................................................98


Recursos de LookUp .....................................................................................................................102
Buscando Registros de outras TabSheets .....................................................................................105
Controlando Transaes...............................................................................................................106
Mantendo o Result Set da Consulta ..............................................................................................108
CONTROLANDO TRANSAES MASTER/DETAIL ............................................................111
TELA DE CONSULTA ..........................................................................................................................111
TELA DE MANUTENO ....................................................................................................................116
Lgica visual.................................................................................................................................119
Controle visual da Transao.......................................................................................................123
Transao - Lgica de negcio ....................................................................................................124
Calculando o Valor Total do Pedido ............................................................................................125
Regras de Negcio ........................................................................................................................126
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

Captulo

1
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.

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 disponibilizlos 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
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.

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

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 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.

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

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:
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

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

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.

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
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

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

Captulo

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

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

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

D A D O S

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 | ...
Fig. 2.2: Sistema de Gerenciamento de Arquivos.

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
Delphi Client/Server

11

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

D A D O S

modelo hierrquico possvel definir relaes de-um-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, decomp-lo 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

20/10/96 15,00

Ana
Maria

Caneta

10

10,00 5

999.876.555-22

Lpis

21/10/96 65,00
111.111.111-22

5,00

3
Maria
Jos
15

Caneta

15,00

Caderno

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

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

D A D O S

R aiz

Ana
M aria

L pis

C an e ta

C ad ern o

M aria
Jos
11 1 .1 1 1 .1 1 1-22

9 9 9.8 76 .55 5 -2 2

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

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

Pedido
NumPed
1
2
3

1
2

B A N C O

D E

D A D O S

Itens
Data

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

Valor

Cliente

15,00
65,00
25,00

1
2
2

Cliente
Codigo

D E

Nome
Ana Maria Lima
Maria Jos

NumPed

Produto

1
1
2
2
3
3

Caneta
Lpis
Caneta
Caderno
Lpis
Caderno

CPF
999.876.555-22
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

Captulo

3
Bancos de Dados
Relacionais
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

B A N C O S

D E

D A D O S

R E L A C I O N A I 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.
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.

Delphi Client/Server

16

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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 e os conseqentes riscos de inconsistncias;
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
Delphi Client/Server

17

B A N C O S

D E

D A D O S

R E L A C I O N A I S

Primeira Forma Normal

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

D
C

D
C

Chave

E
D

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.

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.

Delphi Client/Server

18

B A N C O S

D E

D A D O S

R E L A C I O N A I S

*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.

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.

Delphi Client/Server

19

B A N C O S

D E

D A D O S

R E L A C I O N A I S

*
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 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.

Delphi Client/Server

20

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.

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.

Delphi Client/Server

21

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.

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.
Delphi Client/Server

22

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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 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.

Delphi Client/Server

23

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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 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.

Delphi Client/Server

24

B A N C O S

D E

D A D O S

R E L A C I O N A I S

Fig. 3.15: Interactive SQL program.

Pode-se criar um novo banco de dados atravs da opo File/Create Database do Menu.
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.

Delphi Client/Server

25

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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

VENDEDOR
*PES_CD
VEN_VENDAS
VEN_PERCCOMISSAO
VEN_COMISSAO

PEDIDO
*PED_CD
PED_DT
PED_VALOR
PED_TIPO
PES_CD_CLI
PES_CD_VEN

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
Delphi Client/Server

26

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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

27

B A N C O S

D E

D A D O S

R E L A C I O N A I 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

Para concluir as operaes feitas e registr-las no banco, de modo que outros usurios sejam
capazes de v-las, utiliza-se o comando:
Delphi Client/Server

28

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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 acess-lo 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.

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;
Delphi Client/Server

29

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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. Pode-se 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.
Delphi Client/Server

30

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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));

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.

Delphi Client/Server

31

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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)

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.

Delphi Client/Server

32

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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 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

33

Captulo

4
SQL Explorer
Esse captulo apresenta a ferramenta de definio e manipulao de
dados SQL Explorer da Borland.
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

34

S Q L

E X P L O R E R

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.

Fig 4.3: Visualizao e edio dos dados.

Delphi Client/Server

35

S Q L

E X P L O R E R

Pode-se tambm utilizar comandos SQL par visualizar e editar os dados atravs da ficha
Enter SQL.

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.

Delphi Client/Server

36

S Q L

E X P L O R E R

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 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.
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
Delphi Client/Server

37

S Q L

E X P L O R E R

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

38

S Q L

E X P L O R E R

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

39

S Q L

E X P L O R E R

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

40

Captulo

5
Trabalhando com Bancos de
Dados Relacionais
Esse captulo apresenta a forma de trabalho dos bancos de dados
relacionais.
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

41

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I 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

SERVIDOR
SERVIDOR

SQL/API
SQL/API

Banco
Bancode
deDados
Dados

Transporte

Database
DatabaseEnginer
Enginer
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 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.

Delphi Client/Server

42

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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

43

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I 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

44

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I 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.

Contextos
Aplicao

Conexo
Comando
Insert

Comando
Select

Fig 5.6: Cursores e Result Sets

Delphi Client/Server

Banco

Comando
Update

45

Cursor
Result
Set
.
.
.
.

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.

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
Delphi Client/Server

46

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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, podese disparar um 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, devese 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.

Delphi Client/Server

47

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

StartTransaction

- Inicia a transao.

Commit

- Efetiva as alteraes feitas desde o incio da transao

Rollback

- Cancela as alteraes feitas desde o incio da transao

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.

Delphi Client/Server

48

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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

49

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I 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 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.
Delphi Client/Server

50

T R A B A L H A N D O

DRIVER FLAGS
0
512
4096
4068

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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 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
Delphi Client/Server

51

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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
5

1
Exclusive
Shared

Shared
Shared

Shared

Shared
Exclusive

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.
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.

Delphi Client/Server

52

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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 de dcimos de segundo, porque a unidade segundo no possui preciso
Delphi Client/Server

53

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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

54

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

Fig 5.11: TDatabase.TransIsolation.

Para se configurar o processo de Optimistic Lock no Delphi, deve-se utilizar a


propriedade UpdateMode dos componentes DataSets: TQuery e TTable.

Fig 5.12: TQuery.UpdateMode.


Delphi Client/Server

55

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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

Componente
Form1
Database1

Query1

DataSource1
Grid1
Button1
Button2
Button3

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 a transao executando o mtodo commit


do TDatabase:
procedure TfrmLock.Button2Click(Sender: TObject);
Delphi Client/Server

56

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.

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;

Delphi Client/Server

57

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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 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

Delphi Client/Server

58

T R A B A L H A N D O

C O M

B A N C O S

D E

D A D O S

R E L A C I O N A I S

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.
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

59

T R A B A L H A N D O

Delphi Client/Server

C O M

B A N C O S

D E

D A D O S

60

R E L A C I O N A I S

Captulo

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

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

61

P R O J E T A N D O

A P L I C A E S

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

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.
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
Delphi Client/Server

62

P R O J E T A N D O

A P L I C A E S

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

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 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.

Delphi Client/Server

63

P R O J E T A N D O

A P L I C A E S

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

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

64

C O N S T R U I N D O

A P L I C A E S

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

Captulo

7
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.

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

65

C O N S T R U I N D O

Componente
DataModule1

A P L I C A E S

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

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 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
Delphi Client/Server

66

C O N S T R U I N D O

A P L I C A E S

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

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. 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
Delphi Client/Server

67

C O N S T R U I N D O

A P L I C A E S

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

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.
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
Delphi Client/Server

68

C O N S T R U I N D O

A P L I C A E S

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

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 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

69

C O N S T R U I N D O

A P L I C A E S

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

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.
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
Delphi Client/Server

70

C O N S T R U I N D O

A P L I C A E S

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

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

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;
Delphi Client/Server

71

C O N S T R U I N D O

A P L I C A E S

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

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);
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
Delphi Client/Server

72

C O N S T R U I N D O

A P L I C A E S

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

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.

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
Delphi Client/Server

73

C O N S T R U I N D O

A P L I C A E S

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

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, consegue-se 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.
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 armazenlas 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:

Delphi Client/Server

74

C O N S T R U I N D O

A P L I C A E S

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

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 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;
.
.

Delphi Client/Server

75

C O N S T R U I N D O

A P L I C A E S

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

Depois de criar as procedures, vamos transferir o cdigo contido nos eventos dos botes
para as novas procedures:
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

76

C O N S T R U I N D O

A P L I C A E S

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

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.
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.
Delphi Client/Server

77

C O N S T R U I N D O

A P L I C A E S

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

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.
Vamos implementar ento uma outra tela de cadastro, mas agora utilizando o componente
TTable. Para isso vamos colocar um componente TTable no DataModule DMSistVendas.

Delphi Client/Server

78

C O N S T R U I N D O

A P L I C A E S

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

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
SppedButton1
SppedButton2
SppedButton3
SppedButton4
DBEdit1..DBEdit2

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

Valor
frmUF
UF

DSMain
DMSistVendas.TUF
DSMain
btnAppend
btnDelete
btnSave
btnDiscard
DSMain
UF_SG..UF_NM

Tabela de Propriedades.

Delphi Client/Server

79

C O N S T R U I N D O

A P L I C A E S

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

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

80

Captulo

8
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?
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

81

F I L T R A N D O

R E G I S T R O S

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);
begin
NewForSearch;
end;
procedure TfrmProduto.btnSearchClick(Sender: TObject);
begin
Delphi Client/Server

82

F I L T R A N D O

R E G I S T R O S

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;

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,opSe
arch);

Delphi Client/Server

83

F I L T R A N D O

R E G I S T R O S

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.
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;
Delphi Client/Server

84

F I L T R A N D O

R E G I S T R O S

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 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.

Delphi Client/Server

85

F I L T R A N D O

R E G I S T R O S

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;
function BuildQBE: String;
procedure Save;
procedure NewForInsert;
procedure Delete;
procedure Discard;
procedure NewForSearch;
procedure Search;
Delphi Client/Server

86

F I L T R A N D O

R E G I S T R O S

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,opSe
arch);
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);
procedure btnSearchClick(Sender: TObject);
procedure DSMainStateChange(Sender: TObject);
private
{ Private declarations }
CmdSelect: String;
public
{ Public declarations }
CmdOrderBy: String;
CmdSelectNull: String;
OperState: TOperState;
RecordState: TRecordState;
Delphi Client/Server

87

F I L T R A N D O

R E G I S T R O S

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
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;
Delphi Client/Server

88

F I L T R A N D O

R E G I S T R O S

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);
end else
SetRecordState(rdBrowse);
finally
OperState:=OldOperState;
end;
end;
procedure TfrmProduto.NewForSearch;
var OldOperState: TOPerState;
begin
OldOperState:=OPerState;
OperState:=opNewForSearch;
try
Delphi Client/Server

89

F I L T R A N D O

R E G I S T R O S

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
idYes: Save;
idNo: Discard;
idCancel: Abort;
end;
end;
end;
end;
procedure TfrmProduto.DSMainStateChange(Sender: TObject);
begin
If DSMain.State=dsEdit then
SetRecordState(rdEdit);
end;

Delphi Client/Server

90

F I L T R A N D O

R E G I S T R O S

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
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;
Delphi Client/Server

91

F I L T R A N D O

R E G I S T R O S

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 exibindo-se 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, faa-o informar antes da consulta a cidade da qual os clientes ele deseja. Atravs
dessa tcnica, elimina-se o nmero de tabelas necessrias na 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
Delphi Client/Server

Propriedade
Name

Valor
QConsCliente
92

F I L T R A N D O

R E G I S T R O S

DatabaseName
SQL

Table1

Query2

DBVENDAS
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

Name
DatabaseName
TableName
Name
DatabaseName
SQL

TConsCliUF
DBVENDAS
UF
QConsCliCidade
DBVENDAS
SELECT * FROM CIDADE
WHERE UF_SG = :UF_SG

Params

UF_SG | String

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.

Fig 8.6: Tela de Consulta de Clientes.

Componente
form1
Panel1
Label1
Label2
Delphi Client/Server

Propriedade
Name
Caption
Align
Caption
Caption

Valor
frmConsCliente
Clientes
alTop
Cdigo
Nome
93

F I L T R A N D O

R E G I S T R O S

Label3
Label4
Edit1
Edit2
Edit3
Edit4
Button1
button2
DataSource1
DataSource2
DataSource3
Panel2
DbGrid1
Panel3
Bitbtn1
Bitbtn2

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

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

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:

Delphi Client/Server

94

F I L T R A N D O

R E G I S T R O S

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
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;
Delphi Client/Server

95

F I L T R A N D O

R E G I S T R O S

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);
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:='';
Delphi Client/Server

96

F I L T R A N D O

R E G I S T R O S

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;
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;
Delphi Client/Server

97

F I L T R A N D O

R E G I S T R O S

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.
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

Delphi Client/Server

Propriedade
Name
DatabaseName
CachedUpdate
UpdateObject
SQL

Valor
Qcliente
DBVENDAS
TRUE
USPessoa
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 ,
98

F I L T R A N D O

R E G I S T R O S

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

UpdateSQL1

Params
Name
TableName

PES_CD | Integer
USPessoa
PESSOA

Tabela de Propriedades

Podemos agora, construir a tela de manuteno como mostra as figuras seguintes:

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

Delphi Client/Server

99

F I L T R A N D O

R E G I S T R O S

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

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

Componente
Form1
Panel1
SpeedButton1
SpeedButton2
SpeedButton3
SpeedButton4
SpeedButton5
PageControl1
Delphi Client/Server

Propriedade
Name
Caption
Align
Name
Name
Name
Name
Name
Align

Valor
frmCliente
Clientes
alTop
btnAppend
btnDelete
btnSave
btnDiscard
btnSearch
alClient
100

F I L T R A N D O

R E G I S T R O S

TabSheet1
TabSheet2
TabSheet3
DataSource1
DBControls

Caption
Caption
Caption
Name
DataSet
.....

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;
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;
Delphi Client/Server

101

F I L T R A N D O

R E G I S T R O S

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;
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;
Delphi Client/Server

102

F I L T R A N D O

R E G I S T R O S

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.
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

103

F I L T R A N D O

R E G I S T R O S

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;
end;

Delphi Client/Server

104

F I L T R A N D O

R E G I S T R O S

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'];
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
Delphi Client/Server

105

F I L T R A N D O

R E G I S T R O S

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.

Fig 8.12: Data Module.


Delphi Client/Server

106

F I L T R A N D O

R E G I S T R O S

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 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;
Delphi Client/Server

107

F I L T R A N D O

R E G I S T R O S

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;
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.
Delphi Client/Server

108

F I L T R A N D O

R E G I S T R O S

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
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
Delphi Client/Server

109

F I L T R A N D O

R E G I S T R O S

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

110

Captulo

9
Controlando Transaes
Master/Detail
Esse captulo mostra como implementar telas que exigem um controle de
transao do tipo Master/Detail.
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

111

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

Fig: 9.1: Data Module.

Componente
Query1

Query2

Propriedade
Name
DatabaseName
SQL

Name
DatabaseName
SQL

Valor
QConsPed
DBVENDAS
SELECT PEDIDO.PED_CD , PEDIDO.PED_DT ,
PEDIDO.PED_VALOR , PEDIDO.PED_TIPO
FROM PEDIDO PEDIDO
ORDER BY PEDIDO.PED_CD

QConsPedCliente
DBVENDAS
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

112

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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

113

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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);
procedure edtNomeVendKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
Delphi Client/Server

114

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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
DSMain.DataSet.Close;
TQuery(DSMain.DataSet).SQL.TExt:=CmdSelect;
end;
Delphi Client/Server

115

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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

116

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

Fig. 9.4: Data Module

Componente
Query1

Query2

UpdateSQL1
UpdateSQL2
Query3

Propriedade
Name
DatabaseName
SQL

UpdateObject
CachedUpdate
Name
DatabaseName
SQL

UpdateObject
CachedUpdate
Name
Name
Name
DatabaseName
SQL

Valor
Qpedido
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

USPedido
TRUE
Qitem
DBVENDAS
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

USItem
TRUE
USPedido
USItem
QConsPedProd
DBVENDAS
SELECT * FROM PRODUTO
ORDER BY PRO_NM
Obs: Deixar a segunda linha em branco.

Tabela de Propriedades

Delphi Client/Server

117

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

Deve-se criar tambm, um novo campo do tipo lookup em QPedido para permitir a
escolha do vendedor atravs de seu nome. Iremos 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

118

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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;

O evento OnClose do Form pode ser utilizado para fechar os DataSets que sero abertos
durante o uso da tela.
Delphi Client/Server

119

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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.
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.

Delphi Client/Server

120

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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;
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.
Delphi Client/Server

121

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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);
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.
Delphi Client/Server

122

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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;
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;

Delphi Client/Server

123

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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);
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.
Delphi Client/Server

124

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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.
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;
Delphi Client/Server

125

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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
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:
Delphi Client/Server

126

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

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;
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
Delphi Client/Server

127

C O N T R O L A N D O

T R A N S A E S

M A S T E R / D E T A I L

If QPedidoPED_TIPO.NewValue = 'P' then


VerificaLimiteCreditoCliente(QPedidoPES_CD_CLI.NewValue,
QPedidoPED_VALOR.NewValue);
end;
USPedido.Apply(UpdateKind);
try
QItem.ApplyUpdates;
finally
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

128

Você também pode gostar