Escolar Documentos
Profissional Documentos
Cultura Documentos
Autor:
Thiago Sousa Santos
DELPHI
Delphi
ndice Analtico
INTRODUO AO MODELO CLIENTE/SERVIDOR ................................................................1
MUDANAS DE PARADIGMAS................................................................................................................1
Paradigma Computacional...............................................................................................................1
Paradigma do Negcio .....................................................................................................................3
EVOLUO DA ARQUITETURA ..............................................................................................................4
Arquitetura Time-Sharing.................................................................................................................4
Arquitetura Resource-Sharing ..........................................................................................................5
Arquitetura Cliente/Servidor ............................................................................................................6
PRIMEIRA GERAO CLIENTE/SERVIDOR..............................................................................................8
SEGUNDA GERAO CLIENTE/SERVIDOR..............................................................................................8
SGDB - SISTEMAS GERENCIADORES DE BANCO DE DADOS ...........................................10
MODELOS DE BANCO DE DADOS.........................................................................................................10
Sistema de Gerenciamento de Arquivos..........................................................................................11
Banco de Dados Hierrquico .........................................................................................................11
Banco de Dados de Rede ................................................................................................................12
Banco de Dados Relacional............................................................................................................13
BANCOS DE DADOS RELACIONAIS..........................................................................................15
CLASSIFICAO ..................................................................................................................................15
Corporativos ...................................................................................................................................16
Departamentais...............................................................................................................................16
Locais ou Mveis ............................................................................................................................16
MODELAGEM DE DADOS .....................................................................................................................17
Normalizao..................................................................................................................................17
Propagao de chaves primrias ...................................................................................................22
Ferramentas....................................................................................................................................24
Criao da Base de Dados .............................................................................................................24
Utilizando Interbase Windows ISQL...............................................................................................24
LINGUAGEM SQL................................................................................................................................26
Categorias da Linguagem SQL.......................................................................................................27
Utilizando o Windows ISQL para definir o Banco de Dados .........................................................27
Utilizando o Windows ISQL para acessar o Banco de Dados........................................................27
CONSISTNCIA E INTEGRIDADE DOS DADOS ........................................................................................29
Integridade Referencial ..................................................................................................................29
Domnio dos dados .........................................................................................................................30
Regras de Negcio ..........................................................................................................................30
Captulo
1
Introduo ao Modelo
Cliente/Servidor
Esse captulo mostra a evoluo na arquitetura de computadores e a
tendncia a ambientes de aplicaes cada vez mais distribudos.
Mudanas de Paradigmas
Paradigma Computacional
A viso tradicional da computao era centralizada na figura do computador. que
concentrava todos os servios e recursos fornecidos aos usurios. O acesso a esses
computadores era feito diretamente pelo usurio atravs de teclados e monitores. Em alguns
casos, essa mquina era um equipamento poderoso capaz de atender vrios usurios
simultaneamente (Mainframe). Ou ento, eram pequenos computadores isolados capazes de
atender um nico usurio de cada vez.
Delphi Client/Server
I N T R O D U O
A O
M O D E L O
C L I E N T E / S E R V I D O R
Fig. 1.1: Arquitetura de Mainframe esquerda e um computador PC isolado direita. Ambos simbolizam a viso computacional tradicional,
onde o foco o computador.
Para que essas mquinas pudessem acompanhar a crescente demanda de novos recursos e
servios seria necessrio expandir seus recursos de memria e processamento. No caso de se
utilizar apenas uma mquina servindo vrios usurios (Mainframe), seria difcil expandir
ainda mais seus recursos para suportar as novas demandas tecnolgicas, principalmente a
interface grfica. Por outro lado, atravs de vrias mquinas isoladas, os servios teriam que
ser replicados e cada mquina incrementada para suportar tal processamento. Alm disso,
essa alternativa no prov uma maneira de comunicao entre os usurios.
Para atender a necessidade crescente do usurio de novos servios e recursos, surgiu-se a
necessidade de interligar essas mquinas, para que juntas pudessem fornecer um nmero
maior de benefcios. Assim comearam a aparecer ambientes de redes que permitiam a
distribuio de recursos e servios em locais diferentes, aumentando a capacidade do usurio
final. Com isso as mquinas que tinham que ser equipamentos caros e poderosos podiam ser
aos poucos substitudas por vrias mquinas menores e de custo mais baixo, mas que atravs
de um processo cooperativo conseguiam prover funcionalidade maior.
Com o nmero cada vez maior de computadores interligados, esses deixaram de ser o foco
do ambiente computacional e toda a ateno e importncia foram destinadas s redes,
elementos responsveis em reunir os recursos e servios de vrias mquinas e disponibilizlos aos usurios de forma transparente e eficaz.
Delphi Client/Server
I N T R O D U O
A O
M O D E L O
C L I E N T E / S E R V I D O R
Web
Informao
Dados
Rede
Data
WareHousing
Fig 1.2: As redes interligam os computadores fornecendo servios e recursos de forma escalar e transparente para os usurios finais.
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
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
Arquitetura Resource-Sharing
Essa arquitetura consiste de vrios computadores (estaes de trabalho) interligados, sendo
cada um capaz de realizar seu prprio processamento. Alguns desses computadores so
responsveis em compartilhar e gerenciar recursos tais como impressora, disco, etc
(servidores de rede).
Entretanto, a rede no utilizada para proporcionar um processamento cooperativo entre as
mquinas. Todo o processamento da aplicao ainda feito por uma nica mquina,
havendo apenas o compartilhamento de recursos. Atravs dessa arquitetura possvel
compartilhar a base de dados da aplicao, permitindo o acesso por vrias pessoas
simultaneamente. Mas como todo o processamento dos dados realizado em cada mquina,
a necessidade de um volume maior de informaes torna invivel o trfego de informaes
pela rede. Para resolver esse problema seria necessrio que a mquina responsvel em
armazenar os dados fizesse um processamento local capaz de enviar uma quantidade menor
de dados para a mquina que est processando a aplicao.
Delphi Client/Server
I N T R O D U O
A O
M O D E L O
C L I E N T E / S E R V I D O R
Servidor de Rede
Aplicaes
Processamento
Processamento
Fig. 1.6: Arquitetura Resource-Sharing.
Arquitetura Cliente/Servidor
Essa arquitetura tambm consiste de vrios computadores, cada um com seu prprio
processamento, interligados em rede. A diferena bsica para a arquitetura Resource-Sharing
que aqui j comea a haver um processamento distribudo cooperativo. Parte do
processamento, que era feito pela mquina da aplicao, feito agora pela prpria mquina
responsvel pelo armazenamento e distribuio da informao, diminuindo assim o trfego
de informaes na rede. Portanto, pode-se e deve-se selecionar os dados que sero enviados
para o usurio para uma melhor eficincia do ambiente.
Esse modelo j comea a retirar partes especficas de processamento das aplicaes que eram
executadas pelas mquinas clientes, centralizando-as nas mquinas de localizao fsica mais
adequada, garantindo assim uma melhor distribuio do processamento e utilizao do
ambiente. Atravs dessas especializaes garante-se tambm um melhor gerenciamento e
facilidade de manuteno dos servios devido a sua concentrao em um ou poucos locais
fsicos.
Delphi Client/Server
I N T R O D U O
A O
M O D E L O
Servidor de Banco
de Dados (DBMS)
C L I E N T E / S E R V I D O R
Servidor de Rede
Processamento
Processamento
Processamento
Aplicaes
Processamento
Fig. 1.7 Arquitetura Cliente/Servidor.
Podemos citar alguns benefcios que esta nova arquitetura traz para o ambiente
computacional das empresas:
Permite s corporaes alavacarem a tecnologia de computadores desktops. Hoje, as
estaes de trabalho j possuem um poder computacional considervel, por um custo
muito mais baixo, antes s disponvel em Mainframes.
Permite que o processamento seja feito mais prximo da origem dos dados reduzindo o
trfego na rede.
Facilita a utilizao de aplicaes grficas e multimdias. Isto permite a construo de
aplicaes que excedem as expectativas dos usurios proporcionando-lhes um real
aumento de produtividade.
Permite e encoraja a utilizao de sistemas abertos, j que clientes e servidores podem
rodar em diferentes hardwares e softwares, livrando as corporaes de arquiteturas
proprietrias.
Entretanto, essa arquitetura ainda no perfeita. Algumas caractersticas necessrias para um
completo processo distribudo ainda no foram observadas nesse modelo que ainda
apresenta algumas deficincias:
Se uma poro significante da lgica da aplicao for movida para o servidor, esse se
torna um gargalo assim como na arquitetura de Mainframe. Para resolver esse
problema seria necessrio uma melhor distribuio e gerenciamento do processamento da
Delphi Client/Server
I N T R O D U O
A O
M O D E L O
C L I E N T E / S E R V I D O R
lgica da aplicao. Isto d origem a uma nova arquitetura chamada Segunda Gerao
Cliente/Servidor.
O processo de construo de aplicaes distribudas bem mais complexo que o
desenvolvimento de aplicaes no distribudas devido ao maior nmero de parmetros a
serem definidos, ao desconhecimento da tecnologia e a falta de padres e ferramentas que
auxiliem essa ambientao. Portanto, muito tempo pode ser consumido no processo de
definio e construo do ambiente de desenvolvimento. Esse tempo muitas vezes
subestimado pelas empresas devido ao desconhecimento e as falsas propagandas dos
vendedores de ferramentas.
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
Delphi Client/Server
Captulo
2
SGDB - Sistemas
Gerenciadores de Banco de
Dados
Esse captulo apresenta os sistemas gerenciadores de banco de dados, seus
servios, recursos e modelos.
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
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
20/10/96 15,00
Ana
Maria
Caneta
10
10,00 5
999.876.555-22
Lpis
21/10/96 65,00
111.111.111-22
5,00
3
Maria
Jos
15
Caneta
15,00
Caderno
50,00
Entretanto, esse mtodo ainda possui alguns problemas. Cada nvel da rvore inicialmente
definido e qualquer alterao na estrutura desses nveis uma tarefa difcil. Alm disso, o
problema da redundncia de dados ainda no foi resolvido, j que esse modelo no
implementa relaes de muitos-para-muitos. No exemplo, podemos notar a repetio de
algumas informaes, o que dificulta a integridade e manuteno dos dados.
Banco de Dados de Rede
O modelo de rede descreve, conceitualmente, os banco de dados nos quais permitem
relaes de muitos-para-muitos entre os elementos de dados. Desta forma cada item
possui um ponteiro para os itens com os quais se relaciona, eliminando assim a necessidade
de qualquer tipo de redundncia de dados. O grande problema desse modelo a sua
complexidade devido a flexibilidade existente em suas relaes. Quando o volume de dados
comea a crescer, os relacionamentos entre os itens de dados ficam cada vez mais
complexos, tornando sua visualizao e entendimento cada vez mais difceis.
Delphi Client/Server
12
S G D B
S I S T E M A S
G E R E N C I A D O R E S
D E
B A N C O
D E
D A D O S
R aiz
Ana
M aria
L pis
C an e ta
C ad ern o
M aria
Jos
11 1 .1 1 1 .1 1 1-22
9 9 9.8 76 .55 5 -2 2
Delphi Client/Server
13
S G D B
S I S T E M A S
G E R E N C I A D O R E S
Pedido
NumPed
1
2
3
1
2
B A N C O
D E
D A D O S
Itens
Data
20/10/96
21/10/96
22/10/96
Valor
Cliente
15,00
65,00
25,00
1
2
2
Cliente
Codigo
D E
Nome
Ana Maria Lima
Maria Jos
NumPed
Produto
1
1
2
2
3
3
Caneta
Lpis
Caneta
Caderno
Lpis
Caderno
CPF
999.876.555-22
111.111.111-22
Delphi Client/Server
14
Quantid. Valor
10
5
15
5
15
1
10,00
5,00
15,00
50,00
15,00
10,00
Captulo
3
Bancos de Dados
Relacionais
Esse captulo apresenta algumas caractersticas dos bancos de dados
relacionais.
s bancos de dados relacionais vm se tornando um padro no mercado, servindo
como base de dados para a maioria dos sistemas das empresas. A cada dia, mais
ferramentas so construdas para tirar proveito dessa tecnologia, fazendo surgir
um nmero crescente de recursos e produtos a serem oferecidos para os usurios.
Pode-se dizer ento, que esta uma tecnologia slida e consistente e que ir acompanhar o
mercado por um longo perodo de tempo. No entanto, uma tecnologia que continua
evoluindo com o objetivo de disponibilizar as informaes para os usurios de maneira
eficiente, viabilizando o negcio da corporao e descentralizando as tomadas de deciso.
Classificao
Quanto a capacidade, recursos e facilidade de uso, os banco de dados relacionais podem ser
divididos em trs categorias: corporativos, departamentais e locais. Em uma mesma empresa
todas as categorias podem coexistir e cooperar entre si para juntas formarem uma poderosa
base de dados distribuda.
Delphi Client/Server
15
B A N C O S
D E
D A D O S
R E L A C I O N A I S
Corporativo
Departamentais
Locais
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
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:
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
Uma tabela est na primeira forma normal se no possuir grupos de atributos repetitivos.
D
C
D
C
Chave
E
D
Grupo
Repetitivo
Fig. 3.2: Presena de grupos repetitivos em uma tabela relacional.
Para resolver esse problema, deve-se remover os grupos repetitivos para uma outra tabela A
chave primria dessa nova tabela pode ser formada atravs da chave primria da tabela
original mais um conjunto de novos atributos.
Estrutura Original
Nova Estrutura
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.
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
No exemplo de pedido, isso ocorre com a coluna PRECOPRODUTO, que depende apenas
do NOMEPRODUTO e no depende de NUMPED. Dado o nome do produto possvel
saber seu preo no importando o pedido que o produto pertena. Assim o preo do
produto seria repetido em vrios registros da estrutura desnecessariamente e dificultando a
manuteno e consistncia dos dados. Para evitar essa redundncia, deve-se criar uma nova
estrutura e a chave primria dessa nova tabela seria o NOMEPRODUTO, porm por
questes de eficincia pode-se criar uma nova coluna CODPRODUTO de tamanho menor
para fazer a ligao entre as tabelas. Alm da eficincia, esse procedimento possibilita
alteraes futuras nos nomes dos produtos, j que esses no so mais usados como chave
primria.
*NumPed
*NumPed
DataPed
*CodProduto
ValorPed
Quantidade
NomeCliente ValorItem
CPFCliente
*CodProduto
NomeProduto
PreoProduto
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
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
Delphi Client/Server
21
B A N C O S
D E
D A D O S
R E L A C I O N A I S
Pedido
*NumPed
DataPed
ValorPed
CodCliente
Cliente
*CodCliente
NomeCliente
CPFCliente
ItemPedido
*NumPed
*CodProduto
Quantidade
ValorItem
Produto
*CodProduto
NomeProduto
PreoProduto
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.
Hist Itens
*NumPed
*NumSeq
*Data
Fig 3.14 Tabela de Hist. De Itens.
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
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
A seguir deve-se executar o arquivo script vendas.sql atravs da opo do Menu File/Run
an ISQL Script. Assim o programa ir criar as tabelas, colunas e outras definies presentes
no arquivo. A estrutura criada representada pelo modelo de dados da figura 3.12.
UF
*UF_SG
UF_NM
CIDADE
*CID_CD
*UF_SG
CID_NM
CID_NUMHABITANTES
PESSOA
*PES_CD
PES_NM
PES_TP
PES_CGCCPF
PES_LOGRADOURO
PES_NUMERO
PES_COMPLEMENTO
PES_BAIRRO
CID_CD
UF_SG
CLIENTE
*PES_CD
CLI_LIMITECREDITO
CLI_DEBITO
VENDEDOR
*PES_CD
VEN_VENDAS
VEN_PERCCOMISSAO
VEN_COMISSAO
PEDIDO
*PED_CD
PED_DT
PED_VALOR
PED_TIPO
PES_CD_CLI
PES_CD_VEN
PRODUTO
*PRO_CD
PRO_NM
PRO_PRECO
PRO_ESTOQUE
ITEM
*PED_CD
*PRO_CD
ITE_VALOR
ITE_QUANT
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
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)
ou
select PRO_CD,PRO_NM,PRO_ESTOQUE,PRO_PRECO from PRODUTO
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
Tabela B
*a
b
c
Tabela A
*c
d
e
.
.
Fig 3.19: Integridade Referencial.
Existem trs formas de se manter essa regra quando registros da tabela A so excludos:
Restrict: Define que se o valor da coluna c de A existir em algum registro de B, o
registro no poder ser excludo e uma mensagem de erro retornar para a aplicao;
Cascade: Define que se o valor da coluna c de A existir em algum registro de B,
todos os registros que possurem esse valor sero tambm excludos;
Delphi Client/Server
29
B A N C O S
D E
D A D O S
R E L A C I O N A I S
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
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
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
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
Delphi Client/Server
33
Captulo
4
SQL Explorer
Esse captulo apresenta a ferramenta de definio e manipulao de
dados SQL Explorer da Borland.
ode-se utilizar a ferramenta SQL Explorer para criar a estrutura do banco de dados
de maneira grfica e interativa. Alm disso, essa ferramenta permite definir novos
aliases para os bases de dados, manipular os dados (inserir, alterar, excluir), e
construir um dicionrio de dados com definies que podem ser utilizadas para
facilitar a construo de aplicaes em Delphi.
Criao de Alias
Uma das possibilidades do SQL Explorer a definio de aliases. Ao abrir o SQL
Explorer exibido na lista Databases todos os aliases definidos no BDE.
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.
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.
Delphi Client/Server
36
S Q L
E X P L O R E R
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.
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.
Delphi Client/Server
38
S Q L
E X P L O R E R
Delphi Client/Server
39
S Q L
E X P L O R E R
Alm disso, pode-se tambm associar um componente Field atravs da opo Associate
attributes... do popup menu.
Delphi Client/Server
40
Captulo
5
Trabalhando com Bancos de
Dados Relacionais
Esse captulo apresenta a forma de trabalho dos bancos de dados
relacionais.
s bancos de dados relacionais fornecem um conjunto de servios para que as
aplicaes possam acess-los e manipul-los. Portanto, importante entender
bem sua funcionalidade para melhor aproveitar seus recursos nas aplicaes.
Existem algumas diferenas na forma de trabalhar entre os fornecedores, mas
grande parte dos conceitos podem ser aplicados a todos os bancos de dados relacionais.
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
CLIENTE
CLIENTE
MIDDLEWARE
Aplicaes
Ferramentas
Desenvolvimento
Servios Especficos
SERVIDOR
SERVIDOR
SQL/API
SQL/API
Banco
Bancode
deDados
Dados
Transporte
Database
DatabaseEnginer
Enginer
NetBios
NetBios
TCP/IP
TCP/IP
IPX/SPX
IPX/SPX
SNA
SNA
SOR
Serv.
Serv.Diretrio
Diretrio
Sistema Operacional
RPC
RPC
Serv.
Serv.Diretrio
Diretrio
Mensagens
Mensagens
Segurana
Segurana
Sistema Operacional
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
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
TDatabase e TDataSet
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
Contextos
Aplicao
Conexo
Comando
Insert
Comando
Select
Delphi Client/Server
Banco
Comando
Update
45
Cursor
Result
Set
.
.
.
.
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
Transaes
Um dos recursos mais importantes fornecidos pelos bancos de dados o controle de
transao. O controle de transao permite as aplicaes manterem a integridade lgica dos
dados em um determinado processo. Uma transao um conjunto de comandos
executados no banco de dados que devem ser aplicados integralmente. Se um dos comandos
Delphi Client/Server
46
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
falhar, todos os outros tm que ser desfeitos para manter a integridade e a unicidade do
processo em execuo. Desta forma pode-se garantir a confiabilidade dos dados
armazenados no banco. Considere o seguinte exemplo: um processo de transferncia entre
contas correntes que deve debitar um valor em uma das contas e creditar exatamente o
mesmo valor em uma outra conta. Se o processo falhar no meio e esse recurso de controle
de transao no estiver sendo utilizado, o dinheiro pode ser retirado da primeira conta, mas
no ser creditado na segunda, assim o valor que deveria ser transferido, simplesmente
desaparece da base de dados.
Para fazer o controle de transao, so fornecidos normalmente pelos bancos de dados trs
comandos: um para iniciar a transao (begin transaction), um para finalizar e aplicar as
modificaes executadas (commit) e outro para finalizar cancelando e desfazendo toda a
operao (rollback). Portanto, quando uma transao iniciada, todos os comandos
seguintes fazem parte de uma nica transao at ser encontrado um comando que a finalize
(commit ou rollback). Se todos os comandos foram executados com xito, pode-se ento
disparar o comando commit para que estes sejam aplicados e se tornem visveis para os
demais usurios. Caso contrrio, se um erro foi encontrado em algum dos comandos, podese disparar um rollback descartando todas as alteraes desde o comeo da transao.
Cada conexo, normalmente suporta apenas um controle de transao e todos os seus
contextos fazem parte dessa mesma transao. Quando uma transao iniciada, qualquer
comando disparado em qualquer um dos contextos faz parte da transao. E quando a
transao fechada os comandos executados por todos os contextos so aplicados e os
contextos so destrudos. Sendo assim, comandos que eram mantidos preparados em algum
contexto necessitam ser novamente compilados e contextos que possuam cursores e result
sets que no haviam sido trazidos totalmente para a aplicao so fechados e portanto seus
dados perdidos. Algumas ferramentas, como o Delphi, ao verificarem que os contextos sero
destrudos buscam todos os registros de result sets pendentes para a aplicao antes que
estes sejam destrudos.
Alguns bancos de dados permitem que sejam mantidos os contextos quando a transao
finalizada. Entretanto, necessrio que a ferramenta de desenvolvimento tambm faa uso
desse recurso. Pode-se tambm criar mais de uma conexo, para conseguir manter abertos os
contextos, deixando-os em uma conexo separada da conexo que a transao est sendo
executada, j que o controle de transao s vlido para uma nica conexo. Porm, devese tomar bastante cuidado com essa ltima alternativa. Como ser visto adiante, o controle
de travamento de registros tambm feito para cada transao e portanto uma conexo
pode travar comandos da outra conexo deixando a aplicao em DeadLock, se os
cuidados necessrios no forem tomados.
Alguns servidores de banco de dados tambm suportam o controle de transao entre dois
bancos diferentes atravs do recurso chamado two-phase commit. Esse recurso permite
que a finalizao da transao seja feita em dois passos, possibilitando que um banco de
dados notifique ao outro o sucesso da transao para que ela possa ser finalizada como um
todo.
Transaes no Delphi
Para se fazer esse controle de transao no Delphi deve-se utilizar um conjunto de mtodos
existentes no componente TDatabase.
Delphi Client/Server
47
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
StartTransaction
- Inicia a transao.
Commit
Rollback
Projeto Exemplo
O projeto a seguir mostra como as transaes so tratadas nas operaes feitas pelo prprio
Delphi e como se comportam os cursores e contextos j abertos. Quando no se usa
explicitamente os comandos vistos acima, o Delphi abre implicitamente uma transao a
cada comando de atualizao disparado no banco e logo a seguir fecha a transao com um
comando commit. O objetivo do projeto observar o que ocorre com o Result Set
aberto, quando um comando que finaliza a transao executado. Para construir a aplicao
deve-se desenhar o form como mostrado na figura e definir as propriedades listadas na
tabela.
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
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.
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
Aps abrir o SQL Monitor deve-se executar a aplicao e observar que somente so trazidos
para a aplicao uma quantidade de dados suficiente para preencher a parte visvel do
Grid. Quando o usurio navega pelo Grid, novas linhas so trazidas, at que o usurio
atinja o fim do result set.
Alm disso, deve-se observar que se for feita uma atualizao em algum registro, ser
disparado um comando close fechando o result set e quando o usurio navega para uma
linha que ainda no foi trazida para a aplicao, um novo comando select ser feito
buscando as linhas que ainda no foram trazidas atravs de um filtro colocado na clusula
where do comando.
SELECT PRO_CD ,PRO_NM ,PRO_PRECO ,PRO_ESTOQUE ,TIMESTAMP
WHERE (PRO_CD> ?) ORDER BY PRO_CD ASC
FROM PRODUTO
Atravs desse exemplo, pode-se observar que quando uma transao finalizada todos os
result sets abertos so fechados e no caso do componente TTable um novo select feito
quando o usurio navega por registros ainda no enviados a estao.
Existe, no entanto, uma diferena quando se utiliza o componente TQuery. Nesse
componente, quando a transao finalizada, o Delphi l todas as linhas pendentes at o
final do result set antes de executar o comando commit. Para verificar esse
comportamento, deve-se alterar a propriedade Active do Table1 para False e a propriedade
Active do Query1 para True e fazer os mesmos procedimentos do exemplo anterior.
Porm alguns bancos suportam que os cursores permaneam abertos quando a transao
finalizada. O Interbase um dos bancos que possuem essa caracterstica e para utiliz-la
necessrio alterar a propriedade DRIVER FLAGS no BDE. Entretanto essa alternativa s
vlida para transaes controladas implicitamente pelo Delphi.
Delphi Client/Server
50
T R A B A L H A N D O
DRIVER FLAGS
0
512
4096
4068
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
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 que podem ser colocadas nos registros ou pginas quando o usurio seleciona o
registro. Uma trava shared lock permite que outras travas do mesmo tipo sejam atribudas
ao mesmo registro ou pgina, entretanto probe a atribuio de uma trava exclusive lock.
Portanto, quando se atribui a um registro ou pgina uma trava shared lock, bloqueia-se
qualquer atualizao por outra transao (conexo), permitindo assim a aplicao ter acesso
ao dado com a certeza que este no ser alterado por mais ningum. Entretanto, possvel
que o mesmo usurio na mesma conexo possa alterar o registro ou pgina marcado por ele
Delphi Client/Server
51
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
como shared lock transformando-o para exclusive lock, desde que nenhuma outra
conexo possua tambm uma trava shared lock para o mesmo registro ou pgina. Todas
as travas shared lock tambm so retiradas quando se finaliza a transao.
Pg. ou Regist.\Transaes
1
2
3
4
5
1
Exclusive
Shared
Shared
Shared
Shared
Shared
Exclusive
Tabela de Transaes: Um exclusive lock em uma linha, exclui a possibilidade de existir outro exclusive lock ou shared lock em uma
outra coluna na mesma linha.
Nveis de isolamento
Podemos definir alguns nveis de isolamento, de acordo com a forma que os shared locks
so atribudos aos registros ou pginas. A seguir sero mostradas as vrias formas de se
trabalhar com locks no banco de dados, independente dos nomes utilizados pelos vrios
fornecedores. Sendo possvel que alguns bancos de dados no apresentem todas as
possibilidades descritas abaixo.
Travamento de todos os registros
Nessa modalidade atribuda uma trava shared lock para todos os registros da seleo feita
atravs de um comando select. Este o nvel de isolamento que mais trava os registros e
deve ser utilizado com cautela para no inviabilizar a concorrncia dos dados e o
funcionamento da aplicao.
Travamento de um registro ou pgina
Nesse tipo de travamento, so colocadas travas shared locks somente para o registro ou
pgina onde o cursor se encontra posicionado. Quando a aplicao movimenta o cursor para
um prximo registro, retirada a trava do registro anterior e colocada no registro atual .
Portanto, somente um registro ou pgina fica travado em um determinado momento.
Sem travamento
Nessa modalidade atribuda uma trava shared lock no momento de acesso a cada
registro. Entretanto, essa trava retirada logo a seguir e o registro ou pgina no permanece
travado. Essa pequena atribuio da trava simplesmente para verificar se no existe
nenhuma outra trava do tipo exclusive lock sobre o registro, que no permitiria a sua
leitura. Portanto, mesmo nessa categoria no possvel acessar registros alterados no meio de
uma transao que esto marcados como exclusive lock.
Seleo de registros travados com um exclusive lock.
Alguns bancos permitem que uma conexo acesse os dados de um registro ou pgina
mesmo se estes estejam marcados como exclusive lock. Para tal, o registro acessado sem
a atribuio de nenhuma trava, j que uma trava SL no pode coexistir com uma trava
EL em um mesmo registro ou pgina. Como os registros acessados possuem dados que
no foram efetivados atravs de um commit, so normalmente disponibilizadas cpias
antigas do mesmo registro armazenadas pelo banco de dados. Apesar de permitir esse tipo
de acesso, muitos bancos de dados probem a alterao dos dados quando acessados desta
forma.
Delphi Client/Server
52
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
Optimistic Lock
Para se maximizar a concorrncia entre os dados nas aplicaes, podem ser utilizadas outras
formas de controle que diminuem os travamentos causados na base de dados. Para isso
pode-se utilizar o nvel de isolamento Sem travamentos e fazer um controle adicional para
ter certeza que os dados mostrados para o usurio no foram alterados quando esse inicia o
processo de alterao do registro.
Existem algumas alternativas que podem ser seguidas para se implementar esse processo. O
processo normal de um comando de update ou delete conter na clusula where a
verificao da chave primria para possibilitar a identificao do registro na tabela.
Entretanto, explorando um pouco mais esse recurso de identificao do registro, pode-se
implementar um bom controle de concorrncia.
Where All
Consiste em verificar na clusula where a chave primria mais os campos que sofreram
alterao pelo usurio. Desta forma garante-se que os mesmos campos no foram alterados
por outro usurio. Mas, permite atualizar um registro alterado por outro usurio desde que
no sejam os mesmos campos.
Where Key
Nesse mtodo a concorrncia livre, pois permite que o registro seja alterado sem verificar
se este foi alterado por outro usurio. Na clusula where de alterao, somente utilizada a
chave primria da tabela para localizar o registro.
Where Timestamp/Rowid
Consiste em verificar na Clusula where a chave primria mais uma coluna definida
exclusivamente para fazer o controle de concorrncia. Essa coluna atualizada para um
novo valor toda vez que o registro for inserido ou alterado. Portanto, se o registro for
alterado por um outro usurio, o registro no ser encontrado para atualizao.
Alguns tipos de bancos de dados j possuem algum suporte para esse tipo de controle. O
SQLServer e o Sybase possuem um tipo de dado chamado timestamp, para o qual pode se
criar uma coluna que ser atualizada automaticamente com a hora, minuto, segundo e
dcimos de segundo do momento em que ocorrer uma operao de insero ou alterao. O
SQLBase um outro banco que permite esse controle atravs de uma coluna chamada
rowid que j definida automaticamente para todas as tabelas e atualizada para um valor
nico quando ocorre as operaes insert e update.
Para os bancos que no possuem automaticamente esses recursos, deve-se criar uma nova
coluna que pode armazenar a Data/hora como no caso do timestamp, desde que o tipo
permita a atribuio de dcimos de segundo, porque a unidade segundo no possui preciso
Delphi Client/Server
53
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
suficiente para essa operao. Ou pode-se criar uma coluna numrica, cujo o valor ser
incrementado atravs de um contador nico a cada atualizao realizada sobre o registro.
Alm de criar a coluna, preciso fazer tambm o processo de atualizao dos dados quando
uma operao de insert ou update ocorrer. Pode-se fazer isso atravs da aplicao ou
atravs do recurso de triggers dos bancos de dados. A utilizao de triggers possibilita
que a atualizao possa ser feita atravs de outras ferramentas que no sejam a prpria
aplicao e portanto mais recomendado.
Concorrncia no Delphi
Para se configurar os nveis de isolamento utilizados pelo Delphi deve-se utilizar a
propriedade TransIsolation do componente TDatabase.
tiDirtyRead - Permite ler alteraes feitas por outro usurio (ou transao) ainda no
comitadas (efetivadas), ou seja no respeitando os exclusives locks. Essa opo
normalmente no est disponvel nos banco de dados.
tiReadCommitted - Esse o tipo mais normal de se trabalhar. Somente as alteraes
feitas por outro usurio (ou transao) j comitadas so vistas pelo usurio. Se a
alterao ainda no foi efetivada o registro fica travado e a leitura fica esperando o fim da
transao.
tiRepeatableRead - Esse mtodo utilizado para que o dado visto pelo usurio seja
sempre o mesmo durante a transao se este for lido novamente. Alguns bancos
implementam este recurso permitindo ver uma cpia mais antiga de um registro alterado
por outra transao e ainda no comitado. Desta forma a aplicao no fica travada
esperando a liberao do registro pela transao. Esta alternativa diminui a possibilidade
de travamentos entre as transaes, porm consome um nmero maior de recursos do
banco para fazer as cpias (verses) dos registros. Outros bancos, implementam este
recurso travando com um shared lock todos os registros lidos, no permitindo assim
que outro usurio altere o registro, desta forma se os dados forem relidos seus valores
permanecem o mesmo. Ao contrrio da implementao anterior, essa alternativa aumenta
a possibilidade de travamentos entre as transaes e deve ser utilizada com bastante
cuidado.
Delphi Client/Server
54
T R A B A L H A N D O
C O M
B A N C O S
D E
D A D O S
R E L A C I O N A I S
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
Componente
Form1
Database1
Query1
DataSource1
Grid1
Button1
Button2
Button3
Propriedade
Name
Alias
DatabaseName
Connected
DatabaseName
SQL
Request Live
DataSet
DataSource
Caption
Caption
Caption
Valor
frmLock
VENDAS
DBVENDAS
True
DBVENDAS
Select * from Cidade
True
Query1
DataSource1
Start Trans
Commit
Open
Tabela de Propriedades.
O boto Start Trans ser utilizado para iniciar a transao e portanto implementado da
seguinte maneira:
procedure TfrmLock.Button1Click(Sender: TObject);
begin
Database1.StartTransaction;
end;
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;
Depois de montada a aplicao deve-se compil-la, execut-la e abrir mais uma instncia
executando o EXE atravs do windows explorer.
Delphi Client/Server
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
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;
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.
O objetivo desse exemplo foi monitorar o acesso ao banco de dados atravs da alternncia
de vrias propriedades dos componentes de uma aplicao Delphi, de forma a verificar as
diversas possibilidades que o desenvolvedor pode optar durante a construo da aplicao.
Em uma aplicao real, no se separa dessa forma os comandos de abrir uma transao,
executar a operao e fechar a transao. Essas operaes devem ser executadas
seqencialmente em uma nica interao com o usurio em um perodo de tempo bem
pequeno. No cabe ao usurio tomar a deciso de abrir e fechar a transao, porque ele
poderia deixar travado durante muito tempo um registro que vrios outros usurios precisam
utilizar.
Delphi Client/Server
59
T R A B A L H A N D O
Delphi Client/Server
C O M
B A N C O S
D E
D A D O S
60
R E L A C I O N A I S
Captulo
6
Projetando Aplicaes
Cliente/Servidor
Esse captulo mostra algumas tcnicas de construo de aplicaes
voltadas para o ambiente Cliente/Servidor.
Delphi Client/Server
61
P R O J E T A N D O
A P L I C A E S
C L I E N T E / S E R V I D O R
Gerenciamento de Dados
Parte da aplicao responsvel pelo acesso e a manipulao dos dados no servidor. Como j
foi visto anteriormente, grande parte dessa camada implementada pelo prprio servidor de
banco de dados.
Normalmente o acesso aos servios feito atravs da linguagem SQL. Porm, tambm
necessrio um conjunto de comandos para enviar as sentenas SQLs e gerenciar a
comunicao entre a aplicao e o servidor. Esses comandos se encontram em bibliotecas
disponibilizadas pelos prprios fornecedores de banco de dados que so instaladas em cada
estao de trabalho. Alm disso cada fabricante de ferramentas de desenvolvimento fornece
tambm mtodos e componentes capazes de simplificar e tornar mais transparente o acesso
aos diversos SGDBs.
CLIENTE
APRESENTAO
FSICO
LGICA do NEGCIO
LGICO
GERENCIAMENTO de DADOS
SERVIDOR
Fig 6.1: Camadas Fsicas e Lgicas de uma Aplicao.
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.
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
Componente de ligao
Componente responsvel pela interface entre as duas camadas acima. Sua principal
caracterstica tornar os componentes visuais independentes dos componentes de acesso, ou
seja, a camada de Apresentao da Lgica do Negcio. Esse componente o TDataSource
que fica localizado tambm na pgina Data Access da paleta de componentes.
Delphi Client/Server
64
C O N S T R U I N D O
A P L I C A E S
C L I E N T E / S E R V I D O R
Captulo
7
Construindo Aplicaes
Cliente/Servidor
Esse captulo mostra a construo de uma tela de manuteno
utilizando-se os componentes do Delphi e como configur-los para
trabalhar com banco de dados.
Delphi Client/Server
65
C O N S T R U I N D O
Componente
DataModule1
A P L I C A E S
C L I E N T E / S E R V I D O R
Propriedade
Name
Valor
DMSistVendas
Tabela de Propriedades.
Componente TDatabase
Como foi visto o componente TDatabase responsvel pela conexo com o banco de dados.
Esse componente possui algumas propriedades que permitem a configurao dessa conexo.
Pode-se utilizar a opo Database Editor do popup menu que exibido ao se pressionar o
boto direito do mouse sobre o componente Database1.
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
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
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.
Componente
Query1
Propriedade
Name
DatabaseName
SQL
Valor
QProduto
DBVENDAS
Select * from produto
order by produto.prod_cd
TRUE
TRUE
Request Live
Active
Tabela de Propriedades.
A propriedade Request Live ligada, faz com que o Delphi tente atualizar automaticamente
o result set trazido pela query. Desta forma, o componente TQuery se comporta de
forma semelhante ao componente TTable.
Entretanto, para que isso seja possvel, o comando SQL tem que obedecer algumas
restries como por exemplo:
Conter apenas uma tabela na clusula From;
No possuir agregaes atravs de group by e funes como sum, max, etc;
Aps termos preenchido o DataModule com os componentes responsveis pelo acesso ao
banco de dados, podemos construir o form de manuteno da tabela de produto como a
figura a seguir:
Delphi Client/Server
69
C O N S T R U I N D O
A P L I C A E S
C L I E N T E / S E R V I D O R
Componente
Form1
Panel1
Panel2
DataSource1
DBNavigator1
DBEdit1..DBEdit4
Propriedade
Name
Caption
Align
Caption
Align
Caption
Name
DataSet
DataSource
DataSource
DataField
Valor
frmProduto
Produtos
alTop
alClient
DSMain
DMSistVendas.Qproduto
DSMain
DSMain
pro_cd..pro_estoque
Tabela de Propriedades.
Podemos ento executar a aplicao e verificar que atravs da propriedade Result Live do
Qproduto, foi possvel atualizar os dados e acrescentar novas linhas tabela. Entretanto,
podemos notar que as linhas inseridas desaparecem do Result Set ao navergarmos para
outro registro. Esta uma restrio que existe no componente TQuery quando se utiliza a
propriedade Result Live para torn-lo atualizvel automaticamente pelo Delphi. Alm disso,
foi visto que para ser possvel tornar o result set atualizvel, existem algumas restries
quanto ao comando SQL definido.
Por causa desses motivos, talvez seja ento necessrio trabalhar com a propriedade Result
Live igual a False e utilizar um outro artifcio para tornar o result live atualizvel sem
restries no comando SQL e sem perder as linhas inseridas.
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
Componente
SppedButton1
SppedButton2
SppedButton3
SppedButton4
Propriedade
Name
Name
Name
Name
Valor
btnAppend
btnDelete
btnSave
btnDiscard
Tabela de Propriedades
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;
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.
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
UpdateSQL1
QProduto
Propriedade
Name
Request Live
UpdateObject
Valor
USProduto
False
USProduto
Tabela de Propriedades
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.
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
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;
.
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;
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
Componente
Table1
Propriedade
Name
DatabaseName
TableName
Active
Valor
TUF
DBVENDAS
UF
TRUE
Tabela de Propriedades.
Componente
form1
Panel1
Panel2
DataSource1
DBNavigator
SppedButton1
SppedButton2
SppedButton3
SppedButton4
DBEdit1..DBEdit2
Propriedade
Name
Caption
Caption
Caption
Name
DataSet
DataSource
Name
Name
Name
Name
DataSource
DataField
Valor
frmUF
UF
DSMain
DMSistVendas.TUF
DSMain
btnAppend
btnDelete
btnSave
btnDiscard
DSMain
UF_SG..UF_NM
Tabela de Propriedades.
Delphi Client/Server
79
C O N S T R U I N D O
A P L I C A E S
C L I E N T E / S E R V I D O R
Podemos agora alterar a tela principal do projeto para frmUF e executar a aplicao. Iremos
notar que a tela funciona de uma forma bem semelhante tela de Produto. Porque ento
implementar telas com TQuery, se atravs do componente TTable muito mais simples e
rpida a construo ?
Alm da facilidade, a implementao atravs do componente TTable mais eficiente quando
a transao finalizada j que no busca todos os registros no banco. Entretanto, teremos
que optar pela implementao atravs do TQuery quando existir um nmero maior de tabelas
envolvidas em uma nica tela. Cada TTable ir executar um comando select, que poderia
talvez, ser executado por um nico comando atravs do componente TQuery com uma
performance bem melhor. Outra pequena desvantagem do componente TTable que ele
necessita buscar as informaes de catlogo o que faz demorar mais na primeira abertura de
cada tabela. Porm com a opo ENABLED SCHEMA CACHE do BDE ligada, esse
problema pode ser minimizado.
Delphi Client/Server
80
Captulo
8
Filtrando Registros
Esse captulo mostra algumas possibilidades de seleo que podem ser
apresentadas ao usurio
m aplicaes Cliente/Servidor muito comum haver tabelas com milhares e at
milhes de registros. Sendo assim, preciso implementar consultas de forma a no
trazer todos esses registros desnecessariamente para a aplicao. Em uma aplicao
e principalmente em uma aplicao Cliente/Servidor onde a informao est
localizada em um local fsico diferente do aplicativo e esse local consegue prover uma
inteligncia capaz de manipular os dados, deve-se ter como regra trazer para aplicao
somente as informaes realmente necessrias para o usurio . Qualquer outra informao,
alm de poluir a tela, gera um trfego adicional e desnecessrio na rede. Muitas vezes
tentamos adivinhar a informao que o usurio deseja ao invs de deixar que ele mesmo a
pea. As melhores aplicaes Cliente/Servidor so aquelas que conduzem os usurios atravs
das informaes realmente necessrias detalhando-as a medida do necessrio.
As duas telas que ns construmos j comeam ativas, ou seja, mostrando as primeiras linhas
da tabela. Foi enviado um select para o banco de dados, algumas linhas foram trazidas para
aplicao atravs da rede e nem sequer sabemos se o usurio as desejava ver. Imagine que a
tabela de produto possua cerca de 1.000.000 de registros trazidos ordenadamente por nome.
Na nossa tela de produto, o primeiro registro j iria aparecer quando o usurio abrisse a tela.
Mas se o usurio desejasse ver um produto que comeasse com M, ser que ele iria querer
navegar pelos botes de navegao at encontr-lo?
Alm disso, deve-se lembrar que quando se utiliza o componente TQuery para criar o result
set e um comando commit executado, todas as linhas restantes do result set so
trazidas para aplicao. Imagine isso com 1.000.000 de registros.
Portanto, em aplicaes Cliente/Servidor, principalmente em result sets que retornam uma
grande quantidade de registro, comum induzir o usurio a filtrar seu conjunto de registros
antes de traz-los para a aplicao. Existem vrias formas de se implementar esse mecanismo
na aplicao e a seguir vamos apresentar alguns deles.
Delphi Client/Server
81
F I L T R A N D O
R E G I S T R O S
Componente
SppedButton1
SppedButton2
Propriedade
Name
Name
Valor
btnNewForSearch
btnSearch
Tabela de Propriedades
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;
Delphi Client/Server
83
F I L T R A N D O
R E G I S T R O S
Devemos criar mais trs variveis na seo private do form para controlar o comando select:
.
.
private
{ Private declarations }
CmdSelect: String;
CmdOrderBy: String;
CmdSelectNull: String;
public
{ Public declarations }
.
.
Vamos tambm alterar a propriedade SQL do QProduto tirando a clusula order by:
Componente
QProduto
Propriedade
Active
SQL
Valor
False
select * from Produto
Tabela de Propriedades
84
F I L T R A N D O
R E G I S T R O S
end;
Antes ainda de executar, vamos ajustar a abertura dos forms no Project/Options. Deve-se
alterar o Main Form novamente para frmProduto e arrastar o DMsistVendas para o topo
da lista dos Auto-Create para que ele seja o primeiro a ser criado.
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
dsInactive
dsNewFor
Insert
dsNewFor
Search
dsBrowse
dsEdit
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
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
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
Delphi Client/Server
90
F I L T R A N D O
R E G I S T R O S
91
F I L T R A N D O
R E G I S T R O S
end;
end;
end;
end.
Componente
Query1
Delphi Client/Server
Propriedade
Name
Valor
QConsCliente
92
F I L T R A N D O
R E G I S T R O S
DatabaseName
SQL
Table1
Query2
DBVENDAS
SELECT PESSOA.PES_CD ,
PESSOA.PES_NM , PESSOA.PES_TP ,
PESSOA.PES_CGCCPF
FROM CLIENTE CLIENTE , PESSOA PESSOA
WHERE ( CLIENTE.PES_CD =
PESSOA.PES_CD )
ORDER BY PESSOA.PES_NM
Name
DatabaseName
TableName
Name
DatabaseName
SQL
TConsCliUF
DBVENDAS
UF
QConsCliCidade
DBVENDAS
SELECT * FROM CIDADE
WHERE UF_SG = :UF_SG
Params
UF_SG | String
Tabela de Propriedades
A figura a seguir mostra a tela de consulta de clientes. Note que so colocados na parte de
cima da tela componentes TEdit para que o usurio possa fazer a pesquisa antes de trazer os
dados para tela. Nessa seleo, encontram-se os campos UF e CIDADE que representam o
relacionamento da tabela principal PESSOA com as tabelas UF e CIDADE. Portanto estas
tabelas sero utilizadas para permitir o usurio fazer o filtro, mas no entram no select
principal da tela.
Componente
form1
Panel1
Label1
Label2
Delphi Client/Server
Propriedade
Name
Caption
Align
Caption
Caption
Valor
frmConsCliente
Clientes
alTop
Cdigo
Nome
93
F I L T R A N D O
R E G I S T R O S
Label3
Label4
Edit1
Edit2
Edit3
Edit4
Button1
button2
DataSource1
DataSource2
DataSource3
Panel2
DbGrid1
Panel3
Bitbtn1
Bitbtn2
Caption
Caption
Name
Name
Name
Name
Name
Caption
Name
Caption
Name
DataSet
Name
DataSet
Name
DataSet
Align
DataSource
Align
Kind
Kind
UF
Cidade
edtCodigo
edtNome
edtUF
edtCidade
btnPesquisa
Pesquisa
btnNovo
Novo
DSMain
DMSistVendas.QConsCliente
DSUF
DMSistVendas.TConsCliUF
DSCidade
DMSistVendas.QConsCliCidade
alClient
DSMain
alBottom
bkOk
bkCancel
Tabela de Propriedades
Para o usurio poder selecionar uma cidade e ou uma UF utilizaremos um Grid que ser
exibido quando ele posicionar no campo de UF ou de Cidade.
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
Componente
Form1
DBGrid1
Propriedade
Name
BorderSyle
Align
Options
Valor
frmGrid
bsNone
alClient
[dgTitles, dgColumnResize, dgColLines, dgTabs,
dgRowSelect, dgConfirmDelete, dgCancelOnExit]
Tabela de Propriedades
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}
96
F I L T R A N D O
R E G I S T R O S
97
F I L T R A N D O
R E G I S T R O S
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.
Componente
Query1
Delphi Client/Server
Propriedade
Name
DatabaseName
CachedUpdate
UpdateObject
SQL
Valor
Qcliente
DBVENDAS
TRUE
USPessoa
SELECT PESSOA.PES_CD,
PESSOA.PES_NM,
PESSOA.PES_TP , PESSOA.PES_CGCCPF ,
PESSOA.PES_LOGRADOURO ,
PESSOA.PES_NUMERO ,
PESSOA.PES_COMPLEMENTO ,
PESSOA.PES_BAIRRO , PESSOA.CID_CD ,
PESSOA.UF_SG , PESSOA.PES_DT_NASC ,
PESSOA.CID_CD_NASC ,
PESSOA.UF_SG_NASC ,
CLIENTE.CLI_LIMITECREDITO ,
CLIENTE.CLI_DEBITO , CIDADE.CID_NM ,
98
F I L T R A N D O
R E G I S T R O S
UF.UF_NM
FROM PESSOA PESSOA, CLIENTE CLIENTE,
CIDADE CIDADE, UF UF
WHERE PESSOA.PES_CD =
CLIENTE.PES_CD
AND PESSOA.CID_CD = CIDADE.CID_CD
AND PESSOA.UF_SG = CIDADE.UF_SG
AND CIDADE.UF_SG = UF.UF_SG
AND PESSOA.PES_CD= :PES_CD
ORDER BY PESSOA.PES_NM
UpdateSQL1
Params
Name
TableName
PES_CD | Integer
USPessoa
PESSOA
Tabela de Propriedades
Delphi Client/Server
99
F I L T R A N D O
R E G I S T R O S
Componente
Form1
Panel1
SpeedButton1
SpeedButton2
SpeedButton3
SpeedButton4
SpeedButton5
PageControl1
Delphi Client/Server
Propriedade
Name
Caption
Align
Name
Name
Name
Name
Name
Align
Valor
frmCliente
Clientes
alTop
btnAppend
btnDelete
btnSave
btnDiscard
btnSearch
alClient
100
F I L T R A N D O
R E G I S T R O S
TabSheet1
TabSheet2
TabSheet3
DataSource1
DBControls
Caption
Caption
Caption
Name
DataSet
.....
Informaes Gerais
Pessoa Fsica
Crditos
DSMain
DMSistVendas.Qcliente
.....
Tabela de Propriedades
Os eventos dos botes de edio sero definidos de maneira similar ao padro utilizado pela
tela de produto, ficando cada um da seguinte forma:
procedure TfrmCliente.btnAppendClick(Sender: TObject);
begin
If Not DSMain.DataSet.Active then begin
DSMain.DataSet.Open;
end;
DSMain.DataSet.Append;
end;
procedure TfrmCliente.btnDeleteClick(Sender: TObject);
begin
DSMain.DataSet.Delete;
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
DSMain.DataSet.close;
end;
procedure TfrmCliente.btnSaveClick(Sender: TObject);
begin
TBDEDataSet(DSMain.DataSet).ApplyUpdates;
TBDEDataSet(DSMain.DataSet).CommitUpdates;
end;
procedure TfrmCliente.btnDiscardClick(Sender: TObject);
begin
If TBDEDataSet(DSMain.DataSet).UpdatesPending
then
TBDEDataSet(DSMain.DataSet).CancelUpdates
else
TBDEDataSet(DSMain.DataSet).Cancel;
If TBDEDataSet(DSMain.DataSet).IsEmpty then begin
TBDEDataSet(DSMain.DataSet).close;
end;
end;
O boto de Pesquisa ir chamar a tela de consulta de clientes para que o usurio possa
escolher um determinado cliente para trabalhar. Depois de escolhido, informaes mais
detalhadas desse cliente so trazidas para a tela de manuteno para que possam ser
visualizadas e editadas pelo usurio.
procedure TfrmCliente.btnSearchClick(Sender: TObject);
begin
DSMain.DataSet.Close;
frmConsCliente:=TfrmConsCliente.Create(Self);
If (frmConsCliente.ShowModal=mrOk) and
(Not (frmConsCliente.DSMain.DataSet['PES_CD'] = Null)) then
begin
TQuery(DSMain.DataSet).Params[0].value:=
frmConsCliente.DSMain.DataSet['PES_CD'];
DSMain.DataSet.Open;
end;
frmConsCliente.free;
end;
Delphi Client/Server
101
F I L T R A N D O
R E G I S T R O S
Podemos utilizar a prpria mquina de estado do DataSource fornecida pelo Delphi para
controlar a habilitao dos botes:
procedure TfrmCliente.DSMainStateChange(Sender: TObject);
begin
Case DSMain.State of
dsInactive:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
dsInsert:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsEdit:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsBrowse:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
end;
end;
Recursos de LookUp
Existem vrias formas de fornecer para o usurio uma pesquisa em registros de tabelas
secundrias relacionadas com a tabela principal. Esse recurso, muitas vezes chamado de
lookup deve prover implementaes para as trs fases seguintes:
Preencher e exibir uma lista ou tabela de registros para permitir a escolha do usurio;
Ao se escolher um registro, deve-se atualizar o cdigo relacionado na tabela principal;
Delphi Client/Server
102
F I L T R A N D O
R E G I S T R O S
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
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.
106
F I L T R A N D O
R E G I S T R O S
107
F I L T R A N D O
R E G I S T R O S
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsBrowse:
If TQuery(DSMain.DataSet).UpdatesPending then begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end else begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
end;
end;
108
F I L T R A N D O
R E G I S T R O S
Fazendo essas alteraes, quando se abre novamente a tela a ltima consulta feita pelo
usurio permanece ativa. Entretanto, deve-se ter cuidado com essa abordagem. Se o result
set permanecer aberto e esse possuir muitas linhas que ainda no foram trazidas para
aplicao, essas podem ser trazidas quando o usurio realizar uma manuteno no registro e
salvar as alteraes. Como foi visto, para alguns bancos de dados o Delphi realiza a busca
prematura dos registro de um result set pendente antes de realizar o commit.
Portanto, deve-se optar pelo melhor desempenho e funcionalidade para o usurio de acordo
com o banco de dados. As opes para os bancos que no suportam manter um cursor
aberto aps o fechamento da transao seriam:
Tentar minimizar o nmero de registros que sero trazidos na consulta forando o
usurio a atribuir algum tipo de filtro.
Utilizar uma outra conexo, atravs de um outro componente Database para se fazer a
consulta, desta forma a finalizao da transao de uma conexo no interfere na outra.
Entretanto, essa opo costuma consumir um nmero maior de licenas de usurios do
banco de dados. Normalmente, cada conexo corresponde ao uso de uma licena.
Tentar utilizar na tela de consulta um componente TTable ao invs do componente
TQuery. Como foi visto o componente TTable possui um comportamento mais inteligente
no caso do fechamento da transao, j que permite que o cursor seja fechado e quando
houver a necessidade de caminhar por linhas ainda no trazidas dispara um novo
Delphi Client/Server
109
F I L T R A N D O
R E G I S T R O S
comando select. Entretanto, esse componente costuma abrir uma transao e pode estar
mantendo algum registro travado, dependendo do banco de dados utilizado.
Delphi Client/Server
110
Captulo
9
Controlando Transaes
Master/Detail
Esse captulo mostra como implementar telas que exigem um controle de
transao do tipo Master/Detail.
travs do recurso de controle de transaes disponveis nos servidores de banco de
dados, possvel construir aplicaes que mantenham a unicidade lgica de um
conjunto de operaes. Muitas vezes necessrio que as atualizaes em vrias
tabelas sejam feitas em conjunto para que a integridade dos dados seja preservada.
Se qualquer problema for encontrado durante a transao, os dados devem retornar aos seus
valores originais antes do incio da transao.
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
Componente
Query1
Query2
Propriedade
Name
DatabaseName
SQL
Name
DatabaseName
SQL
Valor
QConsPed
DBVENDAS
SELECT PEDIDO.PED_CD , PEDIDO.PED_DT ,
PEDIDO.PED_VALOR , PEDIDO.PED_TIPO
FROM PEDIDO PEDIDO
ORDER BY PEDIDO.PED_CD
QConsPedCliente
DBVENDAS
SELECT PESSOA.PES_CD , PESSOA.PES_NM
FROM CLIENTE CLIENTE , PESSOA PESSOA
WHERE ( CLIENTE.PES_CD = PESSOA.PES_CD )
ORDER BY PESSOA.PES_NM
*Obs: Deixe a quarta linha em branco
Query3
Name
DatabaseName
SQL
QConsPedVend
DBVENDAS
SELECT PESSOA.PES_CD , PESSOA.PES_NM
FROM VENDEDOR VENDEDOR , PESSOA PESSOA
WHERE ( VENDEDOR.PES_CD = PESSOA.PES_CD )
ORDER BY PESSOA.PES_NM
Tabela de Propriedades
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
Componente
form1
DataSource1
DataSource2
DataSource3
Panel1
Edit...
button1
button2
Panel2
DBGrid1
Panel3
bitbtn1
bibtn2
Propriedade
Name
Caption
Name
DataSet
Name
DataSet
Name
DataSet
Align
Name
Name
Caption
Name
Caption
Align
Align
DataSource
Align
Kind
Kind
Valor
frmConsPedido
Pedidos
DSMain
DMSistVendas.QConsPed
DSCliente
DMSistVendas.QConsPedCliente
DSVend
DMSistVendas.QConsPedVend
alTop
edtNumero, edtCodCliente, edtNomeCliente,
edtCodVend, edtNomeVend
btnPesquisa
Pesquisa
btnNovo
Novo
alClient
alClient
DSMain
alBottom
bkOk
bkCancel
Tabela de Propriedades
Iremos propor aqui uma consulta um pouco diferente da apresentada na tela de consulta de
clientes e vendedores. Para escolher um cliente, o usurio poder digitar as primeiras letras
do nome, antes de abrir o Grid para consulta, tornando a pesquisa mais seletiva. Desta forma
evitaremos trazer todos os registros da tabela de cliente a estao de trabalho, alm de
permitir uma localizao mais rpida e precisa do registro pelo usurio. Com isso o usurio
utiliza melhor e se beneficia dos recursos fornecidos pelos servidores de banco de dados.
Delphi Client/Server
113
C O N T R O L A N D O
T R A N S A E S
M A S T E R / D E T A I L
Fig 9.3: Pesquisa de cliente filtrada pelas letras digitadas pelo usurio.
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
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
Componente
Query1
Query2
UpdateSQL1
UpdateSQL2
Query3
Propriedade
Name
DatabaseName
SQL
UpdateObject
CachedUpdate
Name
DatabaseName
SQL
UpdateObject
CachedUpdate
Name
Name
Name
DatabaseName
SQL
Valor
Qpedido
DBVENDAS
SELECT PEDIDO.PED_CD , PEDIDO.PED_DT ,
PEDIDO.PED_VALOR , PEDIDO.PED_TIPO ,
PEDIDO.PES_CD_CLI , PEDIDO.PES_CD_VEN ,
PESSOA.PES_NM
FROM PEDIDO PEDIDO , CLIENTE CLIENTE ,
PESSOA PESSOA
WHERE ( CLIENTE.PES_CD = PESSOA.PES_CD )
AND ( PEDIDO.PES_CD_CLI = CLIENTE.PES_CD )
AND (PEDIDO.PED_CD = :PED_CD)
ORDER BY
PEDIDO.PED_CD
USPedido
TRUE
Qitem
DBVENDAS
SELECT ITEM.PED_CD , ITEM.PRO_CD ,
ITEM.ITE_VALOR , ITEM.ITE_QUANT ,
PRODUTO.PRO_NM
FROM ITEM ITEM , PRODUTO PRODUTO
WHERE ( ITEM.PRO_CD = PRODUTO.PRO_CD )
and ( ITEM.PED_CD = :PED_CD)
ORDER BY ITEM.PRO_CD
USItem
TRUE
USPedido
USItem
QConsPedProd
DBVENDAS
SELECT * FROM PRODUTO
ORDER BY PRO_NM
Obs: Deixar a segunda linha em branco.
Tabela de Propriedades
Delphi Client/Server
117
C O N T R O L A N D O
T R A N S A E S
M A S T E R / D E T A I L
Deve-se criar tambm, um novo campo do tipo lookup em QPedido para permitir a
escolha do vendedor atravs de seu nome. Iremos nessa tela, utilizar o prprio recurso de
lookup do Delphi para a seleo do vendedor.
A tela de manuteno de pedidos dever ser construda como a figura abaixo. Pode-se notar
que sero apresentadas na mesma tela os dados das tabelas PEDIDO e ITEM para permitir
a manuteno integral dos dados do pedido.
Componente
form1
DataSource1
DataSource2
DataSource3
Panel1
SpeedButton1
SpeedButton2
SpeedButton3
SppedButton4
SpeedButton5
DBEdit...,
DBRadioGroup
DBGrid1
SpeedButton6
SpeedButton7
SpeedButton8
Delphi Client/Server
Propriedade
Name
Caption
Name
DataSet
Name
DataSet
Name
DataSet
Align
Name
Name
Name
Name
Name
DataSource
Valor
frmPedido
Pedidos
DSMain
DMSistVendas.Qpedido
DSDetail
DMSistVendas.QItem
DSCliente
DMSistVendas.QConsPedCliente
alTop
btnAppend
btnDelete
btnSave
btnDiscard
btnSearch
DSMain
DataSource
Name
Name
Name
DSDetail
btnDetailAppend
btnDetailDelete
btnDetailDiscard
118
C O N T R O L A N D O
T R A N S A E S
M A S T E R / D E T A I L
Tabela de Propriedades
Lgica visual
Antes de comear a definir os eventos dos botes, iremos criar um procedimento para
controlar a habilitao desses botes.
procedure TfrmPedido.EnableBtnControls;
begin
Case DSMain.State of
dsInactive:
begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
dsInsert:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsEdit:
begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end;
dsBrowse:
If TQuery(DSMain.DataSet).UpdatesPending then begin
btnAppend.Enabled:=FALSE;
btnDelete.Enabled:=FALSE;
btnSave.Enabled:=TRUE;
btnDiscard.Enabled:=TRUE;
btnSearch.Enabled:=FALSE;
end else begin
btnAppend.Enabled:=TRUE;
btnDelete.Enabled:=TRUE;
btnSave.Enabled:=FALSE;
btnDiscard.Enabled:=FALSE;
btnSearch.Enabled:=TRUE;
end;
end;
end;
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
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;
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;
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;
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
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
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
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;
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;
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;
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
Delphi Client/Server
128