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.
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.
Coloque um SQLConnection e um SQLDataSet no formulário.
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).
Aponte o SQLDataSet para o SQLConnection, através 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 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:
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
- Curso de dbExpress e DataSnap
- Curso de dbExpress e DataSnap - Parte II
- Curso de dbExpress e DataSnap - Parte III
- Curso de dbExpress e DataSnap - Parte IV
- Curso de dbExpress e DataSnap - Parte V
- Curso de dbExpress e DataSnap - Parte VI
- Curso de dbExpress e DataSnap - Parte VII
- Curso de dbExpress e DataSnap - Parte VIII
- Curso de dbExpress e DataSnap - Parte IX
- Curso de dbExpress e DataSnap - Parte X
- Curso de dbExpress e DataSnap - Parte XI
- Curso de dbExpress e DataSnap - Parte XII
- Curso de dbExpress e DataSnap - Parte XIII
- Curso de dbExpress e DataSnap - Parte XIV
- Curso de dbExpress e DataSnap - Parte XV
- Curso de dbExpress e DataSnap - Parte XVI
- Curso de dbExpress e DataSnap - Parte XVII
- Curso de dbExpress e DataSnap - Parte XVIII
- Curso de dbExpress e DataSnap - Parte XIX
- Curso de dbExpress e DataSnap - Parte XX
- Curso de dbExpress e DataSnap - Parte XXI
- Curso de dbExpress e DataSnap - Parte XXII
- Curso de dbExpress e DataSnap - Parte XXIII
- Curso de dbExpress e DataSnap - Parte XXIV
- Curso de dbExpress e DataSnap - Parte XXV
- Curso de dbExpress e DataSnap - Parte XXVI
- Curso de dbExpress e DataSnap - Parte XXVII
- Curso de dbExpress e DataSnap - Parte XXVIII
- Curso de dbExpress e DataSnap - Parte XXIX
- Curso de dbExpress e DataSnap - Parte XXX