Curso de dbExpress e DataSnap

Parte V – Introdução ao uso de DataSets Unidirecionais

Neste artigo teremos uma pequena introdução ao conceito e uso de DataSets Unidirecionais do dbExpress, através dos componentes TSQLDataSet, TSQLTable e TSQLQuery.

O que são DataSets unidirecionais

A principal função dos Datasets unidirecionais é recuperar dados de um banco SQL. Eles não mantêm nenhum buffer na memória 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 são bem mais rápidos, exigem pouco processamento e utilizam o mínimo de recursos da máquina, diferentemente dos Datasets TQuery e TTable baseados no BDE. Em Datasets unidirecionais não é permitido: edição, campos lookup, filtros e navegação com prior e last. Ao tentar realizar operações não permitidas sobre um Dataset unidirecional, uma exceção do tipo EdataBaseError é levantada com a mensagem : “Operation not allowed on a unidirectional dataset”.

 

Até o BDE, quando o usuário “rolava” o cursor de dados na aplicação 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 navegação bidirecional. Isso poderia comprometer a performance de soluções 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 transação 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 solicitação para o DataSetProvider, que abre o Dataset unidirecional associado. O DataSetProvider “varre” então o cursor aberto pela consulta e “empacota” os dados em um DataPacket, que é alocado na memória do ClientDataSet. Nesse momento, o usuário pode trabalhar tranquilamente nos dados em tela, e o dbExpress pode fechar a consulta (cursor) que não ficará mais ativa (diferente do BDE). Por esse motivo, aplicações dbExpress e DataSnap são extremamente rápidas.

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, graças aos recursos de abstração e polimorfismo, principalmente das classes TDataset, TField e TCustomConnection. Essas classes formam a base para os vários componentes de acesso a dados, campos e conexão a bancos de dados disponíveis no Delphi.

 

Os DataSets do pacote dbExpress são chamados unidirecionais. Basicamente, este tipo de Dataset tem a função de retornar dados de um servidor SQL, mas não de manipulá-los (buffering). Para cada banco de dados que vamos acessar, o dbExpress fornece um driver específico que deve ser distribuído juntamente com a aplicação. Todos os TDatsets usados no dbExpress herdam de TCustomSQLDataset. Todas as classes do dbExpress estão declaradas na unit SqlExpr.pas.

image001.gif
Exemplo usando somente DataSets Unidirecionais

É possível utilizar DataSets Unidirecionais diretamente, sem usar um ClientDataSet. Essa técnica é bastante utilizada, por exemplo, para confecção de relatórios. Ou seja, lemos um registro, fazemos alguma coisa com ele, e navegamos para o próximo, sem armazenar nada em memória.

É exatamente isso que mostrarei neste exemplo. Inicie uma nova aplicação VCL no Delphi.

image002.png 

Coloque um SQLConnection e um SQLDataSet no formulário.

image004.png 

No SQLConnection, configure uma conexão para o banco Employee do Interbase ou do Firebird (já discutimos conexões anteriormente, de forma que não vou entrar em detalhes aqui).

image006.png 

Aponte o SQLDataSet para o SQLConnection, através da propriedade Connection e em CommandText digite

 

select * from customer

 

image008.png 

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

image010.png 

 

Isso acontece pois um DBGrid exige navegação bidirecional no cursor de dados, o que não é suportado pelo dbExpress. Para isso, você precisaria de um ClientDataSet (não usaremos ainda neste exemplo). Retire então o DBGrid e coloque um ListView.

 

No evento OnShow do formulário 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 não estamos usando nenhuma espécie de cache, fazendo uma navegação otimizada e unidirecional. Para cada registro do SQLDataSet, adicionamos os valores dos campos no ListView e a seguir navegamos para o próximo registro. A figura a seguir mostra o exemplo em execução:

image012.png 

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 através de drivers e interfaces, definidas na unit DBXpress.pas. Veja a seguir a definição da interface responsável pela manipulação de cursores (se você observar, verá que usamos alguns métodos 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 compõem a arquitetura aberta do dbExpress. Se um desenvolvedor quiser criar um driver dbExpress para acessar um banco de dados não 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. Não listarei os membros herdados de classes mais altas, como TDataSet, focando nos membros introduzidos pela classe TCustomSQLDataSet:

 

BlobBuffer

Reserva buffer de memória 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 comunicação 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 obtém metadatas do BD.

IndexDefs

Contém definições 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 número máximo de bytes retornados por campos BLOB.

NativeCommand

Representa o comando SQL que foi enviado ao servidor SQL.

NumericMapping

Configuração de mapeamento entre campos BCD.

ParamCheck

Especifica se a lista de parâmetros para o Dataset é reconfigurada quando a consulta muda.

ParamCount

Indica o número de parâmetros do DataSet.

Params

Parâmetros da consulta SQL.

Prepared

Indica se o comando está preparado para execução.

ProcParams

Descrição dos parâmetros de Stored Procedures.

RecordCount

Indica o número de registros obtidos pela consulta do DataSet.

RowsAffected

Indica o número de registros afetados pela execução do último comando no DataSet.

SchemaInfo

MetaDados do DataSet.

SortFieldNames

Ordem dos dados da consulta quando o CommandType for ctTable.

SQLConnection

Componente de conexão.

TransactionLevel

Nível de isolamento de transação.

 

Veja a seguir os principais métodos do componente:

 

CreateBlobStream

Cria uma Stream para campos BLOB.

GetBlobFieldData

Recupera o valor corrente do campo BLOB no buffer.

GetDetailLinkFields

Lista os campos da relação 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 números de registros para indicar sua ordem.

Locate

Para busca de registros no DataSet, com limitações imposta pelos cursores unidirecionais.

Lookup

Para busca de campos Lookup no DataSet, com limitações imposta pelos cursores unidirecionais.

ParamByName

Captura um parâmetro pelo nome.

PrepareStatement

Prepara a execução 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 aplicação prepara para processar um comando DELETE armazenado na propriedade CommandText.

ParseInsertSql

Ocorre quando a aplicação prepara para processar um comando INSERT armazenado na propriedade CommandText.

ParseSelectSql

Ocorre quando a aplicação prepara para processar um comando SELECT armazenado na propriedade CommandText.

ParseUpdateSql

Ocorre quando a aplicação prepara para processar um comando UPDATE armazenado na propriedade CommandText.

 

Download

Você pode fazer download de todos os exemplos deste curso a partir do endereço http://cc.borland.com/Author.aspx?ID=222668. É preciso fazer o cadastro na BDN, que é gratuito e pode ser feito a partir do endereço http://bdn.borland.com

 

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+). Para mais informações, consulte o link https://ssl.dominal.com/clubedelphi/loja/descricao.asp?codigo=114&cod_pai=6

Leia todos artigos da série