Você está na página 1de 132

A P O S T I L A E D I T A D A P O R : I S A I A S O L I V E I R A V I S I T E O S I T E : H T T P : / / W W W . D E L P H I V I S A O . B L O G S P O T . C O M O U M M A N D E E M A I L : C O M F E V E N C E R E M O S @ G M A I L .

C O M

APOSTILA COMPLETA DE DELPHI 7


CLIENTE/SERVIDOR

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

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

Rede
Data WareHousing

Dados

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

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

Servidor de Banco de Dados (DBMS)

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 8

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

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

1
Ana Maria 10

2
Maria Jos 15

20/10/96 15,00
999.876.555-22

Caneta

Lpis

21/10/96 65,00
111.111.111-22

Caneta

Caderno

10,00 5

5,00

15,00

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

Ana M aria
9 9 9 .8 7 6 .5 5 5 -2 2

Lpis

C an eta

C ad ern o

M aria Jos
1 1 1 .1 1 1 .1 1 1 -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

D E

B A N C O

D E

D A D O S

Pedido
NumPed 1 2 3 Data 20/10/96 21/10/96 22/10/96 Valor 15,00 65,00 25,00 Cliente 1 2 2

Itens
NumPed 1 1 2 2 3 3 Produto Caneta Lpis Caneta Caderno Lpis Caderno Quantid. Valor 10 5 15 5 15 1 10,00 5,00 15,00 50,00 15,00 10,00

Cliente
Codigo 1 2 Nome Ana Maria Lima Maria Jos CPF
999.876.555-22 111.111.111-22

Fig. 2.5: Banco de Dados Relacional.

Delphi Client/Server

14

Captulo

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.

C C C

D D D

Chave

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


Fig. 3.7. Exemplo na segunda forma normal.

*CodProduto NomeProduto PreoProduto

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 ItemPedido *NumPed *CodProduto Quantidade ValorItem
Fig. 3.10: Exemplo na terceira forma normal.

Cliente *CodCliente NomeCliente CPFCliente Produto *CodProduto NomeProduto PreoProduto

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 PESSOA *PES_CD PES_NM PES_TP PES_CGCCPF PES_LOGRADOURO PES_NUMERO PES_COMPLEMENTO PES_BAIRRO CID_CD UF_SG VENDEDOR *PES_CD VEN_VENDAS VEN_PERCCOMISSAO VEN_COMISSAO PRODUTO *PRO_CD PRO_NM PRO_PRECO PRO_ESTOQUE

CIDADE *CID_CD *UF_SG CID_NM CID_NUMHABITANTES

CLIENTE *PES_CD CLI_LIMITECREDITO CLI_DEBITO

PEDIDO *PED_CD PED_DT PED_VALOR PED_TIPO PES_CD_CLI PES_CD_VEN

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 A *c d e . .
Fig 3.19: Integridade Referencial.

Tabela B *a b c

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

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

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
Aplicaes Ferramentas Desenvolvimento Database DatabaseEnginer Enginer NetBios NetBios

MIDDLEWARE
Servios Especficos
SQL/API SQL/API

SERVIDOR SERVIDOR

Banco Bancode deDados Dados

Transporte
TCP/IP TCP/IP IPX/SPX IPX/SPX SNA SNA

SOR
Serv. Serv.Diretrio Diretrio
Sistema Operacional

Serv. Serv.Diretrio Diretrio Segurana Segurana


Sistema Operacional

RPC RPC

Mensagens Mensagens

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 Update

Banco

Comando Select

Cursor Result Set . . . .

Fig 5.6: Cursores e Result Sets Delphi Client/Server 45

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


Projeto Exemplo

- Inicia a transao. - Efetiva as alteraes feitas desde o incio da transao - Cancela as alteraes feitas desde o incio da transao

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

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

DRIVER FLAGS 0 512 4096 4068

Isolation level and commit type Read committed, hard commit Repeatable read, hard commit Read committed, soft commit Repeatable read, soft commit

Para cada servidor de banco de dados existe uma configurao diferente. Para o Oracle, por exemplo, nada precisa ser feito porque este o comportamento padro.

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

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

Nveis de isolamento Podemos definir alguns nveis de isolamento, de acordo com a forma que os shared locks so atribudos aos registros ou pginas. A seguir sero mostradas as vrias formas de se trabalhar com locks no banco de dados, independente dos nomes utilizados pelos vrios fornecedores. Sendo possvel que alguns bancos de dados no apresentem todas as possibilidades descritas abaixo.
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. 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 Where Key

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


Tabela de Propriedades.

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

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

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

Delphi Client/Server

60

Captulo

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 GERENCIAMENTO de DADOS SERVIDOR


Fig 6.1: Camadas Fsicas e Lgicas de uma Aplicao.

LGICO

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

Construindo Aplicaes Cliente/Servidor


Esse captulo mostra a construo de uma tela de manuteno utilizandose 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

A P L I C A E S

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

Componente DataModule1
Tabela de Propriedades.

Propriedade Name

Valor DMSistVendas

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 Request Live Active

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

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


Tabela de Propriedades.

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

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


Tabela de Propriedades

Propriedade Name Name Name Name

Valor btnAppend btnDelete btnSave btnDiscard

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


Tabela de Propriedades

Propriedade Name Request Live UpdateObject

Valor USProduto False USProduto

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

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

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

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


Tabela de Propriedades

Propriedade Name Name

Valor btnNewForSearch btnSearch

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
Tabela de Propriedades

Propriedade Active SQL

Valor False select * from Produto

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

F I L T R A N D O

R E G I S T R O S

end; end;

Antes ainda de executar, vamos ajustar a abertura dos forms no Project/Options. Deve-se alterar o Main Form novamente para frmProduto e arrastar o DMsistVendas para o topo da lista dos Auto-Create para que ele seja o primeiro a ser criado.

Fig 8.2: Project/Options.

Controlando os Estados da Tela


Para finalizar a tela de produtos, deve-se utilizar os estados da tela para controlar a habilitao dos botes de edio. O componente 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

dsBrowse

dsNewFor Search

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
92

Valor QConsCliente

F I L T R A N D O

R E G I S T R O S

DatabaseName SQL

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

Table1

Query2

Name DatabaseName TableName Name DatabaseName SQL Params

TConsCliUF DBVENDAS UF QConsCliCidade DBVENDAS


SELECT * FROM CIDADE WHERE UF_SG = :UF_SG

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


93

Valor frmConsCliente Clientes alTop Cdigo Nome

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
Tabela de Propriedades

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

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

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

Delphi Client/Server

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
Tabela de Propriedades

Params Name TableName

PES_CD | Integer USPessoa PESSOA

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


Delphi Client/Server

Propriedade Name Caption Align Name Name Name Name Name


100

Valor frmCliente Clientes alTop btnAppend btnDelete btnSave btnDiscard btnSearch

F I L T R A N D O

R E G I S T R O S

PageControl1 TabSheet1 TabSheet2 TabSheet3 DataSource1 DBControls


Tabela de Propriedades

Align Caption Caption Caption Name DataSet .....

alClient Informaes Gerais Pessoa Fsica Crditos DSMain DMSistVendas.Qcliente .....

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

F I L T R A N D O

R E G I S T R O S

frmConsCliente.free; end;

Podemos utilizar a prpria mquina de estado do DataSource fornecida pelo Delphi para controlar a habilitao dos botes:
procedure TfrmCliente.DSMainStateChange(Sender: TObject); begin Case DSMain.State of dsInactive: begin btnAppend.Enabled:=TRUE; btnDelete.Enabled:=FALSE; btnSave.Enabled:=FALSE; btnDiscard.Enabled:=FALSE; btnSearch.Enabled:=TRUE; end; dsInsert: begin btnAppend.Enabled:=FALSE; btnDelete.Enabled:=FALSE; btnSave.Enabled:=TRUE; btnDiscard.Enabled:=TRUE; 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;
Delphi Client/Server 102

F I L T R A N D O

R E G I S T R O S

Ao se escolher um registro, deve-se atualizar o cdigo relacionado na tabela principal; Quando for selecionado um registro na tabela principal, deve-se selecionar o registro correspondente na tabela secundria, permitindo a exibio de alguns campos como a descrio.
Campo LookUp do Delphi

O Delphi permite a criao de campos lookups dentro de um dataset para apresentar uma lista com os registros de uma outra tabela relacionada. Esse recurso implementa as trs fases discutidas acima da seguinte forma: Fase 1. A lista preenchida atravs de um DataSet que seleciona os registros da tabela relacionada. utilizado componentes combo box ou list box para apresentar a lista. 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;
Delphi Client/Server 107

F I L T R A N D O

R E G I S T R O S

btnDelete.Enabled:=FALSE; btnSave.Enabled:=TRUE; btnDiscard.Enabled:=TRUE; btnSearch.Enabled:=FALSE; end; dsBrowse: If TQuery(DSMain.DataSet).UpdatesPending then begin btnAppend.Enabled:=FALSE; btnDelete.Enabled:=FALSE; btnSave.Enabled:=TRUE; btnDiscard.Enabled:=TRUE; btnSearch.Enabled:=FALSE; end else begin btnAppend.Enabled:=TRUE; btnDelete.Enabled:=TRUE; btnSave.Enabled:=FALSE; btnDiscard.Enabled:=FALSE; btnSearch.Enabled:=TRUE; end; end; end;

Agora podemos modificar os eventos de gravao e excluso:


procedure TfrmCliente.btnDeleteClick(Sender: TObject); begin DMSistVendas.Database1.StartTransaction; try DSMain.DataSet.Delete; TBDEDataSet(DSMain.DataSet).ApplyUpdates; DMSistVendas.Database1.Commit; TBDEDataSet(DSMain.DataSet).CommitUpdates; DSMain.DataSet.close; except DMSistVendas.Database1.Rollback; TBDEDataSet(DSMain.DataSet).CancelUpdates; end; 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.

Delphi Client/Server

108

F I L T R A N D O

R E G I S T R O S

Para fazer isso devemos alterar o cdigo da tela de consulta para que ela no feche o DataSet quando o form for destrudo. Alm disso algumas outras alteraes devem ser feitas. Devemos alterar os seguintes eventos do form:
procedure TfrmConsCliente.FormCreate(Sender: TObject); begin // CmdSelect:=TQuery(DSMain.DataSet).SQL.TExt; CmdSelect:=DMSistVendas.CmdSelectQCliente; If Not DSUF.DataSet.Active then DSUF.DataSet.Open; end; procedure TfrmConsCliente.FormDestroy(Sender: TObject); begin //DSCidade.DataSet.close; //TQuery(DSMain.DataSet).SQL.TExt:=CmdSelect; end;

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

procedure TDMSistVendas.DMSistVendasCreate(Sender: TObject); begin 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.

Delphi Client/Server

109

F I L T R A N D O

R E G I S T R O S

Tentar utilizar na tela de consulta um componente TTable ao invs do componente TQuery. Como foi visto o componente TTable possui um comportamento mais inteligente no caso do fechamento da transao, j que permite que o cursor seja fechado e quando houver a necessidade de caminhar por linhas ainda no trazidas dispara um novo comando select. Entretanto, esse componente costuma abrir uma transao e pode estar mantendo algum registro travado, dependendo do banco de dados utilizado.

Delphi Client/Server

110

Captulo

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

Propriedade 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

Query2

Name DatabaseName SQL

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
Tabela de Propriedades

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

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

Propriedade 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

Query2

UpdateObject CachedUpdate Name DatabaseName SQL

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

UpdateSQL1 UpdateSQL2 Query3

UpdateObject CachedUpdate Name Name Name DatabaseName SQL

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

Valor frmPedido Pedidos DSMain DMSistVendas.Qpedido DSDetail DMSistVendas.QItem DSCliente DMSistVendas.QConsPedCliente alTop btnAppend btnDelete btnSave btnDiscard btnSearch DSMain 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