106
CURSO POR GUINTER PAULI – CLUBE DELPHI Curso de dbExpress e DataSnap Parte I - Apresentação A idéia de construir este curso partiu na boa aceitação que tivemos em lançar o curso em 20 partes sobre acesso a dados no Delphi for .NET, com ADO.NET (Active Data Objects) e BDP (Borland Data Provider), que o leitor pôde acompanhar nos últimos meses aqui na ClubeDelphi. O curso também está disponível na forma de E-Book. A partir deste artigo, damos início um curso de acesso a dados usando o Delphi Win32, voltado para o ambiente client/server e multitier. Acredito que muitos de vocês desenvolvedores já possuem pelo menos uma aplicação desse tipo em produção. Que tal conhecer alguns recursos interessantes para aplicar rapidamente em suas aplicações, fazendo otimizações e garantindo performance? Este é o primeiro de uma série de 30 artigos que mostrará técnicas avançadas de uso das principais tecnologias de acesso a dados do Delphi Win32: dbExpress e DataSnap. Ao longo deste curso, você conhecerá importantes características e recursos de cada um dos componentes utilizados. Em especial, pretendo apresentar inúmeras dicas e segredos de utilização de um componente que considero ser o melhor já criado pela Borland: o ClientDataSet. Pretendo criar um curso bastante dinâmico: gostaria de elaborar tópicos de acordo com a necessidade e idéias de vocês, amigos leitores. Escrevam e sugiram tópicos que gostariam que fossem contemplados aqui no curso. Além disso, no final, pretendo colocar um grande documento de FAQs com perguntas e respostas, enviadas pelos leitores que farão o curso. Para criar os exemplos aqui demonstrados, você pode utilizar o Delphi 6, 7 ou o Delphi 2005. Como banco de dados, utilizarei o Interbase 7.5, mas você pode utilizar qualquer outro banco de sua preferência, como Firebird, SQL Server, Oracle, MySQL, todos os códigos apresentados funcionam perfeitamente com qualquer um desses SGDBs. Desejo um bom curso a todos, sugestões serão bem-vindas. Um abraço a todos e desejo sucesso nos projetos de banco de dados com Delphi Download Você pode fazer download de todos os exemplos deste curso a partir do endereço cc.borland.com/cc/ccweb.exe/author?authorid=222668 dbExpress, DataSnap e ClientDataSet: técnicas avançadas Para mais informações sobre acesso a dados no Delphi e técnicas avançadas, sugiro a leitura do meu livro, “Delphi: Programação para Banco de Dados e Web”, como apoio para o aprendizado das tecnologias. Na obra mostro várias técnicas introdutórios e avançadas de desenvolvimento com ClientDataSet, dbExpress e DataSnap (multicamadas, incluindo SOAP e COM+). Parte II - Conhecendo os componentes Nesta primeira parte do curso de dbExpress, vamos conhecer os principais componentes da paleta dbExpress e DataSnap do Delphi, vendo suas funcionalidades e objetivos. Componentes do dbExpress e DataSnap Os componentes dbExpress e DataSnap podem ser vistos na figura a seguir:

Db Express

Embed Size (px)

Citation preview

  • CURSO POR GUINTER PAULI CLUBE DELPHI

    Curso de dbExpress e DataSnap

    Parte I - Apresentao

    A idia de construir este curso partiu na boa aceitao que tivemos em lanar o curso em 20 partes sobre acesso a dados no Delphi for .NET, com ADO.NET (Active Data Objects) e BDP (Borland Data Provider), que o leitor pde acompanhar nos ltimos meses aqui na ClubeDelphi. O curso tambm est disponvel na forma de E-Book. A partir deste artigo, damos incio um curso de acesso a dados usando o Delphi Win32, voltado para o ambiente client/server e multitier. Acredito que muitos de vocs desenvolvedores j possuem pelo menos uma aplicao desse tipo em produo. Que tal conhecer alguns recursos interessantes para aplicar rapidamente em suas aplicaes, fazendo otimizaes e garantindo performance? Este o primeiro de uma srie de 30 artigos que mostrar tcnicas avanadas de uso das principais tecnologias de acesso a dados do Delphi Win32: dbExpress e DataSnap. Ao longo deste curso, voc conhecer importantes caractersticas e recursos de cada um dos componentes utilizados. Em especial, pretendo apresentar inmeras dicas e segredos de utilizao de um componente que considero ser o melhor j criado pela Borland: o ClientDataSet. Pretendo criar um curso bastante dinmico: gostaria de elaborar tpicos de acordo com a necessidade e idias de vocs, amigos leitores. Escrevam e sugiram tpicos que gostariam que fossem contemplados aqui no curso. Alm disso, no final, pretendo colocar um grande documento de FAQs com perguntas e respostas, enviadas pelos leitores que faro o curso. Para criar os exemplos aqui demonstrados, voc pode utilizar o Delphi 6, 7 ou o Delphi 2005. Como banco de dados, utilizarei o Interbase 7.5, mas voc pode utilizar qualquer outro banco de sua preferncia, como Firebird, SQL Server, Oracle, MySQL, todos os cdigos apresentados funcionam perfeitamente com qualquer um desses SGDBs. Desejo um bom curso a todos, sugestes sero bem-vindas. Um abrao a todos e desejo sucesso nos projetos de banco de dados com Delphi DownloadVoc pode fazer download de todos os exemplos deste curso a partir do endereo cc.borland.com/cc/ccweb.exe/author?authorid=222668 dbExpress, DataSnap e ClientDataSet: tcnicas avanadas Para mais informaes sobre acesso a dados no Delphi e tcnicas avanadas, sugiro a leitura do meu livro, Delphi: Programao para Banco de Dados e Web, como apoio para o aprendizado das tecnologias. Na obra mostro vrias tcnicas introdutrios e avanadas de desenvolvimento com ClientDataSet, dbExpress e DataSnap (multicamadas, incluindo SOAP e COM+).

    Parte II - Conhecendo os componentes

    Nesta primeira parte do curso de dbExpress, vamos conhecer os principais componentes da paleta dbExpress e DataSnap do Delphi, vendo suas funcionalidades e objetivos.

    Componentes do dbExpress e DataSnap

    Os componentes dbExpress e DataSnap podem ser vistos na figura a seguir:

  • Nesta primeira parte do curso, vamos conhecer brevemente cada um dos componentes envolvidos em aplicaes dbExpress. Nos artigos seguintes, vamos detalhar cada um deles.

    Viso geral dos componentes

    SQLConnection Esse componente responsvel pela conexo com o banco de dados.

    TSQLDataset Componente responsvel por obter dados de um servidor SQL usando cursores unidirecionais. Tambm pode executar uma procedure no servidor. Ele pode atuar tanto como uma Query, uma Table ou uma StoredProc.

    TSQLQuery Componente que fornece uma maneira de executar um comando SQL usando uma conexo dbExpress.

    TSQLStoredProc Usado para executar um procedure remoto no servidor SQL.

    TSQLTable Usado para representar uma tabela acessada atravs de uma conexo dbExpress.

    TSQLMonitor Monitora as trocas de mensagens e instrues SQL feitas entre uma aplicao cliente e um servidor SQL.

    TClientDataset Utilize TClientDataset para fornecer um mecanismo de cache para os Datasets unidirecionais. Por ser conectado a um TDatasetProvider, os dados podero ser capturados de um servidor de aplicao.

    TDatasetProvider

  • TDatatasetProvider prov dados de um Dataset e aplica as atualizaes feitas em um TClientDataset (delta) no servidor de dados. Ele responsvel por criar os pacotes de dados que trafegam entre uma aplicao cliente e um servidor de aplicao em uma arquitetura multicamadas. Ele pode se comunicar com um servidor de aplicao por meio da interface IAppServer.

    SimpleDataSet Esse componente o conjunto de quatro componentes, e facilita a conexo rpida com banco de dados, indicado para criao de aplicaes simples e prottipos.

    DCOMConnection Efetua uma conexo com um servidor de aplicao DataSnap, do tipo DCOM, MTS ou COM+

    SocketConnection Efetua uma conexo com um servidor de aplicao DataSnap, do tipo Sockets

    WebConnection Efetua uma conexo com um servidor de aplicao DataSnap, usando o protocolo HTTP. Seu uso no mais aconselhado, sendo prefervel o uso de um SOAPConnection para conexes DataSnap atravs da Web / HTTP.

    SimpleObjectBroker Permite criar um mecanismo simples de balanceamento de carga em servidores DataSnap. Por exemplo, ele pode despachar uma conexo cliente para um segundo servidor de aplicao se o primeiro servidor estiver congestionado.

    SharedConnection Permite acesso a um DataModule filho em um servidor de aplicao com mltiplos mdulos.

    LocalConnection Permite simular um ambiente multicamadas em um ambiente 2-tier, atravs de um mdulo compartilhado (DataModule). Com isso, ClientDataSets podem enxergar Providers em outras units, como se fosse uma camada fsica.

    ConnectionBroker Este componente tem por finalidade abstrair (isolar) o tipo de conexo para os ClientDatasets.

  • Se algum dia for preciso mudar o tipo de servidor, no seria necessrio reconfigurar os ClientDatasets caso se mudasse DCOMConnection para SOAPConnection, por exemplo.

    Parte III - Arquitetura

    Nesta parte do curso, vamos examinar detalhes sobre a arquitetura de aplicaes dbExpress que utilizam os componentes bsicos, tpicos de uma aplicao client/server ou multicamadas: SQLConnection, SQLQuery, DataSetProvider e ClientDataSet . Veremos tambm as vantagens em se utilizar uma aplicao multicamadas.

    Arquitetura

    A figura a seguir mostra as partes envolvidas em um sistema multicamadas. Na primeira camada, temos o chamado servidor de dados. Nele est localizado o banco de dados propriamente dito. O banco de dados pode ser Oracle, DB2, MySQL, Interbase ou qualquer outro suportado pelo dbExpress. Como a conexo feita via TCP/IP, voc pode usar um SO diferente do Windows, como Linux. A camada intermediria, conhecida como servidor de aplicao, contm o DataModule (tambm conhecido como RemoteDataModule ) responsvel pelo acesso a dados. Nessa camada se encontram os componentes dbExpress, como SQLConnection, SQLQuery, SQLDataSet e os DataSetProviders . Nessa camada voc tambm vai incluir as chamadas regras de negcio . Business Rules so regras impostas sobre dados, que no sero mais processadas no servidor SQL. Com isso, seu sistema consegue centralizar as regras sem depender do tipo de banco utilizado. E finalmente, a camada cliente responsvel apenas pela apresentao dos dados. Contm basicamente regras de tratamento de tela e interface. Por isso, so chamadas de thin-clients (clientes leves).

    A seguir, vamos examinar as vantagens de se desenvolver aplicaes multicamadas com DataSnap e dbExpress. Vantagens de uma soluo Multitier

    Modularizao da aplicao em 3 camadas

    Quando desenvolvemos uma aplicao Multicamadas com DataSnap, separamos a lgica de negcio e regras de acesso a banco de dados das regras de interface de usurio. Dessa forma, vrios clientes podem compartilhar as mesmas regras, que ficam encapsuladas em uma camada de acesso comum. Observe que em uma aplicao 2 camadas h geralmente uma redundncia de regra de negcio nas estaes clientes, e uma simples mudana nessa regra requer uma nova distribuio da aplicao. Suponha que voc esteja fazendo um simples cadastro de clientes, com interface tradicional baseada em formulrios. Ento voc inclui no

  • DataModule da aplicao uma determinada validao de dados. Se voc agora precisar construir uma verso On-Line deste cadastro, atravs de um formulrio que poder ser aberto em um browser Web, ento ser necessrio recodificar a regra anterior. Neste caso, uma camada lgica intermediria poderia centralizar todas as regras e atender tanto aplicaes clientes baseadas em formulrios, quanto aplicaes baseadas em browser.

    Clientes Leves (Thin-Clients)

    Uma aplicao cliente de uma arquitetura Multitier basicamente contm cdigo de interface. Todo o processamento das solicitaes de dados ao servidor SQL so feitas na camada intermediria, de forma que um cliente nunca se comunica diretamente com o banco de dados. Dessa forma, uma aplicao cliente DataSnap muito leve, pequena, e de configurao quase zero. Por este motivo este tipo de cliente tambm conhecido como Thin-Client ou cliente leve (magro).

    Economia de licenas de acesso a Banco de dados

    Quando trabalhamos com aplicaes 2 camadas, cada cliente de nossa aplicao se comunica diretamente com o banco de dados, por meio de um SQL Client. Este SQL Client um conjunto de bibliotecas (que muitas vezes ocupam vrios MB de espao em disco) que possibilita um terminal da rede trocar comandos SQL com um servidor de dados SQL. Voc precisa instalar estas bibliotecas em cada estao que acesse o servidor. Quando o driver do cliente troca de verso, voc precisa atualizar todas as mquinas da rede (isso sem falar da sua aplicao). Um outro aspecto nada interessante que geralmente as empresas fabricantes de banco de dados cobram licenas extras pela instalao destas bibliotecas clientes. Agora se voc construir seu sistema baseado em uma arquitetura Multitier, as bibliotecas do SQL Client (seja qual for o banco) devero ser instaladas somente na camada intermediria (camada de acesso a dados), poupando conexes no servidor e economizando licenas. Todos os clientes dessa forma compartilham uma nica conexo com o banco de dados SQL. Se seu aplicativo utiliza ainda o BDE como tecnologia Borland de acesso a dados, estes drivers tambm sero necessrio somente na aplicao intermediria, e no mais no cliente final. O mesmo vale para os drivers de acesso a dados do dbExpress.

    Escalabilidade

    Um software bem desenvolvido sobre uma arquitetura Multicamadas extremamente escalvel. Isto , a medida que mais clientes comeam a conectar no servidor de aplicao e utilizar seus recursos, no h uma perda de desempenho e performance, como acontece em aplicaes 2 camadas tradicionais.

    Independncia de Linguagem

    Uma camada de negcio construda sobre um padro COM, por exemplo, pode ser acessada por clientes escritos em diversas linguagens de programao que dem suporte ao COM. Essa comunicao possvel devido ao mecanismo conhecido como Interfaces. Um servidor COM (escrito em Delphi, por exemplo) pode ser acessado de um cliente escrito em VB, ASP, C, etc.

    Independncia de Banco de Dados

    Como uma aplicao cliente Datasnap no se comunica diretamente com o banco de dados, este no precisa nem mesmo saber qual o banco de dados utilizado. possvel migrar de Banco de Dados, por exemplo, de Interbase para Oracle, sem mesmo recompilar as aplicaes clientes (e as vezes nem mesmo a camada intermediria).

    Balanceamento de Carga

    Um servidor de aplicao pode ser configurado para automaticamente distribuir as conexes clientes para outros servidores de aplicao, tornando a arquitetura ainda mais escalvel. Isso facilmente alcanado apenas se usando o componente SimpleObjectBroker.

    Funcionamento de uma aplicao dbExpress

    A figura a seguir mostra uma aplicao tpica dbExpress, e todos os passos envolvidos desde a requisio de dados at a atualizaes das alteraes no servidor. Todos os procedimentos a

  • seguir acontecem imediatamente aps voc dar um Open em um ClientDataSet . Inclu tambm a especificao das etapas de atualizao. Conexo: o componente SQLConnection estabelece uma conexo TCP/IP com o servidor SQL; Abertura de um cursor de dados: um SQLDataSet ou SQLQuery efetua uma consulta ( select ) no banco de dados, por meio de um SQLConnection . O servidor SQL abre ento um cursor de dados, alocando recursos; DataSetProvider : aps o cursor ser aberto, o DataSetProvider varre o cursor de dados (uma operao while not eof ) montando um pacote de dados ( DataPacket ). Providing : o DataSetProvider fecha o cursor de dados, liberando recursos, e envia os dados obtidos em forma de DataPacket para o ClientDataSet , em uma operao chamada Providing . Cache : o ClientDataSet armazena os dados em cache, local, no sendo necessria nenhuma conexo com o banco de dados; ApplyUpdates - o ClientDataSet envia as atualizaes de volta ao DataSetProvider , em um pacote de dados conhecido como Delta ; Resolving para cada registro alterado, o DataSetProvider tenta atualizar o respectivo registro no banco de dados, gerando automaticamente instrues de atualizao como update, delete e insert . Reconcile eventuais erros ocorridos durante o Resolving so devolvidos ao ClientDataSet , para que sejam tomadas solues, em um processo chamado Reconcile .

  • Parte IV SQLConnection criando conexes

    Nesta parte do curso, vamos conhecer em detalhes o componente SQLConnection e criar nossa primeira conexo dbExpress.

    A classe TSQLConnection

    A classe TSQLConnection representa uma conexo dbExpress. Um SQLConnection faz uso de um driver para conectar um servidor de banco de dados. Os atuais bancos suportados so:

    DB2 MySQL SQL Server Informix Oracle InterBase

    O dbExpress no suporta bancos locais, como Paradox e DBase. Isso porque o dbExpress nada mais que uma casca fina sobre a API do banco de dados SQL. Como Paradox e DBase no so servidores SQL (SDBDs), no so suportados pelo dbExpress.Os driver e conexes so definidos em dois arquivos de configurao. O primeiro, dbxdrivers.ini em Windows ou os dbxdrivers em Linux, lista os drivers instalados e as bibliotecas (DLLs ou shared objects no Linux (so)) requeridos pela conexo. O segundo, dbxconnections.ini, em Windows ou os dbxconnections em Linux, lista as configuraes da conexo. Cada configurao representa um conjunto de parmetros de componentes TSQLConnection e descreve uma conexo com um banco de dados. Para criar uma configurao, basta dar um duplo clique em um componente TSQLConnection e usar editor para criar a conexo.

    SQLConnection - Principais Propriedades

  • ActiveStatements Nmero de comandos ativos sendo executados no banco de dados AutoClone Especifica se o componente automaticamente clona conexes com o banco de dados quando for necessrio Connected Indica se a conexo est ativa ConnectionName Nome da conexo no arquivo de configurao ConnectionState Indica o corrente estado da conexo DataSets Lista todos os DataSets ativos da conexo DriverName Indica o nome do driver associado conexo GetDriverFunc Indica a funo exportada na DLL do driver InTransaction Se uma transao est em progresso KeepConnection Indica se a conexo deve ficar ativa se no existirem DataSets abertos LibraryName Nome da DLL do driver dbExpress LoadParamsOnConnect Indica se o componente deve ler as configuraes dinamicamente a partir do dbxconnections.ini LocaleCode Indica se a ordenao em DataSets deve ser com base na localizao MaxStmtsPerConn Indica o nmero mximo de comandos ativos por conexo MetaData Permite acesso aos metadados do banco de dados MultipleTransactionsSupported Indica se o banco de dados suporta mltiplas transaes Params Lista os parmetros de conexo ParamsLoaded Indica se os parmetros foram lidos do dbxconnections.ini

  • SQLConnection Acesso ao objeto interno do driver dbExpress que representa a conexo SQLHourGlass Indica se o cursor de tela (SQL) deve ser usado durante processamentos TableScope Indica quais tipos de tabelas devem ser retornadas em operaes de metadados TransactionsSupported Indica se o banco de dados suporta transaes VendorLib Indica o nome da biblioteca cliente (DLL) do banco de dados

    SQLConnection - Principais Mtodos

    CloneConnection Retorna uma cpia do objeto de conexo CloseDataSets Fecha todos os DataSets associados conexo Commit Efetua um Commit em uma transao Create Cria uma instncia da classe TSQLConnection Destroy Destri a instncia do objeto Execute Executa um comando SQL no banco de dados ExecuteDirect Executa um comando SQL no banco de dados, que no possua parmetros GetDefaultSchemaName Retorna o default schema do objeto do banco de dados GetFieldNames Obtm a lista de campos de uma tabela, em um TStrings GetIndexNames Obtm a lista de ndices de uma tabela, em um TStrings GetLoginUsername Retorna o nome do usurio logado no BD GetPackageNames Obtm a lista de packages do BD, em um TStrings GetProcedureNames

  • Obtm a lista de stored procedures do BD, em um TStrings GetProcedureParams Obtm a lista de parmetros de stored procedures do BD, em um TStrings GetSchemaNames Obtm o nome de todos os objetos do BD, em um TStrings GetTableNames Obtm a lista de tabelas do BD, em um TStrings LoadParamsFromIniFile Carrega os parmetros de conexo a partir de um arquivo INI Rollback Efetua um RollBack em uma transao SetTraceCallbackEvent Define uma funo de callback que chamada para cada comando executado no servidor StartTransaction Inicia uma transao Close Fecha a conexo Open Abre a conexo

    SQLConnection - Principais Eventos

    OnLogin Permite definir parmetros de login (username e senha). TraceCallbackEvent Permite acesso a uma funo de callback que chamada para cada comando executado no servidor AfterConnect Disparado aps a conexo ser estabelecida BeforeConnect Disparado antes da conexo ser estabelecida AfterDisconnect Disparado aps a conexo ser fechada BeforeDisconnectDisparado antes da conexo ser fechada

    Criando uma conexo

    muito simples criar uma conexo dbExpress. Pressupondo que voc j tenha o Interbase rodando e previamente configurando (neste curso vou usar o IB 7.5), basta colocar um SQLConnection no form, dar um duplo clique sobre ele e acessar o editor de conexes:

  • No editor de conexes do dbExpress, clique no cone +. Escolha o driver do banco desejado e d um nome para a conexo:

    Informe no parmetro DataBase o caminho do banco de dados:

    Nota: O parmetro Database utilizado de diferentes formas, dependendo do driver. Para conexes IB / FB, preciso especificar o nome do host (servidor) juntamente com o caminho do banco, separados por :. errado tentar acessar o servidor usando algo do tipo \\servidor\c$\caminho\db.gdb. Nesse caso, o servidor seria a mquina local, mas acessando um arquivo em outra mquina usando compartilhamento de rede. Para o SQL Server, por exemplo, o nome do servidor e do banco so indicados em parmetros distintos (HostName e Database). Nesse caso, Database no o caminho fsico do banco no disco, mas o nome registrado no Enterprise Manager. Para o DB2, o Database deve indicar o Alias configurado no DB2 Client, que contm outras informaes de acesso, como o endereo do servidor, porta etc. Procedimento semelhante tambm feito para o Oracle.Essa configurao fica salva no arquivo dbxconnections.ini

  • dbxconnections.ini

    O dbExpress e o BDE so bastante semelhantes com relao aos parmetros utilizados em uma conexo. Como voc deve lembrar, o BDE guardava informaes sobre os aliases em um arquivo chamado idapi32.cfg, localizado normalmente em c:\Arquivos de Programas\Arquivos Comuns\Borland Shared\BDE. Esse arquivo no era manipulado diretamente, e sim utilizando-se o BDE Administrator a partir do Painel de Controle. Com isso, ficava muito simples alterar dinamicamente um parmetro de conexo, como o caminho do banco ou endereo do servidor, pois essas informaes ficavam externas aplicao. No era preciso recompilar nada caso fosse necessrio fazer alguma modificao nos parmetros de acesso. Tambm era possvel utilizar um componente Database. Em compensao, o BDE uma camada pesada de acesso a servidores SQL. Foi feito para o mundo duas camadas e possui um mecanismo de cache rudimentar. Freqentemente o desenvolvedor sentia-se obrigado a instalar quase 18 MB de DLLs para acesso a um BD. Ao contrrio, o dbExpress um engine leve de acesso, baseado na implementao de interfaces que acessam diretamente o driver cliente do BD, dispensado a instalao de bibliotecas adicionais. Tudo o que voc precisa distribuir a DLL indicada na propriedade LibraryName do SQLConnection, cujo tamanho varia entre 90 kb e 200 kb, dependendo do driver. Tambm necessrio distribuir a biblioteca Midas.dll, que na atual verso possui somente cerca de 290 Kb. Semelhante ao BDE, as informaes sobre as conexes criadas no dbExpress ficam em um arquivo de configurao, chamado dbxconnections.ini, localizado normalmente em c:\Arquivos de Programas\Arquivos Comuns\Borland Shared\dbExpress. Abra esse arquivo e veja que ele contm uma sesso chamada DB_IB, que define os parmetros da conexo que criamos no Delphi. Veja um trecho do arquivo a seguir (esses parmetros so os mesmos que esto atualmente na propriedade Params do SQLConnection): [DB_IB]DriverName=InterbaseDatabase=localhost:c:\caminho\db.gdbUser_Name=sysdbaPassword=masterkeySQLDialect=3... O arquivo dbxconnections.ini utilizado, at agora, apenas pela IDE do Delphi. Repare que cada conexo tem um parmetro chamado DriverName. O Delphi utiliza esse valor para configurar algumas propriedades do SQLConnection que so especficas do driver utilizado. Por exemplo, se o DriverName for Interbase, as propriedades GetDriverFunc, LibraryName e VendorLib tero os valores getSQLDriverINTERBASE, dbexpint.dll e gds32.dll, respectivamente. Esses valores mudam para o DB2, Oracle, SQL Server etc. Essas configuraes so obtidas atravs de um segundo arquivo de configurao, chamado dbxdrivers.ini. Dica: Para mais informaes sobre os arquivos de inicializao do dbExpress, consulte os tpicos dbxconnections.ini e dbxdrivers.ini na ajuda do Delphi.

    Testando a conexo

    Pronto, agora j podemos testar a conexo! Para isso, coloquei um Button no form e digitei: procedure TForm1.Button1Click(Sender: TObject);begin try try SQLConnection1.Open();

  • ShowMessage('Conexo feita com sucesso!'); except ShowMessage('Erro ao conectar'); end; finally if SQLConnection1.Connected then SQLConnection1.Close(); end;end; Lembre-se de definir o LoginPrompt como False. Execute e teste a aplicao.

    Parte V Introduo ao uso de DataSets Unidirecionais

    Neste artigo teremos uma pequena introduo ao conceito e uso de DataSets Unidirecionais do dbExpress, atravs dos componentes TSQLDataSet, TSQLTable e TSQLQuery.

    O que so DataSets unidirecionais

    A principal funo dos Datasets unidirecionais recuperar dados de um banco SQL. Eles no mantm nenhum buffer na memria ou criam qualquer tipo de cache, o que era comum em Datasets bidirecionais, como os usados no BDE. Por serem implementados desta forma, Datasets unidirecionais so bem mais rpidos, exigem pouco processamento e utilizam o mnimo de recursos da mquina, diferentemente dos Datasets TQuery e TTable baseados no BDE. Em Datasets unidirecionais no permitido: edio, campos lookup, filtros e navegao com prior e last. Ao tentar realizar operaes no permitidas sobre um Dataset unidirecional, uma exceo do tipo EdataBaseError levantada com a mensagem : Operation not allowed on a unidirectional dataset. At o BDE, quando o usurio rolava o cursor de dados na aplicao cliente, por exemplo, usando um DBNavigator, havia uma troca de mensagens com o kernel do BDE, que precisava manter o cursor alocado no servidor de banco de dados para permitir a navegao bidirecional. Isso poderia comprometer a performance de solues baseadas nessa arquitetura, visto que para atender n cliente simultaneamente, o BD precisava manter ativo os cursores alocados no servidor. Com o dbExpress, o tempo de transao e vida til do cursor de dados no servidor bastante

  • pequeno, devido ao uso de DataSetProviders e ClientDataSets. Quando damos um Open em um ClientDataSet, enviada uma solicitao para o DataSetProvider, que abre o Dataset unidirecional associado. O DataSetProvider varre ento o cursor aberto pela consulta e empacota os dados em um DataPacket, que alocado na memria do ClientDataSet. Nesse momento, o usurio pode trabalhar tranquilamente nos dados em tela, e o dbExpress pode fechar a consulta (cursor) que no ficar mais ativa (diferente do BDE). Por esse motivo, aplicaes dbExpress e DataSnap so extremamente rpidas.

    DataSets do dbExpress

    Os componentes da guia dbExpress possuem os mesmos ancestrais dos componentes baseados no BDE. Isso significa que muita coisa ser semelhante ao migrar de BDE para DBX, graas aos recursos de abstrao e polimorfismo, principalmente das classes TDataset, TField e TCustomConnection. Essas classes formam a base para os vrios componentes de acesso a dados, campos e conexo a bancos de dados disponveis no Delphi. Os DataSets do pacote dbExpress so chamados unidirecionais. Basicamente, este tipo de Dataset tem a funo de retornar dados de um servidor SQL, mas no de manipul-los (buffering). Para cada banco de dados que vamos acessar, o dbExpress fornece um driver especfico que deve ser distribudo juntamente com a aplicao. Todos os TDatsets usados no dbExpress herdam de TCustomSQLDataset. Todas as classes do dbExpress esto declaradas na unit SqlExpr.pas.

    Exemplo usando somente DataSets Unidirecionais

    possvel utilizar DataSets Unidirecionais diretamente, sem usar um ClientDataSet. Essa tcnica bastante utilizada, por exemplo, para confeco de relatrios. Ou seja, lemos um registro, fazemos alguma coisa com ele, e navegamos para o prximo, sem armazenar nada em memria. exatamente isso que mostrarei neste exemplo. Inicie uma nova aplicao VCL no Delphi.

  • Coloque um SQLConnection e um SQLDataSet no formulrio.

    No SQLConnection, configure uma conexo para o banco Employee do Interbase ou do Firebird (j discutimos conexes anteriormente, de forma que no vou entrar em detalhes aqui).

  • Aponte o SQLDataSet para o SQLConnection, atravs da propriedade Connection e em CommandText digite select * from customer

    Coloque um DataSource e aponte sua propriedade DataSet para o SQLDataSet. Neste momento, voc dever receber a seguinte mensagem de erro:

  • Isso acontece pois um DBGrid exige navegao bidirecional no cursor de dados, o que no suportado pelo dbExpress. Para isso, voc precisaria de um ClientDataSet (no usaremos ainda neste exemplo). Retire ento o DBGrid e coloque um ListView. No evento OnShow do formulrio digite: procedure TForm1.FormShow(Sender: TObject);var it: TListItem; i: integer;begin ListView1.ViewStyle := vsReport; SQLDataSet1.Open; try for i := 0 to pred(SQLDataSet1.Fields.Count) do with ListView1.Columns.Add do Caption := SQLDataSet1.Fields[i].FieldName; while not SQLDataSet1.Eof do begin it := ListView1.Items.Add; it.Caption := SQLDataSet1.Fields[0].AsString; for i := 1 to pred(SQLDataSet1.Fields.Count) do it.SubItems.Append(SQLDataSet1.Fields[i].AsString); SQLDataSet1.Next; end; finally SQLDataSet1.Close; end;end; Aqui no estamos usando nenhuma espcie de cache, fazendo uma navegao otimizada e unidirecional. Para cada registro do SQLDataSet, adicionamos os valores dos campos no ListView e a seguir navegamos para o prximo registro. A figura a seguir mostra o exemplo em execuo:

  • ISQLCursor

    Quando usamos DataSets do dbExpress diretamente, na verdade estamos trabalhando diretamente com o driver a ele associado. Essa interface dbExpress com o banco de dados feito atravs de drivers e interfaces, definidas na unit DBXpress.pas. Veja a seguir a definio da interface responsvel pela manipulao de cursores (se voc observar, ver que usamos alguns mtodos dessa interface no exemplo anterior): ISQLCursor = interface function SetOption(eOption: TSQLCursorOption; PropValue: LongInt): SQLResult; stdcall; function GetOption(eOption: TSQLCursorOption; PropValue: Pointer; MaxLength: SmallInt; out Length: SmallInt): SQLResult; stdcall; function getErrorMessage(Error: PChar): SQLResult; overload; stdcall; function getErrorMessageLen(out ErrorLen: SmallInt): SQLResult; stdcall; function getColumnCount(var pColumns: Word): SQLResult; stdcall; function getColumnNameLength( ColumnNumber: Word; var pLen: Word): SQLResult; stdcall; function getColumnName(ColumnNumber: Word; pColumnName: PChar): SQLResult;stdcall; function getColumnType(ColumnNumber: Word; var puType: Word; var puSubType: Word): SQLResult; stdcall; function getColumnLength(ColumnNumber: Word; var pLength: LongWord):SQLResult; stdcall; function getColumnPrecision(ColumnNumber: Word; var piPrecision: SmallInt): SQLResult; stdcall; function getColumnScale(ColumnNumber: Word; var piScale: SmallInt): SQLResult;stdcall; function isNullable(ColumnNumber: Word; var Nullable: LongBool): SQLResult; stdcall; function isAutoIncrement(ColumnNumber: Word; var AutoIncr: LongBool): SQLResult; stdcall; function isReadOnly(ColumnNumber: Word; var ReadOnly: LongBool): SQLResult; stdcall; function isSearchable(ColumnNumber: Word; var Searchable: LongBool):

  • SQLResult; stdcall; function isBlobSizeExact(ColumnNumber: Word; var IsExact: LongBool): SQLResult; stdcall; function next: SQLResult; stdcall; function getString(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getShort(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getLong(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getDouble(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getBcd(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getTimeStamp(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getTime(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getDate(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getBytes(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool): SQLResult; stdcall; function getBlobSize(ColumnNumber: Word; var Length: LongWord; var IsBlank: LongBool): SQLResult; stdcall; function getBlob(ColumnNumber: Word; Value: Pointer; var IsBlank: LongBool; Length: LongWord): SQLResult; stdcall;end; Essa interface, juntamente com ISQLCommand, ISQLConnection e ISQLDriver compem a arquitetura aberta do dbExpress. Se um desenvolvedor quiser criar um driver dbExpress para acessar um banco de dados no suportado nativamente, deve implementar essas interfaces.

    TCustomSQLDataSet

    A seguir, veremos as principais propriedades de TCustomSQLDataSet, que a classe base para todos os DataSets Unidirecionais do dbExpress. No listarei os membros herdados de classes mais altas, como TDataSet, focando nos membros introduzidos pela classe TCustomSQLDataSet: BlobBuffer Reserva buffer de memria para armazenar campos BLOB.

    CommandText Especifica o comando que o DataSet ir executar.

    CommandType Indica o tipo de comando passado no CommandText, que pode ser texto, o nome de uma tabela ou nome de uma stored procedure.

    CurrentBlobSize Tamanho do ultimo campo BLOB lido.

    DataLink Identifica o Datalink que gerencia a comunicao entre o DataSet e o DataSetMaster.

    DataSource Faz um link ente o DataSet e outro DataSet Master.

    DesignerData Armazena dados customizados.

    GetMetadata Especifica se o DataSet obtm metadatas do BD.

    IndexDefs Contm definies de todos os ndices definidos para o DataSet

    InternalConnection Indica o componente que conecta o DataSet ao BD.

    LastError Indica o ultimo erro SQL retornado pelo dbExpress.

  • MaxBlobSize Indica o nmero mximo de bytes retornados por campos BLOB.

    NativeCommand Representa o comando SQL que foi enviado ao servidor SQL.

    NumericMapping Configurao de mapeamento entre campos BCD.

    ParamCheck Especifica se a lista de parmetros para o Dataset reconfigurada quando a consulta muda.

    ParamCount Indica o nmero de parmetros do DataSet.

    Params Parmetros da consulta SQL.

    Prepared Indica se o comando est preparado para execuo.

    ProcParams Descrio dos parmetros de Stored Procedures.

    RecordCount Indica o nmero de registros obtidos pela consulta do DataSet.

    RowsAffected Indica o nmero de registros afetados pela execuo do ltimo comando no DataSet.

    SchemaInfo MetaDados do DataSet.

    SortFieldNames Ordem dos dados da consulta quando o CommandType for ctTable.

    SQLConnection Componente de conexo.

    TransactionLevel Nvel de isolamento de transao.

    Veja a seguir os principais mtodos do componente: CreateBlobStream Cria uma Stream para campos BLOB.

    GetBlobFieldData Recupera o valor corrente do campo BLOB no buffer.

    GetDetailLinkFields Lista os campos da relao Master / Detail.

    GetFieldData Recupera o valor corrente de um campo no buffer.

    GetKeyFieldNames Preenche uma lista com os nomes de todos os ndices do DataSet.

    GetQuoteChar Retorna o character usado em comandos SQL para manipular abertura e fechamento de strings.

    IsSequenced Indica se o DataSet pode usar nmeros de registros para indicar sua ordem.

    Locate Para busca de registros no DataSet, com limitaes imposta pelos cursores unidirecionais.

    Lookup Para busca de campos Lookup no DataSet, com limitaes imposta pelos cursores unidirecionais.

    ParamByName Captura um parmetro pelo nome.

    PrepareStatement Prepara a execuo do comando SQL.

    SetSchemaInfo Indica se o Dataset representa metadados do servidor e de que tipo.

    Veja a seguir os principais eventos do componente:ParseDeleteSql Ocorre quando a aplicao prepara para processar um comando DELETE armazenado na propriedade

    CommandText.ParseInsertSql Ocorre quando a aplicao prepara para processar um comando INSERT armazenado na propriedade

    CommandText.ParseSelectSql Ocorre quando a aplicao prepara para processar um comando SELECT armazenado na propriedade

    CommandText.ParseUpdateSql Ocorre quando a aplicao prepara para processar um comando UPDATE armazenado na propriedade

    CommandText.

    Parte VI Recuperando MetaDados

  • Nesta parte do curso, veremos como utilizar o dbExpress para recuperar informaes sobre objetos do banco de dados, como nome de tabelas, campos, ndices (o que chamamos de metadados).

    dbExpress e MetaDados

    No dbExpress, a interface responsvel pela obteno de metadados a ISQLMetaData, declarada na unit DBXpress.pas da seguinte forma: ISQLMetaData = interface function SetOption(eDOption: TSQLMetaDataOption; PropValue: LongInt): SQLResult; stdcall; function GetOption(eDOption: TSQLMetaDataOption; PropValue: Pointer; MaxLength: SmallInt; out Length: SmallInt): SQLResult; stdcall; function getObjectList(eObjType: TSQLObjectType; out Cursor: ISQLCursor): SQLResult; stdcall; function getTables(TableName: PChar; TableType: LongWord; out Cursor: ISQLCursor): SQLResult; stdcall; function getProcedures(ProcedureName: PChar; ProcType: LongWord; out Cursor: ISQLCursor): SQLResult; stdcall; function getColumns(TableName: PChar; ColumnName: PChar; ColType: LongWord; Out Cursor: ISQLCursor): SQLResult; stdcall; function getProcedureParams(ProcName: PChar; ParamName: PChar; out Cursor: ISQLCursor): SQLResult; stdcall; function getIndices(TableName: PChar; IndexType: LongWord; out Cursor: ISQLCursor): SQLResult; stdcall; function getErrorMessage(Error: PChar): SQLResult; overload; stdcall; function getErrorMessageLen(out ErrorLen: SmallInt): SQLResult; stdcall;end; Como voc pode notar pelos mtodos, podemos recuperar praticamente qualquer informao do catlogo do BD, como nomes de tabelas, tipos e nomes de campos, informaes sobre Stored Procedures e mais. Para usar essa interface, devemos usar o mtodo SetSchemaInfo de um DataSet do dbExpress.

    Aplicao usando MetaDados

    Inicie uma nova aplicao VCL no Delphi.

  • Figura 1.Coloque SQLConnection no e configure uma conexo para o banco Employee do Interbase ou do Firebird (j discutimos conexes anteriormente, de forma que no vou entrar em detalhes aqui).

    Figura 2.Coloque mais alguns componentes dbExpress e da paleta Data Access, conforme mostrado a seguir:

  • Figura 3.Configure o relacionamento entre os componentes da seguinte forma: object DBGrid1: TDBGrid DataSource = DataSource1endobject DBGrid2: TDBGrid DataSource = DataSource2endobject SQLQuery1: TSQLQuery SQLConnection = SQLConnection1endobject DataSetProvider1: TDataSetProvider DataSet = SQLQuery1endobject ClientDataSet1: TClientDataSet ProviderName = 'DataSetProvider1'endobject DataSource1: TDataSource DataSet = ClientDataSet1endobject SQLQuery2: TSQLQuery SQLConnection = SQLConnection1endobject DataSetProvider2: TDataSetProvider

  • DataSet = SQLQuery2endobject ClientDataSet2: TClientDataSet ProviderName = 'DataSetProvider2'endobject DataSource2: TDataSource DataSet = ClientDataSet2endNo evento OnCreate do formulrio digite o seguinte: procedure TForm1.FormCreate(Sender: TObject);begin SQLQuery1.SetSchemaInfo(stTables,'',''); ClientDataSet1.Open;end; E no evento OnDateChange do DataSource1 digite o seguinte: procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);var tb: string;begin ClientDataSet2.Close; tb := ClientDataSet1.FieldByName('TABLE_NAME').AsString; SQLQuery2.SetSchemaInfo(stColumns,tb,''); ClientDataSet2.Open;end; Com isso, exibimos no primeiro DBGrid as tabelas do banco de dados. Quando uma tabela for selecionada, exibimos informaes sobre suas colunas no segundo DBGrid. Execute a aplicao:

    Figura 4.

  • Parte VII ndices em memria A partir desta parte do curso, vamos conhecer algumas tcnicas avanadas de desenvolvimento com dbExpress e DataSnap, principalmente atravs do componente ClientDataSet.O exemplo apresentado neste artigo mostra como definir ndices em memria para o ClientDataSet. Para isso, basta setar a propriedade IndexFieldNames (para criar ndices mais personalizados use a propriedade IndexDefs e IndexName). No necessrio criar arquivos de ndices como no Paradox ou refazer a consulta SQL no banco.Inicie uma nova aplicao Delphi VCL e coloque no formulrio principal coloque um ComboBox, Label, ClientDataSet, DataSource e DBGrid, fazendo as ligaes como mostrado abaixo: object ClientDataSet1: TClientDataSetend object DataSource1: TDataSource DataSet = ClientDataSet1end object DBGrid1: TDBGrid DataSource = DataSource1end D um clique de direita no ClientDataSet, escolha Load from MyBase Table e abra o arquivo Customer.XML, localizado nos demos do Delphi, por padro no diretrio: C:\Arquivos de programas\Arquivos comuns\Borland Shared\Data Seu formulrio deve estar semelhante ao mostrado a seguir:

    Figura 1.No evento OnCreate do formulrio digite: procedure TFrmMain.FormCreate(Sender: TObject);begin ClientDataSet1.GetFieldNames(ComboBox1.Items);end; Isso preenche o Combo com a lista de campos disponveis no ClientDataSet.

  • Figura 2.No evento OnChange do Combo, ordenamos o ClientDataSet atravs do campo que o usurio selecionou: procedure TFrmMain.ComboBox1Change(Sender: TObject);begin ClientDataSet1.IndexFieldNames := ComboBox1.Text;end; Fazemos isso tambm no OnTileClick do DBGrid, para ordenar o DataSet conforme a coluna clicada no grid: procedure TFrmMain.DBGrid1TitleClick(Column: TColumn);begin if Assigned(OldColumn) then OldColumn.Title.Color := DBGrid1.FixedColor; ClientDataSet1.IndexFieldNames := Column.FieldName; Column.Title.Color := $00408080; OldColumn := Column; end; A figura a seguir mostra a aplicao em execuo, observe o efeito de cor que colocamos na coluna:

    Figura 3.

    Parte VIII DataSetFields Este exemplo mostra como utilizar DataSetFields em ClientDataSets. DataSetField um campo TField especial que pode representar o contedo de outro DataSet.Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm duas SQLQuery, um DataSetProvider, um ClientDataSet, dois DataSources, um DBGrid, um Button e um

  • DBNavigator.Relacione os componentes conforme mostrado a seguir: object SQLConnection1: TSQLConnectionend object SQLQuery1: TSQLQuery SQLConnection = SQLConnection1End object SQLQuery2: TSQLQuery SQLConnection = SQLConnection1End object DataSource1: TDataSource DataSet = SQLQuery1End object DataSetProvider1: TDataSetProvider DataSet = SQLQuery1End object DBGrid1: TDBGrid DataSource = DataSource2End object DBNavigator1: TDBNavigator DataSource = DataSource2End object ClientDataSet1: TClientDataSet ProviderName = 'DataSetProvider1'End object DataSource2: TDataSource DataSet = ClientDataSet1end Seu formulrio deve estar semelhante ao mostrado a seguir:

  • Figura 1.Configura a instruo SQL da primeira Query para: select * from CUSTOMER E da segunda para: select * from SALESwhere CUST_NO=:CUST_NO Configure o parmetro (propriedade Params) da segunda Query como mostrado a seguir:

    Figura 2.Adicione todos os TField no ClientDataSet, e observe que teremos um DataSetField:

  • Figura 3.Esse campo representa o DataSet detalhe. Com isso temos no mesmo DataSet acesso a tabela principal (master) e tabela detalhe (detail).No evento OnCreate do form digite: procedure TFrmMain.FormCreate(Sender: TObject);begin ClientDataSet1.Open;end; No evento OnClick do boto digite: procedure TFrmMain.BitBtn1Click(Sender: TObject);begin ClientDataSet1.ApplyUpdates(0);end; A figura a seguir mostra a aplicao em execuo.

    Figura 4.Navegue at a ltima coluna do DBGrid e observe que ela lista o DataSetField. Clique em [...] e note que uma outra DBGrid ser aberta automaticamente, mostrando dados da tabela detalhe, relacionados com o registro selecionado no DBGrid principal.

  • Figura 5.Note que usamos para isso somente um nico ClientDataSet. Alm disso, alteraes no DataSetField sero refletidos no DataSet principal (por isso s precisamos de um ApplyUpdates, que aplicar alteraes em ambas as tabelas no banco).

    Parte IX InternalCalc Este exemplo mostra como utilizar o recurso de InternalCalc em ClientDataSets, utilizado para otimizar campos calculados em memria. Faremos um exemplo que vai compara campos calculados tradicionais com InternalCalcs.O evento OnCalcFields de um DataSet uma armadilha. Ele , obviamente, utilizado para implementar campos calculados. O que poucos sabem que esse evento chamado a todo o momento, por exemplo, quando um valor de um campo muda, mesmo que esse campo no afete o valor do clculo. Se voc escrever um cdigo mais complexo nesse evento, como uma consulta ao banco de dados para obter um valor a ser usado no clculo (o que considero um suicdio), ver que a performance da sua aplicao cair consideravelmente. A soluo criar o campo como InternalCalc ao invs de Calculated. A seguir, no evento OnCalcFields, testamos se o estado (State) do DataSet dsInternalCalc antes de fazermos o processamento, como no exemplo: if ClientDataSet1.State = dsInternalCalc then ClientDataSet1CAMPO.Value := ... Com isso, o cdigo ser executado uma nica vez para cada registro, por exemplo quando h uma navegao ou quando um Post chamado.Para ver um exemplo prtico, coloque um ClientDataSet no formulrio e um DataSource. D um duplo clique no componente e clique de direita no editor, escolhendo New Field (vamos criar um DataSet de memria neste exemplo).Adicione o campo NUM1 como mostrado a seguir:

  • Figura 1.Adicione o campo NUM2 como mostrado a seguir:

    Figura 2.Adicione o campo DUMMY como mostrado a seguir:

  • Figura 3.Adicione o campo CALCULATED como mostrado a seguir (observe que ele ser um campo calculado):

    Figura 4.Adicione o campo INTERNALCALC como mostrado a seguir (observe que ele ser um campo InternalCalc):

    Figura 5.Arraste os campos criados para o formulrio, para que sejam criados os controles Data-Aware. Coloque tambm um DBNavigator apontando para o DataSource.Seu formulrio deve estar semelhante ao mostrado a seguir:

  • Figura 6.Neste exemplo, vamos processar o clculo de NUM1 e NUM2 e atribuir a ambos os campos calculados, sem cada um de um tipo (um internal e outro no). Para isso, no evento OnCalcFields do ClientDataSet, digitamos: procedure TFrmMain.ClientDataSet1CalcFields(DataSet: TDataSet);begin inherited; { calculado normal} ClientDataSet1CALCULATED.AsInteger := ClientDataSet1NUM1.AsInteger + ClientDataSet1NUM2.AsInteger; {internal calc} if ClientDataSet1.State = dsInternalCalc then ClientDataSet1INTERNALCALC.AsInteger := ClientDataSet1NUM1.AsInteger + ClientDataSet1NUM2.AsInteger;end; Execute a aplicao e digite dois valores, para NUM1 e NUM2:

    Figura 7.Para ver o campo em ao, coloque dois Brekpoints no cdigo conforme mostrado a seguir:

  • Figura 8.Agora digite um valor qualquer no campo DUMMY, observe que ele no afeta o clculo, pois no serve para nada (no participa da soma). Mesmo assim, note que o cdigo do campo calculado ser executado, mesmo que a soma no seja afetada por DUMMY. O processo no o mesmo para o campo InternalCalc (no segundo breakpoint), ele ser executado somente uma vez para fazer a soma, quando voc der o Post no DataSet (mais otimizado).

    Figura 9.

    Parte X UpdateStatus Este exemplo mostra como utilizar as propriedades UpdateStatus e UpdateFilter do ClientDataSet. Enquanto o State indica o estado de um DataSet inteiro, UpdateStatus representa o estado atual de um registro. StatusFilter permite exibir filtrar os registros de acordo com seu estado.Inicie uma nova aplicao Delphi VCL e coloque no formulrio principal um ComboBox e um ClientDataSet. D um clique de direita no ClientDataSet, escolha Load from MyBase Table e abra o arquivo Customer.XML, localizado nos demos do Delphi, por padro no diretrio:

  • C:\Arquivos de programas\Arquivos comuns\Borland Shared\Data Adicione os TFields no DataSet e adicione um campo calculado, chamado STATUS, conforme mostrado a seguir:

    Figura 1.Arraste os campos para o formulrio para criar os controles Data-Aware. Coloque tambm um DBNavigator apontando para o DataSource.Seu formulrio deve estar semelhante ao mostrado a seguir:

    Figura 2.No OnCreate do form, digite: uses TypInfo;...procedure TFrmMain.FormCreate(Sender: TObject);var i : integer;begin for i := 0 to 3 do ComboBox1.Items.Add(GetEnumName(TypeInfo(TUpdateStatus),I)); ComboBox1.Items.Add('Todos');end; Isso preenche o Combo com os possveis valores para a enumerao TUpdateStatus: TUpdateStatus = (usUnmodified, usModified, usInserted, usDeleted); TUpdateStatusSet = set of TUpdateStatus;

  • Figura 3.No evento OnCalcFields digite: procedure TFrmMain.ClientDataSet1CalcFields(DataSet: TDataSet);begin ClientDataSet1STATUS.AsString := GetEnumName(TypeInfo(TUpdateStatus),Integer(ClientDataSet1.UpdateStatus));end; Isso faz com que o campo STATUS indique o status atual do registro.Quando o usurio selecione um Status na combo, vamos exibir somente os registros que estejam naquele estado (excludo, inserido, modificado etc.). Isso feito no evento OnChange do Combo: procedure TFrmMain.ComboBox1Change(Sender: TObject);begin if ComboBox1.ItemIndex = 4 then ClientDataSet1.StatusFilter := [] else ClientDataSet1.StatusFilter := [TUpdateStatus(ComboBox1.ItemIndex)];end; Execute a aplicao, insira, modifique e exclua alguns registros, e a seguir escolha o filtro.

    Figura 4.

    Parte XI Data vs. Delta Este exemplo apresenta o uso das propriedades Data e Delta do ClientDataSet. Data um OLEVariant que armazena a cache de dados, Delta um OLEVariant que armazena as ALTERAES feitas em um ClientDataSet.Inicie uma nova aplicao Delphi VCL e coloque no formulrio principal coloque um Button, dois ClientDataSets, dois DataSource2, dois DBGridse um DBNavigator, fazendo as ligaes como mostrado abaixo: object ClientDataSet1: TClientDataSetend

  • object DataSource1: TDataSource DataSet = ClientDataSet1end object DBGrid1: TDBGrid DataSource = DataSource1end object ClientDataSet2: TClientDataSetend object DataSource2: TDataSource DataSet = ClientDataSet2end object DBGrid2: TDBGrid DataSource = DataSource2end object DBNavigator1: TDBNavigator DataSource = DataSource1end D um clique de direita no ClientDataSet, escolha Load from MyBase Table e abra o arquivo Customer.XML, localizado nos demos do Delphi, por padro no diretrio: C:\Arquivos de programas\Arquivos comuns\Borland Shared\Data Seu formulrio deve estar semelhante ao mostrado a seguir:

    Figura 1.No evento OnClick do boto capturamos o Delta do primeiro CDS e jogamos como Data do segundo CDS:

  • procedure TFrmMain.BitBtn1Click(Sender: TObject);begin if ClientDataSet1.ChangeCount > 0 then ClientDataSet2.Data := ClientDataSet1.Delta;end; Execute a aplicao. Faa algumas modificaes no primeiro DBGrid e observe que, ao clicar no boto, essas alteraes so registradas no segundo DBGrid:

    Figura 2.No exemplo anterior, modifiquei o campo Company de um registro. O CDS mantm os valores originais em Delta para serem utilizados no processo de Resolving do DataSetProvider, quando os campos precisam ser processados em instrues SQL de atualizao.

    Parte XII ClientDataSet e XML Neste artigo veremos alguns interessantes recursos do ClientDataSet para suporte a XML. Atravs de uma aplicao dbExpress tpica, vamos salvar os dados obtidos em XML para o disco, ler, examinar a estrutura do DataPacket e o XML em memria.O suporte a XML no se restringe simplesmente ao salvamento dos dados para o disco em um formato estruturado. A Borland inclui essa funcionalidade no componente para que possamos facilmente trafegar DataSets pela Web em formato XML, usando o SOAP (Simple Object Access Protocol).Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm duas SQLQuery, um DataSetProvider, um ClientDataSet, dois DataSources, um DBGrid, quatro Buttons, um DBNavigator, um Memo e um TWebBrowser (paleta Internet).Relacione os componentes conforme mostrado a seguir: object SQLConnection1: TSQLConnectionend object SQLQuery1: TSQLQuery SQLConnection = SQLConnection1

  • End object SQLQuery1: TSQLQuery SQLConnection = SQLConnection1End object DataSetProvider1: TDataSetProvider DataSet = SQLQuery1End object DBGrid1: TDBGrid DataSource = DataSource2End object DBNavigator1: TDBNavigator DataSource = DataSource2End object ClientDataSet1: TClientDataSet ProviderName = 'DataSetProvider1'End A instruo SQL da SQLQuery1 mostrada a seguir: select * from EMPLOYEE Seu formulrio deve estar semelhante ao mostrado a seguir:

  • Figura 1. O cdigo do boto Open mostrado a seguir: procedure TForm1.btnOpenClick(Sender: TObject);begin ClientDataSet1.Open;end; O cdigo do boto Save XML mostrado a seguir: procedure TForm1.Button2Click(Sender: TObject);begin ClientDataSet1.SaveToFile('c:\Employee.xml'); WebBrowser1.Navigate('file:///c:\Employee.xml');end; O cdigo do boto Load XML mostrado a seguir: procedure TForm1.Button3Click(Sender: TObject);begin ClientDataSet1.LoadFromFile('c:\Employee.xml');end; O cdigo do boto XML Data mostrado a seguir:

  • procedure TForm1.Button4Click(Sender: TObject);begin Memo1.Lines.Text := ClientDataSet1.XMLData;end; Execute a aplicao e clique nos botes Open, Save XML e XML Data. Observe o resultado na figura a seguir:

    Figura 2.A propriedade XMLData, somente-leitura, permite o acesso as dados em XML do ClientDataSet sem a necessidade de salvamento no disco, ideal para por exemplo trafegarmos informaes via Web Services / SOAP.O mtodo SaveToFile salva o DataPacket para o disco. Usamos um WebBrowser para exibir o XML dentro do form, atravs de um plugin do Internet Explorer usado pelo componente. Observe que voc pode expandir e reduzir nodes. Tambm possvel fazer isso no browser, claro:

  • Figura 3.Inclua, exclua, altere alguns registros no DBGrid e salve novamente o XML. Abra o arquivo para examinarmos seu formato. xml version="1.0" standalone="yes" ?> - - -

  • FIELDS> METADATA>- ... demais registros omitidos ROWDATA> DATAPACKET> Um DataPacket contm duas sees principais: MetaData e RowData. Na seo MetaData temos informaes que definem os campos do DataSet (Fields) e tambm os parmetros extras que inclumos no DataPacket (Params). Em RowData temos informaes sobre os registros, inclusive que foram modificados. Finalmente, execute a aplicao e carregue o DataSet sem efetuar a consulta ao BD, clicando em Load XML:

    Figura 4.Lembre-se que muitas operaes podem ser realizadas em tempo de design, clicando-se de direita sobre o ClientDataSet:

    Figura 5.Uma ltima dica que voc pode roubar dados de qualquer tipo de DataSet em designtime e jogar na memria do ClientDataSet. Por exemplo, coloque uma Table do BDE apontando para

  • DBDemos>Employee.DB. Coloque um ClientDataSet, d um clique de direita sobre ele escolha a opo Assign Local Data:

    Figura 6.

    Figura 7.Observe que os dados foram colocados no Data do CDS (veja o arquivo DFM abaixo):

  • Figura 8.

    Agora voc pode acessar os menus de contexto e salvar para XML fcil e rapidamente. Ideal para extrair dados de qualquer banco e salvar rapidamente em XML.

    Parte XIII Aplicaes desconectadas

    Neste exemplo, veremos como usar o dbExpress e ClientDataSet para obter dados do banco de dados, sem no entanto manter uma conexo sempre ativa com o mesmo. Isso far com que suas aplicaes se tornem mais escalveis e apresentem uma melhor performance quando o nmero de clientes aumentar. Para isso, vamos fazer o seguinte: abriremos a conexo e o cursor de dados somente o tempo mnimo necessrio para executar o select. A partir da, o DataSetProvider ir empacotar os dados em um DataPacket que ser armazenado no Data do ClientDataSet. A seguir, fechamos a consulta e a conexo, e o usurio poder continuar trabalhando normalmente (com dados em cache). Quando for necessrio aplicar os dados no servidor, abrimos novamente a conexo e enviamos o Delta.O mais interessante de tudo que o prprio DataSetProvider j realiza a maioria desse trabalho para ns: ele sabe exatamente o momento que se faz necessria uma conexo, se encarrega de abri-la, executar a query (que tambm aberta por ele) e assim por diante. Precisamos apenar configurar algumas propriedades. Vamos a prtica.Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm um SQLQuery, um DataSetProvider, um ClientDataSet, um DataSource, um DBGrid, dois Buttons e um Memo. Configure os componentes conforme o cdigo a seguir: object DBGrid1: TDBGrid

  • DataSource = DataSource1 end object DataSource1: TDataSource DataSet = ClientDataSet1 end object ClientDataSet1: TClientDataSet ProviderName = 'DataSetProvider1' end object DataSetProvider1: TDataSetProvider DataSet = SQLQuery1 end object SQLQuery1: TSQLQuery SQLConnection = SQLConnection1 end object SQLConnection1: TSQLConnection ConnectionName = 'EMPLOYEE' LoginPrompt = False Connected = False KeepConnection = False Params.Strings = ( 'DriverName=Interbase' 'Database=C:\Borland\InterBase\examples\database\employee.gdb' 'User_Name=sysdba' 'Password=masterkey' 'ServerCharSet=WIN1252' 'SQLDialect=3') end Seu formulrio deve estar semelhante ao mostrado a seguir:

  • Figura 1.

  • Figura 2.Observe como configurei as propriedades Connected e KeepConnection do SQLConnection, ambas para False. A SQLQuery tambm tem o Active configurado para False.O cdigo do boto Executar repassa para a SQLQuery a instruo SQL (Select) digitada no Memo: procedure TFrmMain.BitBtn1Click(Sender: TObject);begin SQLQuery1.SQL.Assign(Memo1.Lines); ClientDataSet1.Close; ClientDataSet1.Open;end; Execute a aplicao, digite uma instruo SQL e clique no boto Executar (veja a figura a seguir).

  • Figura 3.Quando fazemos a consulta, os seguintes procedimentos foram realizados pelo dbExpress:

    ClientDataSet (CDS) recebe a chamada ao Open; CDS chama o mtodo de interface As_GetRecords; DataSetProvider (DSP) recebe a chamada ao GetRecords; DSP abre a conexo (SQLConnection); DSP abre o cursor da SQLQuery; DSP varre os dados da consulta; DSP empacota os dados; DSP fecha o cursor (SQLQuery); DSP verifica a propriedade KeepConnection, como est False, fecha a conexo com

    o BD; DSP envia o DATA ao CDS; CDS fica com dados em memria.

    Mesmo com a aplicao em execuo, podemos comprovar que no h nenhuma conexo ativa com o servidor.

  • Figura 4.O boto Apply simplesmente chama o ApplyUpdates para atualizar o BD: o DSP se encarregar de abrir todas conexes necessrias e novamente, fechar aps a operao. Use o KeepConnection com cuidado: em um cenrio onde o cliente ir frequentemente enviar solicitaes (selects, updates etc.) ao BD, no uma boa soluo ter essa propriedade configurada. Ela ideal para quando o usurio ir trabalhar de forma desconectada, em um conjunto de dados maior, por um longo perodo de tempo.Outro lembrete: no use o Packet Records com essa abordagem, pois exige intensa comunicao com o servidor SQL. Pelo motivo anterior, essa combinao no faria sentido.

    Parte XIV Cache BDE x Cach ClientDataSet

    Neste exemplo vamos estudar um pouco sobre o mecanismo de cache do ClientDataSet. Aproveitarei para fazer alguns comparativos com o mecanismo de cache do BDE, para aqueles que esto migrando de soluo. Ou seja, a aplicao que preparei um comparativo entre o mecanismo de cache do BDE (Cache Updates) e a cache (Data) do ClientDataSet. Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm um SQLQuery, um DataSetProvider, um ClientDataSet, um DataSource, dois DBGrids e dois Buttons. Da mesma forma, configure um acesso BDE para o mesmo banco de dados. Os componentes so: DataBase, Query, UpdateSQL e DataSource. Configure-os como mostrado na listagem a seguir: object DBGrid1: TDBGrid DataSource = DataSource1 end object DBGrid2: TDBGrid DataSource = DataSource2 end object SQLConnection1: TSQLConnection

  • ConnectionName = 'EMPLOYEE' DriverName = 'Interbase' GetDriverFunc = 'getSQLDriverINTERBASE' LibraryName = 'dbexpint.dll' LoginPrompt = False Params.Strings = ( 'DriverName=Interbase' 'Database=C:\Borland\InterBase\examples\database\employee.gdb' 'RoleName=RoleName' 'User_Name=sysdba' 'Password=masterkey' 'ServerCharSet=' 'SQLDialect=3' 'BlobSize=-1' 'CommitRetain=False' 'WaitOnLocks=True' 'ErrorResourceFile=' 'LocaleCode=0000' 'Interbase TransIsolation=ReadCommited' 'Trim Char=False') VendorLib = 'gds32.dll' end object SQLQuery1: TSQLQuery SQL.Strings = ( 'select * from CUSTOMER') SQLConnection = SQLConnection1 end object DataSource1: TDataSource DataSet = ClientDataSet1 end object Database1: TDatabase DatabaseName = 'LOCAL' DriverName = 'INTRBASE' LoginPrompt = False Params.Strings = ( 'SERVER NAME=IB_SERVER:/PATH/DATABASE.GDB' 'USER NAME=SYSDBA' 'OPEN MODE=READ/WRITE' 'SCHEMA CACHE SIZE=8' 'LANGDRIVER=' 'SQLQRYMODE=' 'SQLPASSTHRU MODE=SHARED AUTOCOMMIT' 'SCHEMA CACHE TIME=-1' 'MAX ROWS=-1' 'BATCH COUNT=200' 'ENABLE SCHEMA CACHE=FALSE' 'SCHEMA CACHE DIR=' 'ENABLE BCD=FALSE' 'BLOBS TO CACHE=64' 'BLOB SIZE=32' 'WAIT ON LOCKS=FALSE'

  • 'COMMIT RETAIN=FALSE' 'ROLE NAME=' 'PASSWORD=masterkey') SessionName = 'Default' end object Query1: TQuery CachedUpdates = True DatabaseName = 'LOCAL' SQL.Strings = ( 'select * from CUSTOMER') UpdateObject = UpdateSQL1 end object UpdateSQL1: TUpdateSQL end object DataSetProvider1: TDataSetProvider DataSet = SQLQuery1 end object ClientDataSet1: TClientDataSet ProviderName = 'DataSetProvider1' end object DataSource2: TDataSource DataSet = Query1 end Seu formulrio deve estar semelhante ao mostrado a seguir:

  • Figura 1.Para ver como a cache funciona em ambos os engines, criei um mtodo que adiciona 5 mil registros em um DataSet passado como parmetro. Ele gera valores strings randomizados para cada ccampo da tabela: procedure TFrmMain.AppendRandomRecords(DataSet: TDataSet);var i : integer;begin DataSet.Open; for i := 1 to 5000 do begin DataSet.Append; DataSet.FieldByName('CUST_NO').AsInteger := I; DataSet.FieldByName('CUSTOMER').AsString := RandomStr; DataSet.FieldByName('CONTACT_FIRST').AsString := RandomStr; DataSet.FieldByName('CONTACT_LAST').AsString := RandomStr; DataSet.FieldByName('ADDRESS_LINE1').AsString := RandomStr; DataSet.FieldByName('ADDRESS_LINE2').AsString := RandomStr; DataSet.FieldByName('STATE_PROVINCE').AsString := RandomStr; DataSet.POST; Application.ProcessMessages; end;end;

  • No boto Iniciar de cada engine, chamamos o mtodo passando o respectivo DataSet (Query do BDE ou ClientDataSet do DataSnap): procedure TFrmMain.BitBtn1Click(Sender: TObject);begin AppendRandomRecords(ClientDataSet1);end; procedure TFrmMain.BitBtn2Click(Sender: TObject);begin AppendRandomRecords(Query1);end; Aps iniciar a insero de registros aleatrios, observe que a aplicao consome mais memria (Task Manager) quando se usa o CDS. Isso comprova que todas as alteraes e updates ficam no processo da aplicao, em memria.

    Figura 2.

  • Figura 3.No BDE, so criados arquivos no disco (veja dir. atual da aplicao) para armazenar as atualizaes, como comprova a figura a seguir.

  • Figura 4.

    Figura 5.Com isso, vemos que o DataSnap usa um mecanismo de cache muito mais inteligente e

  • efetivo. Aplicaes com DataSnap sero bem mais escalveis e rpidas. O BDE no foi feito e no est preparado para a criao de solues desse tipo, deficincia que foi suprida pelo dbExpress e ClientDataSet.

    Parte XV Packet Records

    Neste exemplo veremos como usar o recurso de Packet Records do ClientDataSet. Configurando essa propriedade, podemos instruir ao servidor de aplicao que empacote e envie dados por demanda. Quando temos um grande select no servidor, podemos instruir ao DataSetProvider que v enviando essas informaes aos poucos, para otimizar o trfego de dados. importante notar desde j, que essa abordagem menos escalvel: necessrio manter o cursor e conexo ativos durante todo o processo.Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm um SQLQuery, um DataSetProvider, um ClientDataSet, um DataSource, um DBGrid, um DBNavigator, um Memo e um SQLMonitor. Para configurar o recurso em tempo de execuo, coloque tambm um RadioGroup, dois CheckBoxes, um SpinEdit. A Figura a seguir mostra como ficou o form:

    Figura 1.

    A listagem a seguir mostra como cada componente foi configurado: object DBGrid1: TDBGrid DataSource = DataSource1 end object DBNavigator1: TDBNavigator DataSource = DataSource1 end object SQLConnection1: TSQLConnection ConnectionName = 'EMPLOYEE' DriverName = 'Interbase' GetDriverFunc = 'getSQLDriverINTERBASE' LibraryName = 'dbexpint.dll' LoadParamsOnConnect = True LoginPrompt = False Params.Strings = ( 'DriverName=Interbase' 'Database=C:\Borland\InterBase\examples\database\employee.gdb' 'RoleName=RoleName' 'User_Name=sysdba' 'Password=masterkey' 'ServerCharSet=' 'SQLDialect=3' 'BlobSize=-1' 'CommitRetain=False' 'WaitOnLocks=True' 'ErrorResourceFile=' 'LocaleCode=0000' 'Interbase TransIsolation=ReadCommited' 'Trim Char=False') VendorLib = 'gds32.dll'

  • Left = 72 Top = 232 end object SQLQuery1: TSQLQuery SQL.Strings = ( 'select * from CUSTOMER') SQLConnection = SQLConnection1 end object DataSetProvider1: TDataSetProvider DataSet = SQLQuery1 end object ClientDataSet1: TClientDataSet ProviderName = 'DataSetProvider1' end object DataSource1: TDataSource DataSet = ClientDataSet1 end object SQLMonitor1: TSQLMonitor SQLConnection = SQLConnection1 end No boto Refresh verificamos as configuraes de tela e configuramos o Packet Records do ClientDataSet: procedure TFrmMain.BitBtn2Click(Sender: TObject);begin Memo1.Lines.Clear; if CheckBox1.Checked then ClientDataSet1.PacketRecords := SpinEdit1.Value else ClientDataSet1.PacketRecords := -1; ClientDataSet1.Close; ClientDataSet1.Open;end; No OnChange do SpinEdit tambm mudamos essa configurao: procedure TFrmMain.SpinEdit1Change(Sender: TObject);begin ClientDataSet1.PacketRecords := SpinEdit1.Value;end; No boto GetNextPacket chamamos o mtodo de mesmo nome do ClienDataSet: procedure TFrmMain.BitBtn1Click(Sender: TObject);begin ClientDataSet1.GetNextPacket;end; No CheckBox com o nome de FetchOnDemand configuramos a propriedade de mesmo do ClienDataSet: procedure TFrmMain.CheckBox2Click(Sender: TObject);begin

  • BitBtn1.Enabled := not CheckBox2.Checked; ClientDataSet1.FetchOnDemand := CheckBox2.Checked;end; No CheckBox usar PacketRecords fazemos alguns ajustes visuais: procedure TFrmMain.CheckBox1Click(Sender: TObject);var i : integer;begin GroupBox1.Enabled := CheckBox1.Checked; Label1.Enabled := CheckBox1.Checked; SpinEdit1.Enabled := CheckBox1.Checked; CheckBox2.Enabled := CheckBox1.Checked; BitBtn1.Enabled := (CheckBox1.Checked) and not (CheckBox2.Checked);end; No evento OnLogTrace do SQLMonitor vamos monitorar a comunicao com o IB/FB para verificar quando e quais comandos esto sendo executados, jogando essas informaes para um Memo: procedure TFrmMain.SQLMonitor1LogTrace(Sender: TObject; CBInfo: pSQLTRACEDesc);begin Memo1.Lines.Add(String(CBInfo.pszTrace));end; Vamos executar a aplicao e fazer alguns testes, usando diferentes configuraes. Dessa forma, vamos entender exatamente como o PacketRecords funciona. 1 Sem usar PacketRecords - clique no boto Refresh e veja o comportamento na figura a seguir. Todos os dados retornados pelo Select foram empacotados e jogados na memria (Data) do ClienDataSet. Nenhum recurso do servidor, incluindo conexo e cursor, fica preso. No entanto, o tempo necessrio e trfego de rede usado para transferir o packet maior.

    Figura 2.2 Usando PacketRecords com FetchOnDemand ativado - clique no boto Refresh e veja o comportamento na figura a seguir. Configuramos o packet size como 3, o que indica que os registros sero empacotados de 3 em trs. O prprio ClientDataSet detecta quando mais registros so necessrio e traz por demanda (da o nome FetchOnDemand). Isso pode acontecer tanto por necessidade do usurio (confirme isso navegando no DBGrid para baixo) ou programaticamente (via chamadas subseqentes ao mtodo Next). Observe que cada Fetch envolve, lgico, uma chamada ao servidor SQL. A soluo mais otimizada para grandes resultsets, mais vai consumir mais recursos do BD, pois o cursor e conexo ficam presos aguardando novas solicitaes de packet.

    Figura 3.3 Usando PacketRecords com FetchOnDemand desativado - clique no boto Refresh e veja o comportamento na figura a seguir. Configuramos o packet size como 3, o que indica que os registros sero empacotados de 3 em trs. A diferena em no usar o FetchOnDemand que voc deve solicitar novos pacotes de dados, atravs do mtodo GetNextPacket do CDS (fizemos isso no boto). Faa um teste navegando at o ltimo registro do DBGrid, observe que ele trava no terceiro. Clicando no boto GetNextPacket, mais trs registros sero

  • trazidos. Cada Fetch envolve, lgico, uma chamada ao servidor SQL. A soluo mais otimizada para grandes resultsets, mais vai consumir mais recursos do BD, pois o cursor e conexo ficam presos aguardando novas solicitaes de packet.

    Figura 4.

    Parte XVI Save Point

    Neste artigo veremos como usar o interessante recurso de SavePoint do ClientDataSet. Esse recursos permite que voc tire um foto do atual status da memria do ClientDataSet, e recupere este status a qualquer momento. Voc pode, por exemplo, salvar as atuais alteraes do CDS em memria, fazer novas alteraes e a seguir desfaze-las, voltando ao estado original previamente sinalizado. Imagine isso como uma espcie de transaes em memria, com Rollback e Commit. Para ver como isso funciona na prtica, preparei um exemplo interessante.Coloque os componentes no formulrio conforme mostrado na figura a seguir. Aqui colocamos um ClientDataSet, um DataSource e trs Buttons. D um clique de direita no ClientDataSet, escolha Load from MyBase Table e abra o arquivo Customer.XML, localizado nos demos do Delphi, por padro no diretrio C:\Arquivos de programas\Arquivos comuns\Borland Shared\Data. Arraste os TFields para o form para criar os controles Data-Aware.

    Figura 1.O cdigo DFM do form mostrado a seguir: object ClientDataSet1: TClientDataSet Active = True object ClientDataSet1CustNo: TFloatField FieldName = 'CustNo' end object ClientDataSet1Company: TStringField FieldName = 'Company' Size = 30 end object ClientDataSet1Addr1: TStringField FieldName = 'Addr1' Size = 30 end object ClientDataSet1Addr2: TStringField FieldName = 'Addr2' Size = 30 end object ClientDataSet1City: TStringField FieldName = 'City' Size = 15 end object ClientDataSet1State: TStringField FieldName = 'State' end object ClientDataSet1Zip: TStringField FieldName = 'Zip'

  • Size = 10 end object ClientDataSet1Country: TStringField FieldName = 'Country' end object ClientDataSet1Phone: TStringField FieldName = 'Phone' Size = 15 end object ClientDataSet1FAX: TStringField FieldName = 'FAX' Size = 15 end object ClientDataSet1TaxRate: TFloatField FieldName = 'TaxRate' end object ClientDataSet1Contact: TStringField FieldName = 'Contact' end object ClientDataSet1LastInvoiceDate: TDateTimeField FieldName = 'LastInvoiceDate' endendobject DataSource1: TDataSource DataSet = ClientDataSet1endobject DBGrid1: TDBGrid DataSource = DataSource1endobject DBNavigator1: TDBNavigator DataSource = DataSource1end O cdigo do boto SavePoint salve o estado atual do ClientDataSet: procedure TFrmMain.BitBtn2Click(Sender: TObject);begin MyPoint := ClientDataSet1.SavePoint;end; MyPoint uma varivel declara no form, com o seguinte tipo: public MyPoint : integer; Para recuperar o estado, basta atribuir novamente essa propriedade ao SavePoint, observe (fizemos isso no outro boto): procedure TFrmMain.BitBtn3Click(Sender: TObject);begin ClientDataSet1.SavePoint := MyPoint;end; Para voltar ao ltimo estado, chamamos o UndoLastChanges no boto de mesmo nome:

  • procedure TFrmMain.BitBtn1Click(Sender: TObject);begin ClientDataSet1.UndoLastChange(true);end; Executando a aplicao, vamos fazer alguns testes para ver como recurso funciona. Altere o Company colocando TESTE1 ao final, clique em Post no DBNavigator e a seguir no boto SavePoint. Seguindo os mesmos passos, altere para TESTE2 e TESTE3, dando sempre um post e savepoint ao final.

    Figura 2.Agora clique vrias vezes no boto UnLastChanges e verifique que o estado atual de cada savepoint recuperado a cada chamado a mtodo, como transaes de BD, porm, tudo em memria.

    Parte XVII Record Count e RecNo

    Neste artigo veremos como usar as propriedades RecordCount e RecNo do ClienDataSet. Essas propriedades so exclusivas desse componente, sendo que possuam capacidades limitadas quando se trabalhava com BDE. RecordCount, por exemplo, sempre retornava -1 em Queries de consultas a banco de dados quando se usa o BDE. Essa limitao no existe no ClientDataSet, pois o mesmo possui todas as informaes em memria.

    RecordCount retorne o nmero atual de registros na memria do ClientDataSet, ou seja, o nmero de registros retornados pelo Select associado (a menos que se use Packet Records).

    RecNo a posio atual do cursor local de dados na memria do ClientDataSet. Por exemplo, se esse valor for 5, estamos navegando no quinto registro do ClientDataSet.

    Vejamos como isso funciona na prtica. Coloque os componentes no formulrio conforme mostrado na figura a seguir. Aqui colocamos um ClientDataSet, um DataSource, um DBGrid, um Button e um Label. D um clique de direita no ClientDataSet, escolha Load from MyBase Table e abra o arquivo Customer.XML, localizado nos demos do Delphi, por padro no diretrio C:\Arquivos de programas\Arquivos comuns\Borland Shared\Data.

    Figura 1.No boto simplesmente jogamos o valor de RecordCount no Caption do Label. procedure TFrmMain.BitBtn1Click(Sender: TObject);begin Label1.Caption := IntToStr(ClientDataSet1.RecordCount);end; Tambm criamos um campo calculado no ClientDataSet, como mostrado a seguir. Ele do tipo Integer e vai indicar a posio atual do registro no resulset, se comportando como se fosse um campo normal do BD (como cdigo, ID etc.)

    Figura 2.

  • Figura 3.

    No OnCalcFields atribumos o valor do campo com base no valor do RecNo atual do ClientDataSet: procedure TFrmMain.ClientDataSet1CalcFields(DataSet: TDataSet);begin ClientDataSet1RECNO.AsInteger := ClientDataSet1.RecNo;end; Observe ambos RecordCount e RecNo em ao na figura a seguir.

    Figura 4.Voc tambm pode fazer um DBGrid zebrado usado o RecNo, bastando verificar se o ndice do registro mpar o par. Isso no possvel no BDE, pois sempre retorna -1. Isso pode ser feito com o seguinte cdigo no evento OnDrawColumnCell do DBGrid: procedure TFrmMain.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);begin (*DBGrid zebrado*) if not odd(ClientDataSet1.RecNo) then // se for mpar if not (gdSelected in State) then // se a clula no est selecionada begin DBGrid1.Canvas.Brush.Color:= clYellow; // define uma cor de fundo DBGrid1.Canvas.FillRect(Rect); // pinta a clula DBGrid1.DefaultDrawDataCell(rect,Column.Field,State); // pinta o texto padro end;end; O resultado mostrado na figura a seguir:

    Figura 5.

    Parte XIX Usando interfaces

    Em DataSnap e qualquer arquitetura de objetos distribudos, faz-se uso intensivo de interfaces. importantssimo que voc saiba e compreenda como feita a comunicao entre cliente e servidor de aplicao, atravs do uso desse recurso. Interfaces so usadas amplamente nos bastidores de um arquitetura DataSnap.Dessa forma, decido incluir um captulo introdutrio neste curso, que mostra como usar interfaces puras (no relacionadas ao DataSnap, mas a POO). Entendendo o princpio bsico aqui proposto, ser base para entendermos abordagens mais complexas quando estudarmos o COM, MTS, COM+ e SOAP.Uma interface semelhante a uma classe que possua somente mtodos abstratos, ou seja, sem implementao. Uma interface apenas define mtodos que depois devem ser implementados por uma classe. Dessa forma, um objeto pode se comunicar com o outro apenas conhecendo a sua interface, que funciona como uma espcie de contrato.

  • Figura 1.Uma interface como se fosse um controle remoto. Voc consegue interagir com um objeto conhecendo o que ele oferece, tendo a interface que descreve cada funo, porm, sem a mnima idia de como ele implementa essa funcionalidade internamente.Assim como TObject a classe base para todas as classes do Delphi, a interface base para todas as interfaces IInterface. A interface base para todos as interfaces COM IUnknown. IUnknown na verdade apenas um alias para IInterface. Uma Interface no tem cdigo de implementao associado. Ateno - O uso de Interfaces um recurso da linguagem Delphi, de forma que voc poder utilizar interfaces mesmo que no esteja programando objetos distribudos. Veja a seguir a declarao de IInterface: IInterface = interface ['{00000000-0000-0000-C000-000000000046}'] function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function__Release: Integer; stdcall;end; Os mtodos _AddRef e _Release definem o mecanismo de contagem de referncia. Isso significa que voc no precisa liberar um objeto que implementa IInterface. QueryInterface faz solicitaes dinamicamente um objeto para obter uma referncia para as interfaces que ele suporta. Para demonstrar o uso de interfaces vamos criar um pequeno exemplo. Inicie uma nova aplicao no Delphi. Salve o formulrio como uFrmMain.pas e o projeto Interfaces.dpr. D o nome de FrmMain ao formulrio.Abra a unit do formulrio e declara a seguinte interface na seo type. type ICalc = interface function Multiplicar (const x,y : integer) : integer; end; Note que no podemos apertar Shift+Ctrl+C e implementar o mtodo Multiplicar. Esse um mtodo de interface, como um mtodo abstract de uma classe.Logo abaixo da interface declare a seguinte classe: TComputador = class (TInterfacedObject,ICalc) function Multiplicar (const x,y : integer) : integer;end; TCalculadora = class (TInterfacedObject,ICalc) function Multiplicar (const x,y : integer) : integer;end; TComputador = class (TInterfacedObject,ICalc) significa TComputador herda de TInterfacedObject e implementa a interface ICalc. Isso no herana mltipla. Observe que ambas as classes TComputador e TCalculadora implementam a interface ICalc, e descendem de TInterfacedObject. TInterfacedObject se encarrega de implementar a interface

  • IInterface.Aperte Shift+Ctrl+C para declarar os cabealhos dos mtodos. function TComputador.Multiplicar(const x, y: integer): integer;begin result:=x*y;end; function TCalculadora.Multiplicar(const x, y: integer): integer;var i : integer;begin result:=0; if (x0) and (y0) then for i:=1 to abs(y) do result:=result+x;end; Observe que ambas as classes implementam o mtodo Multiplicar de ICalc, porm de formas diferentes. Usando Edits, um RadioGroup e um Button construa o seguinte formulrio:

    Figura 2.D um duplo clique no boto e digite: procedure TFrmMain.Button1Click(Sender: TObject);var Obj : ICalc; n1,n2,r : integer;begin if RadioGroup1.ItemIndex=-1 then exit; case RadioGroup1.ItemIndex of 0 : Obj:=TCalculadora.create; 1 : Obj:=TComputador.create; end; n1:=StrToInt(Edit1.Text); n2:=StrToInt(Edit2.Text); r:=Obj.Multiplicar(n1,n2); Edit3.Text:=IntToStr(r);end; Rode e teste a aplicao.

  • Figura 3.

    Parte XX Objetos COM

    Neste artigo, veremos como criar e como usar objetos COM, um dos fundamentos mais bsicos da programao de objetos distribudos e DataSnap.COM (Component Object Model) a tecnologia desenhada pela Microsoft que possibilita a comunicao entre aplicaes clientes e aplicaes servidoras. Essa comunicao feita atravs do que chamamos de interfaces. Uma interface COM a maneira como um objeto expe sua funcionalidade ao meio externo. Um GUID (Globally Unique Identifier) um nmero utilizado no COM para identificar uma interface ou uma Co-Class. Quando utilizado para identificar uma interface, um GUID tambm chamado de IID (interface ID).Vamos ento criar nosso primeiro objeto COM. Siga os seguintes passos: Clique em File|New|Other. Na guia ActiveX clique em ActiveX Library. Uma nova biblioteca ser criada, que ser a DLL que conter nosso objeto COM. Salve esta biblioteca com o nome de LibExemplo.Clique novamente em File|New|Other. Na guia ActiveX escolha agora COM Object.

    Figura 1.Na caixa de dilogo que aparece, digite Soma para o nome da classe. O Delphi automaticamente preenche o nome da interface a ser implementada (ISoma). Deixe a opo

  • Multiple Instance como padro para Instancing (isso far com que uma nova instncia de nosso objeto seja criada para cada aplicao cliente). Na opo Threading Model deixe o padro Apartment (cada objeto COM executado dentro de seu prprio Thread).

    Figura 2.Clique em OK. Aparecer a Type Library do objeto COM. Salve a unidade criada com o nome de uSoma. Uma Type Library constitui a maneira de identificarmos os mtodos suportados por uma interface. O Delphi oferece um editor onde se pode facilmente construir uma interface para um objeto e atravs de cdigo Pascal implementar essa interface. Para visualizar o editor da Type Library de um objeto voc pode acessar o menu View|Type Library.No editor da Type Library, d um clique de direita sobre a interface ISoma. Escolha New|Method. D o nome de Somar para o mtodo. Em nosso primeiro exemplo, criaremos um funo que receber dois parmetro Single, retornando a soma de ambos.Na opo Return Type da guia Parameters, escolha Single. Clique em Add e insira dois parmetros (Num1 e Num2) do tipo Single. Salve tudo para o Delphi atualizar a unit de implementao. Tenha em mente que OLE e COM no oferecem suporte a todos os tipos de dados do Delphi.

  • Figura 3.Clique agora em View|Units e escolha LibExemplo_TLB. Voc ver uma extensa unidade Pascal que define a Type Library da interface criada. Logo no incio h uma declarao avisando a voc para no mudar este cdigo fonte. Qualquer modificao neste cdigo deve ser feita por meio do editor da Type Library.Procure pela seguinte declarao: ISoma = interface(IUnknown) ['{F1431B15-D645-4BC1-8F26-81B7BBF8D4C7}'] function Somar(Num1: Single; Num2: Single): single; stdcall;end; Esta a definio da nossa interface feita anteriormente no editor da Type Library. O que precisamos fazer agora codificar o mtodo Somar. Abra a unit uSoma e na seo implementation implemente a funo (o Delphi j colocou os cabealhos). function TSoma.Somar(Num1, Num2: Single): Single;begin result:=Num1+Num2;end; Agora basta compilar a biblioteca. Clique em Project|Build LibExemplo. Clique em Run|Register ActiveX Server para registrar o objeto.Vamos agora criar um cliente para nosso objeto COM criado anteriormente. Siga os passos abaixo: Clique em File|New Application. Salve a unit com o nome de uClienteCOM.pas e o projeto com o nome de ClienteCOM.dpr. D o nome de FrmMain e Caption Objetos COM ao formulrio. Coloque um boto no formulrio, com o Caption Somar, e trs Edits. Veja a figura:

  • Figura 4.O objetivo agora clicar no boto e chamar a funo Somar de nosso objeto COM, definido por nossa interface ISoma, implementada em nossa Co-Class TSoma. Para isso precisamos importar a Type Library do objeto que queremos instanciar. Clique em Project|Add to Project e localize o arquivo LibExemploLib_TLB. Isso faz com que a Type Library da interface ISoma seja incorporada ao nosso aplicativo. Mas veja bem, apenas a interface do objeto ser conhecida por ns, pois a sua implementao ficar oculta. Ns saberemos que ISoma possui o mtodo Somar mas no sabemos como ela realiza o processamento internamente.Agora no formulrio principal clique em File|Use Unit e escolha LibExemplo_TLB. Faa o seguinte no evento OnClick do boto: procedure TFrmMain.Button1Click(Sender: TObject);var Obj : ISoma; n1,n2,n3 : single;begin Obj:=CoSoma.Create; n1:=StrToInt(Edit1.Text); n2:=StrToInt(Edit2.Text); n3:=Obj.Somar(n1,n2); Edit3.text:=FloatToStr(n3);end; Execute e veja o resultado como na figura abaixo:

    Figura 5.Caso o servidor no tenha sido registrado uma exceo do tipo EOleSysError levantada com a mensagem Classe no registrada.

    Parte XXI Servidores de Aplicao e Remote Data Modules

    Utilizamos o DataModule para separar o cdigo de acesso a dados da interface de usurio. no DM que utilizamos os TDataSets, como a TSQLQuery e a TSQLTable, alm do componente TSQLConnection. Esses componentes so responsveis pela comunicao com o banco de dados (SGBDR). No DM geralmente tambm so implementados procedures que fazem validao sobre alguns dados, introduzem um campo padro ou um campo calculado. Quando compilamos nossa aplicao, todo o cdigo de acesso, clculos e regras ficaro residentes em nosso executvel. Se por algum motivo for necessrio mudar algum parmetro de consulta, alguma instruo SQL, um campo TField, uma mscara, alguma regra de dados, precisaremos recompilar toda a aplicao. E isso no tudo: os componentes dbExpress usam bibliotecas .dll que devem ser colocadas em todas as mquinas da rede que acessar o banco. Tambm devemos instalar e configurar os conhecidos SQL Clients (clientes de banco SQL), que so

  • bibliotecas especficas para cada SGBDR. Uma soluo separar o acesso em um Remote DataModule. O Remote DataModule nada mais do que um servidor COM, que basicamente tem a mesma funo de um DM, porm pode fazer uso dos recursos do DCOM. A grande vantagem de se usar um RDM que ele pode ficar totalmente separado do executvel principal da aplicao cliente, residindo em um outro processo, em um outro computador. Surge ento mais uma camada em nosso sistema. Temos a o que se chama de uma aplicao 3 camadas : o cliente, o RDM (constituindo o servidor de aplicao) e o servidor de dados (SGBDR). As aplicaes desenvolvidas utilizando esse tipo de tecnologia tambm so conhecidas como aplicaes MultiTier (Multicamadas).

    Figura1. Remote DataModuleAqueles procedures que colocamos em nosso antigo DM agora sero implementados dentro de nosso servidor DCOM (o RDM). O cliente conhecendo a interface do nosso objeto DCOM pode ento fazer chamadas a esses procedimentos residentes no RDM, atravs de uma RPC (Remote Procedure Call) usada pelo DCOM. O mecanismo que possibilita que clientes faam chamadas a funes de objetos residindo em um diferente processo ou em uma diferente mquina se chama marshaling.O RDM ser responsvel por gerenciar o acesso aos dados e as regras de negcio chamadas Business Rules (todas as validaes, imposies, clculos, constraints e acessos que envolvem os dados de uma aplicao). Os componentes de acesso do Delphi (BDE, ADO ou IBX) precisam estar somente no servidor de aplicao. As bibliotecas de clientes SQL tambm no sero necessrias em cada mquina cliente, somente no servidor de aplicao. O cliente s precisa levar consigo a biblioteca dbclient.dll se for feito em Delphi 4 e Midas.dll se feito em Delphi 5 ou superior. Todas as aplicaes clientes acessam a mesma camada (middle- tier), evitando redundncia de regras de negcio, que ficam encapsuladas em uma nica camada compartilhada. As aplicaes clientes ento contm no muito mais do que a interface com o usurio. Para o usurio final uma aplicao MultiTier no muito diferente de uma aplicao tradicional cliente-servidor. Os clientes passam a fazer apenas alguma manipulao de tela e alguma chamadas ao servidor de aplicao (por esse motivo so chamados Thin Clients). Os clientes NUNCA se comunicam diretamente com o servidor de banco de dados.

    Criando um Servidor de Aplicao DCOM

    Comece uma nova aplicao em Delphi clicando em File|New|Application. Salve a unit com o nome de uFrmMain.pas e o projeto como AppServerDCOM.dpr. D o nome de FrmMain ao formulrio, caption de Servidor DCOM Ativado e reduza o seu tamanho. Coloque-o no canto inferior direito da tela. Este formulrio apenas indicar que o servidor DCOM est ativado.

    Figura. Formulrio principal do servidor DCOMAgora clique em File|New|Other. No Object Repository, na guia MultiTier, escolha Remote DataModule.

  • Figura. Criando um Remote DataModuleNa caixa de dilogo que aparece, digite RDM para o nome da Co-Class. Deixe as opes Instancing e Threading Model como esto (estas so as mesmas opes disponveis quando criamos um objeto COM no primeiro exemplo). Salve a unit criada como uRDM.pas.Coloque um componente SQLConnection e configure o acesso ao banco EMPLOYEE.GDB do Interbase, normalmente situado em C:\Arquivos de programas\Arquivos Comuns\Borland Shared\data\employee.gdb. Defina seu LoginPrompt como False e Connected como True.

    Figura. Conectando ao Interbase a partir do Remote DataModule

    Coloque um SQLDataSet apontando para o SQLConnection e na sua propriedade CommandText digite: select * from CUSTOMER Coloque no RDM o componente TDataSetProvider (paleta DataAccess). Aponte sua propriedade DataSet para SQLDataSet. Voc deve usar um componente TDataSetProvider para cada TDataSet que utilizar no RDM.Vamos tambm incluir um procedure em nosso servidor, que ser chamado remotamente pelo cliente, que ter a funo de verificar se um determinado CPF vlido (note que a validao - uma regra de negcio - ficar totalmente separada do cliente, e se ela mudar, no precisaremos recompilar ou reinstalar clientes, alm do processamento de clculo da regra ser totalmente feito no servidor de aplicao). Importante: no confunda isso com um Stored Procedure dos servidores SQL que algo totalmente diferente.Abra o editor da Type Library clicando em View|Type Library. D um clique de