quarta-feira, 7 de março de 2018

DROP Wiki (English) #2 - Connecting to DBMSs

Você pode ler este post em português clicando aqui.

Continuing the DROP Wiki Series, the current topic is the connection establishment with a data source (usually a DBMS) and the choice of the best class to perform the task.

Further on, we will see that for the DROP ORM to work, a few prerequisites are necessary. Of them all, the most important is to create an object of TAqDBConnection, and, trough it, to establish a connection with the data source.

The TAqDBConnection class (in unit AqDrop.DB.Connection) is almost entirely abstract. This class goal is to abstract the minimum required to execute commands in the supported DBMSs. Obviously, this abstraction exists to provide the framework extension to different data access engines, being that we already have implementations for DBX and FireDAC.

Basically, the interfaces provided by TAqDBConnection are:
  - Transaction manipulation routines;
  - Command preparing (Prepare e Unprepare);
  - Cursor opening routines;
  - Command execution routines (usually DMLs);

The routines of transaction manipulation, command preparation and execution are not so different from the routines provided by any other data access engine (including third party components). The most significant difference is that of the cursor opening routines, which were built to provide interfaces similar to the readers  in DBX Framework, providing a code that is more suitable to O.O. characteristics (summing up: no DataSets ;-)

Aiming to split the specific code to support DBX and FireDAC, two classes were implemented and, as VCL style, both of them have the prefix 'Custom' on their names (TAqDBXCustomConnection e TAqFDCustomConnection), doing the heavy work so as to achieve their respective goals. Two direct inheritances, omitting the 'Custom' from their names (TAqDBXConnection e TAqFDConnection), have only the job of publishing the parameters provided by DROP and the mapped engines, in order to allow the complete configuration to access the desired DBMS.

Our first example will use Interbase as the DBMS and DBX as the internal engine, so, we will use the generic class TAqDBXConnection, as shown in the example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function TfrmDW0002.GetConnection
  (const pDatabasePath: string): TAqDBConnection;
var
  lConnection: TAqDBXConnection;
begin
  lConnection := TAqDBXConnection.Create;

  try
    lConnection.DriverName := 'InterBase';
    lConnection.VendorLib := 'GDS32.DLL';
    lConnection.LibraryName := 'dbxint.dll';
    lConnection.GetDriverFunc := 'getSQLDriverINTERBASE';
    lConnection.Properties[TDBXPropertyNames.Database] := pDatabasePath;
    lConnection.Properties[TDBXPropertyNames.Username] := 'SYSDBA';
    lConnection.Properties[TDBXPropertyNames.Password] := 'masterkey';
    lConnection.DBXAdapter := TAqDBXIBAdapter.Create;

    lConnection.Connect;
  except
    lConnection.Free;
    raise;
  end;

  Result := lConnection;
end;

The above example shows that to configuring a DROP connection or a TSQLConnection, for example, are similar tasks. However, a clear difference between the DROP connection and any other connection component provided by Delphi, is in the building of an adapter, which is necessary for a complete operation of the DROP features. The adapters tell DROP how to solve some tasks, or else, how to read some DBMS data types.

As the DROP aims to work more as a library  and less as a component, the above task can be simplified through the use of other set of classes, which easily wrap some distinctions of the supported DBMSs. Bellow we'll see the creation of a connection with the same database, however, using another class, which wraps DBX and the Interbase particularities.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function TfrmDW0002.GetConnectionBySpecificClass
  (const pDatabasePath: string): TAqDBConnection;
var
  lConnection: TAqDBXIBConnection;
begin
  lConnection := TAqDBXIBConnection.Create;

  try
    lConnection.DataBase := pDatabasePath;
    lConnection.UserName := 'SYSDBA';
    lConnection.Password := 'masterkey';

    lConnection.Connect;
  except
    lConnection.Free;
    raise;
  end;

  Result := lConnection;
end;

In the above example we can notice that the class TAqDBXIBConnection takes responsibility of configuring the properties as standards defines for Interbase, and exposes properties that are specific to IB. These properties allow us to statically setup some data like the GDB path, user and password, unlike the first example, where this information was dynamically provided.

Well, this was the example using the Interbase, and examples with other DBMSs simply mean a little bit of the same, talking specifically of the code. However, it is important at least to provide the list of specialized classes already available in DROP, each of them setting up the connection as the known standards of the supported DBMSs, and providing their particular properties. Here goes the complete list of classes, grouped by the data access engine:

DBX:
 - TAqDBXIBConnection;
 - TAqDBXMSSQLConnection;
 - TAqDBXMySQLConnection;
 - TAqDBXFBConnection;
 - TAqDBXOraConnection;
 - TAqDBXSQLiteConnection;

FD:
 - TAqFDIBConnection;
 - TAqFDMSSQLConnection;
 - TAqFDMySQLConnection;
 - TAqFDFBConnection;
 - TAqFDOraConnection;
 - TAqFDSQLiteConnection;
 - TAqFDPGConnection;

You may notice that FireDAC has an additional variation, which is PostgreSQL.

And, for the time being, that will be all. As a follow-up, we will have the episode #3, which will show how to use these objects, and in future episodes, how to use them inside the ORM aspect.

Until the next post!

quarta-feira, 28 de fevereiro de 2018

DROP Wiki #5 - Abstração de comandos SQL

Uma das premissas do DROP é fornecer interfaces uniformes para que o código seja escrito uma vez, e este opere sobre múltiplos SGBDs. O tema deste post é sobre um dos principais pilares do DROP para garantir a veracidade desta premissa, que são as interfaces de abstração de comandos SQL.

Sabemos que os recursos oferecidas pelos SGDBs e respectivas DMLs são muito parecidos. Todos entregam praticamente as mesmas funcionalidades, por estruturas de SQLs ligeiramente diferentes.

As units AqDrop.DB.SQL.Intf e AqDrop.DB.SQL fornecem, respectivamente, interfaces e classes que abstraem as funcionalidades de DML, sendo que as instâncias destas classes são traduzidas para a respectiva DML do SGBD apontado pela conexão do DROP que for executar o comando.

Vamos começar por algo simples: 'select * from country'. Sabemos que praticamente todo SGBD relacional aceitará este comando, desde que exista a uma tabela chamada country no esquema selecionado. O código abaixo demonstra como é simples usar as interfaces e classes de abstração de comandos SQL para executar o comando:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
procedure TfrmDW0005.TestSelect;
var
  lSelect: IAqDBSQLSelect;
  lReader: IAqDBReader;
begin
  MemoCountries.Lines.Clear;
  MemoCountries.Lines.Add('List of all registered contries:');

  lSelect := TAqDBSQLSelect.Create('country');

  lReader := FEmployeeConnection.OpenQuery(lSelect);
  while lReader.Next do
  begin
    MemoCountries.Lines.Add(lReader.Values['country'].AsString);
  end;
end;

Detalhes importantes sobre o código acima:
  • Note que a variável lSelect, que armazena a referência para o comando, é do tipo IAqDBSQLSelect;
  • Na linha 9, é instanciado um objeto do tipo select, onde a origem deste select (utilizada na cláusula from) é uma tabela de nome country (a origem pode ser também uma view, uma string contendo um subselect, ou um subselect na forma de outra interface do tipo IAqDBSQLSelect);
  • Não é informada nenhuma coluna de retorno no select, logo, o DROP assume o * como lista de colunas a serem retornadas;
  • Tanto o comando OpenQuery quanto o funcionamento do reader foram apresentados no DROP Wiki #3, mas na oportunidade, a sobrecarga utilizada do comando foi um select escrito manualmente em uma constante string. Já neste caso, estamos vendo o uso da sobrecarga que recebe uma interface do tipo IAqDBSQLSelect (linha 11);
  • A partir do início do while segue a mesma lógica já apresentada no Wiki #3.
Vamos partir agora para um comando que continua simples, mas que já exige um select diferente para (quase) cada um dos SGDBs suportados, que é a limitação da quantidade de registros que devem ser retornados pelo select. Segue novo método que retorna no máximo 3 registros da tabela country:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
procedure TfrmDW0005.TestSelectLimited;
var
  lSelect: IAqDBSQLSelect;
  lReader: IAqDBReader;
begin
  MemoCountries.Lines.Clear;
  MemoCountries.Lines.Add('List of up to 3 countries:');

  lSelect := TAqDBSQLSelect.Create('country');
  lSelect.Limit := 3;

  lReader := FEmployeeConnection.OpenQuery(lSelect);
  while lReader.Next do
  begin
    MemoCountries.Lines.Add(lReader.Values['country'].AsString);
  end;
end;

Ao executar o comando acima, o DROP traduz a informação para os recursos nativos de cada um dos SGDBs, limitando a quantidade de registros retornados.

Visando uma melhor performance do comando, vamos tornar explícito que o select deve trazer somente a coluna country de cada registro, e somente para ampliar os recursos demonstrados, vamos fazer com que esta coluna seja retornada com o alias 'country_name'. Segue método demonstrando as alterações necessárias (para deixar claro, o alias não é obrigatório ao definir uma coluna):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
procedure TfrmDW0005.TestSelectWithColumnAndAlias;
var
  lSelect: IAqDBSQLSelect;
  lReader: IAqDBReader;
begin
  MemoCountries.Lines.Clear;
  MemoCountries.Lines.Add('List of up to 3 countries:');

  lSelect := TAqDBSQLSelect.Create('country');
  lSelect.AddColumn('country', 'country_name');
  lSelect.Limit := 3;

  lReader := FEmployeeConnection.OpenQuery(lSelect);
  while lReader.Next do
  begin
    MemoCountries.Lines.Add(lReader.Values['country_name'].AsString);
  end;
end;

Também no DROP Wiki #3 comentei que o objetivo de um ORM não é fazer com que o usuário escreva manualmente os comandos SQL. Pois bem, o objetivo do DROP também não é fazer o desenvolvedor criar os próprios comandos de SQL através destas interfaces e classes. Então, por que fiz questão de passar por este conteúdo antes de chegar no ORM propriamente dito?

Eventualmente (ou algo com frequência muito maior que eventualmente), teremos que customizar os comandos que serão disparados contra o SGDB. Para fazer esta customização, o DROP conta diversas variações e recursos, no entanto, a variação mais indicada passa por conhecermos as interfaces de abstração de SQLs.

Mais adiante veremos que o ORM do DROP pode nos retornar objetos instanciados e configurados para as interfaces dos tipos IAqDBSQLSelect, IAqDBSQLInsert, IAqDBSQLUpdate e IAqDBSQLDelete. Manipular estas referências já devidamente configuradas é a maneira mais rápida e simples para customizar os comandos enviados para o SGDB.

Através destas interfaces, é possível realizar uma série de customizações, como adicionar cláusulas where, joins (use com moderação, afinal você está em um ambiente que visa o uso do ORM ;-), funções de agregação de dados, ordenação, etc. E sempre que for necessário mudar o texto resultante do comando de DML, o DROP faz isso por você.

Segue uma última variação do método de busca de países, onde uma cláusula where é adicionada, trazendo somente países em que a moeda seja o Euro:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
procedure TfrmDW0005.TestSelectWithCondition;
var
  lSelect: IAqDBSQLSelect;
  lReader: IAqDBReader;
begin
  MemoCountries.Lines.Clear;
  MemoCountries.Lines.Add('List of countries where the currency is Euro:');

  lSelect := TAqDBSQLSelect.Create('country');
  lSelect.CustomizeCondition.AddColumnEqual('currency', 'Euro');

  lReader := FEmployeeConnection.OpenQuery(lSelect);
  while lReader.Next do
  begin
    MemoCountries.Lines.Add(lReader.Values['country'].AsString);
  end;
end;

Sobre o método acima, é importante dizer que foi utilizado o método AddColumnEqual, que monta uma condição com o operador de igualdade entre valores, mas existem diversos outros métodos que mudam o operador a ser utilizado. Cada um destes métodos possui várias sobrecargas, sendo que cada uma das sobrecargas permite o uso de tipos primitivos para seus respectivos fins, e o DROP traduz o tipo primitivo do Delphi para o respectivo tipo primitivo do SGDB, se este existir.

Por exemplo, alguns SGDBs trazem suporte nativo ao tipo Boolean, no entanto, o uso deste tipo varia de SGDB para SGDB. Em alguns deles a constante True se escreve desta forma, em outros se escreve como se fosse uma string ('True'), e em outros ela sequer existe. Para todos estes casos, o DROP faz a devida tradução para a constante do SGDB utilizado (ou para o formato padrão, como no caso de datas), e para os SGDBs sem suporte a esta constante, o DROP propõe outras como 1 ou 'T'. E se você não estiver satisfeito com estas propostas, você ainda pode informar ao DROP como traduzir cada um dos tipos de dados do Delphi para os tipos de dados do seu SGDB.

Finalizando, segue uma pequena demonstração destas variações de métodos e suas sobrecargas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
procedure TfrmDW0005.ShowSQL;
var
  lSelect: IAqDBSQLSelect;
begin
  lSelect := TAqDBSQLSelect.Create('any_table');

  lSelect.CustomizeCondition.AddColumnEqual('intcolumn', 1).
    AddColumnEqual('boolcolumn', True).
    AddColumnGreaterEqualThan('datecolumn', EncodeDate(2018, 2, 28)).
    AddColumnIsNull('nullablecolumn');

  ShowMessage(FEmployeeConnection.Adapter.SQLSolver.SolveSelect(lSelect));
end;

E o resultado obtido (formatado para melhor leitura):

1
2
3
4
5
6
7
8
9
select
  *
from
  any_table
where (
  intcolumn = 1 and
  boolcolumn = True and
  datecolumn >= '2018.02.28 00:00:00:000' and 
  nullablecolumn is null)


E para o momento era isto! Em algum momento da série voltaremos às interfaces de abstração para cobrir todas as variações implementadas, mas acredito que o conteúdo até aqui descrito já seja suficiente para orientar o usuário do DROP, mostrando de onde estas interfaces vêm, como vivem, como se reproduzem, e do que se alimentam.

Até o próximo episódio!

Episódio Anterior

terça-feira, 30 de janeiro de 2018

DROP Wiki (English) #1 - Introduction

Você pode ler este post em português clicando aqui.

With the main goal of providing large documentation about the DROP, I'll start the series DROP Wiki, and as lots of conversations about the framework start with the question "What is the DROP?", nothing better than to start the series by trying to answer this question.

Using the shortest possible answer, the DROP is a set of classes built in Delphi, to make the daily tasks of the developer easier. A bunch of these classes have been developed to support their own ORM framework (object relational mapping), and it is mainly for fulfilling this goal that the DROP is best known.

Another great thing about the DROP is that it is a totally free and open source (MIT license), and if you are already interested in downloading or using it, you can do it through GitHub, or by downloading the source in a compacted file using this link. Now, lets break the DROP down a little further.

I remember that, while developing/rewriting the DROP, for many times I have talked with my teammates at Aquasoft, or among other developers (as my friend Muka), and the subject of these conversations was the reason of the DROP having been divided into some layers, each of them with one responsibility. Understanding these layers can make the understanding of the whole DROP easier:
  • Core: contains useful classes for several tasks, with generic development purposes. On this layer there's nothing implemented to access DBMSs. To tell you the truth, some classes are not even used directly or indirectly in ORM tasks;
  • DB: the classes contained here abstract the communication with DBMSs, and therefore, many of these classes are abstract as well, providing uniform structures, that further allows to DROP write code once, however compatible with the supported DBMSs. Even though on this level there aren't concrete classes to access data, there are lots of specific classes to some DBMSs, like the classes that solve the SQL objects, translating them into the specific language of each of the supported DBMSs;
  • ORM: contains the ORM classes, proper. They transform classes structures and their attributes to SQL objects, as enable those entities objects to be persisted and retrieved from the DBMSs;
  • ORM Base (I still think that this name needs a facelift from a marketing department ;-): the ORM layer doesn't requier that the classes to be mapped inherit from the DROP classes, however, the DROP provides, by this layer, some classes that can be extended, reducing the developer work while doing the ORM.
The Core layer presents all its units in a namesake package, being that all other layers are implemented in a package called DB. It is important to emphasize that all classes contained in these packages are free from specific any data access engines.

To perform the physical data access, the DROP can work with both DBX Framework or FireDAC. It is up to the developer to use one technology or the other. Therefore, each of the implementation of the DROP to the above technologies has received its own package.

Talking about physical data access, the DROP currently supports seven relational DBMSs:
  • Interbase;
  • Oracle;
  • Microsoft SQL Server;
  • PostgreSQL (only by FireDAC);
  • MySQL;
  • Firebird;
  • SQLite (ok, not really a DBMS, but...).
Concluding, if it were possible to summarize the above content in an image, it would be something like this:



Finishing this post, the sequence of these serie are still being written (some of the Portuguese posts are already published, and I'll work in the translation ASAP). The next posts will cover the above topics alternating topics between all the DROP layers.

Thank you for reading! See ya!

terça-feira, 7 de novembro de 2017

DROP Wiki #4 - Transações

O post de hoje é extremamente simples, mas sobre algo muito importante: Transações!




O sistema de transações do DROP trabalha com o conceito de chamadas cumulativas. E o que isto quer dizer?

Nota: O exemplo a seguir é uma versão resumida do que podemos encontrar em produção, mas demonstra bem o conceito do sistema de transações do DROP (não queime neurônios discutindo se você resolveria com uma estrutura de classes diferentes ou não, se concentre no recurso apresentado).


Começaremos com uma classe de nome TProduto. Esta possui um método que, entre outros, trata a movimentação do estoque deste produto. Toda movimentação deve atualizar o campo de saldo de estoque do produto, e também incluir um registro de detalhamento da movimentação. Este método deve ser auto-suficiente, e consequentemente, se responsabilizar pela garantia de que os dados armazenados são consistentes, o que com certeza demandará o uso de transação com o banco, concorda? Se você concorda, este método seria escrito mais ou menos assim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
procedure TProduto.Movimentar(const pIDDocumento: UInt32; 
  const pMovimentacao: Currency);
begin
  ConexaoDoDROP.StartTransaction;

  try
    ConexaoDoDROP.ExecuteCommand('update estoque_produto ...');
    ConexaoDoDROP.ExecuteCommand('insert into movimento_estoque ...');

    ConexaoDoDROP.CommitTransaction;
  except
    ConexaoDoDROP.RollbackTransaction;
    raise;
  end;
end;

Pois bem, imagine que outra classe, de nome TVenda, possui um método responsável por consolidar uma venda de produtos no banco de dados. Este método registra os dados da venda em diversas tabelas, e como neste momento a venda está sendo consolidada, o estoque dos produtos também deve ser movimentado. Como bom programador, você deve estar pensando em reutilizar o método anterior para persistir a nova posição do estoque, certo? Vamos ao código de TVenda.Consolidar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
procedure TVenda.Consolidar;
var
  lItem: TVendaItem;
begin
  ConexaoDoDROP.StartTransaction;

  try
    ConexaoDoDROP.ExecuteCommand('insert into venda ...');

    for lItem in Itens do
    begin
      ConexaoDoDROP.ExecuteCommand('insert into venda_item ...');
      lItem.Produto.Movimentar(Self.ID, lItem.Quantidade);
    end;

    ConexaoDoDROP.CommitTransaction;
  except
    ConexaoDoDROP.RollbackTransaction;
    raise;
  end;
end;

Muitos desenvolvedores optam por esta solução. No entanto, o que acontece com a sua engine de acesso a dados se você chamar uma segunda vez o comando de início de transação, sem ter terminado a transação anterior? Algumas engines não suportam esta técnica, e portanto, várias alternativas de workaround normalmente são implementadas, como testar se a conexão já possui uma transação aberta, ou delegar a abertura da transação para outro método, entre outras.

O sistema de chamadas cumulativas de transações do DROP permite que o comando de início da transação possa ser chamado mais de uma vez, sem que tenha que existir o desfecho da transação entre estas chamadas. Claro que, para que este sistema faça sentido, todas as chamadas de início de transação devem ser finalizadas também cumulativamente.

Concluindo, o DROP permite que, tanto o código de TProduto.Movimentar possa ser chamado individualmente, quanto possa ser reutilizado em outras rotinas já com transação aberta, sem que você precise escrever qualquer desvio para que isto tudo aconteça.

Era isto, até o próximo post!

Episódio Anterior Próximo Episódio

sexta-feira, 3 de novembro de 2017

DROP 1.3.1 - Edição Especial Embarcadero Conference 2017


Conforme anunciado antes mesmo do evento, saiu a nova versão do DROP, em edição especial para a Embarcadero Conference 2017. A nova versão está liberada nos tradicionais canais de distribuição:


Os esforços para esta versão se concentraram no suporte ao Interbase (nova feature), total tradução dos testes automatizados para a nova plataforma de testes, e claro, diversas pequenas melhorias foram realizadas.

Quer ajudar a desenvolver o DROP? Fique à vontade para enviar Pull Requests pelo GitHub.

Tem dúvidas de como usar o DROP ou quer entrar em contato para trocar ideias? Por favor, envie seu e-mail para drop@aquasoft.com.br.

E para fechar, novos posts sobre o DROP já estão em produção. Aguarde, pois muitas novidades estão por vir!!!

quinta-feira, 26 de outubro de 2017

Embarcadero Conference 2017

Hoje estou dando uma passada rápida aqui só para registrar que a Embarcadero Conference deste ano foi demais!!!

Obrigado a todos os participantes, especialmente aos que prestigiaram minhas apresentações! Me sinto honrado em merecer a atenção de vocês!


Nota importante: A versão especial do DROP para a Embarcadero Conference (1.3.1 - conforme prometido) já foi publicada. Os participantes do evento receberão via e-mail as instruções de como obtê-la.

Auditório principal durante a abertura do evento.

Momento de break, e muito networking.

Durante a minha primeira apresentação.

Meu muito obrigado aos expectadores!

Tradicional foto com os palestrantes e MVPs.


Embarcadero Conference 2018, nos aguarde!

sexta-feira, 6 de outubro de 2017

DROP Wiki #3 - Instruções SQL via texto

Por mais que um ORM tenha o objetivo de não escrever explicitamente instruções SQL, usuários novos de ORMs tendem a não aceitar uma ferramenta que não permita usar uma instrução SQL escrita manualmente. Em outras palavras, a aceitação de um framework com suporte a ORM passa por também suportar SQLs via texto, é quase que uma válvula de escape para desenvolvedores que não dominam ou até mesmo não confiam 100% na técnica.

No post anterior, foi apresentada a lista de classes nativas do DROP para estabelecimento de conexões com os SGBDs suportados. Como estratégia de evolução na apresentação e uso dos recursos do DROP, este post demonstrará como conversar com o SGBD, via instruções SQL escritas manualmente.

Os exemplos a seguir serão implementados sobre o Interbase, mas como já mencionado no post anterior, os recursos apresentados aqui estão disponíveis para todos os SGBDs suportados pelo DROP. Para os exemplos abaixo, assuma que já existe um objeto de conexão instanciado, e apontando para a tradicional base de exemplos Employee.

Inicialmente, o DROP não possui componentes ou objetos a parte para executar os comandos, como os tradicionais TSQLQuery, TFDQuery, entre outros. Todos os acessos e manipulações de dados são executados diretamente a partir do objeto de conexão com o SGBD.

Para começar, vamos buscar dados através de um select simples. As instruções SQL que retornam cursores são executadas através do método OpenQuery. Uma das sobrecargas do OpenQuery permite passar diretamente o texto de um select (demais sobrecargas serão demonstradas em outro post). Todas as sobrecargas do método OpenQuery retornam uma implementação da interface IAqDBReader.

A interface IAqDBReader foi especificada visando ser uma camada de acesso muito fina entre o seu código e os dados do cursor aberto pela instrução SQL. Portanto, o retorno não faz cache, e é unidirecional. Também é muito importante dizer que a leitura do primeiro registro passa obrigatoriamente pela primeira chamada do método Next no reader (ao estilo DBX Framework e readers de outras linguagens). E por fim, existem duas maneiras de ler colunas do cursor, as tradicionais referências por índice ou nome das mesmas.

O exemplo abaixo demonstra a leitura de todos os países da base, e então a alimentação de um componente memo com cada um dos nomes retornados.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
procedure TfrmExemplos.btnSelectClick(Sender: TObject);
var
  lReader: IAqDBReader;
begin
  mmoResultados.Lines.Clear;

  lReader := FConexao.OpenQuery('select country from country');

  while lReader.Next do
  begin
//    mmoResultados.Lines.Add(lReader.Values[0].AsString);
    mmoResultados.Lines.Add(lReader.Values['country'].AsString);
  end;
end;

Note que no laço de alimentação do memo constam duas linhas. A primeira linha, que está comentada e, portanto, sem efeito, o índice da coluna seria usado para referenciá-la, e na segunda linha, acessamos o valor da coluna através do seu nome.

Aos que não estão habituados com este conceito de reader, é necessário salientar que o método Next já é responsável por informar se o cursor chegou ao seu fim ou não, fazendo com que o teste de quebra do laço realize a troca do registro apontado pelo cursor.

O próximo exemplo visa demonstrar uma variação do exemplo acima, desta vez criando um parâmetro para execução da consulta.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
procedure TfrmExemplos.btnSelectComParametrosClick(Sender: TObject);
var
  lReader: IAqDBReader;
begin
  mmoResultados.Lines.Clear;

  lReader := FConexao.OpenQuery(
    'select country from country where currency = :currency',
    procedure(pParametros: IAqDBParameters)
    begin
      pParametros['currency'].AsString := 'Euro';
    end);

  while lReader.Next do
  begin
    mmoResultados.Lines.Add(lReader.Values['country'].AsString);
  end;
end;

É possível observar que a criação de parâmetros segue o tradicional padrão de quase todas as engines de acesso a dados do Delphi, com a prefixação do parâmetro com o caractere ':'. A principal diferença do DROP às demais metodologias está na passagem dos valores dos parâmetros, que acontece com um método anônimo (callback) que dá acesso aos parâmetros antes da execução da instrução contra o SGBD.

Como nota importante do exemplo acima, é importante dizer que, mesmo quando usando o DBX Framework como engine da conexão do DROP, o método de parâmetros nomeados também está disponível. Para quem não lembra, o DBX Framework não suporta parâmetros nomeados, mas o DROP realiza um parse sobre a instrução SQL, mapeia os parâmetros, e então faz o meio de campo entre os parâmetros do DBX e os parâmetros disponibilizados ao usuário, podendo estes serem acessados também via nome.

Por fim, vamos a um exemplo de instrução de DML. Estes comandos são executados a partir do método ExecuteCommand, que entre outras sobrecargas, também possui a capacidade de receber uma instrução de DML via texto, e passagens dos parâmetros via callback. Observe o exemplo abaixo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
procedure TfrmExemplos.btnUpdateClick(Sender: TObject);
var
  lRegistrosAfetados: Int64;
begin
  lRegistrosAfetados := FConexao.ExecuteCommand(
    'update country set currency = :currency where country = :country',
    procedure(pParametros: IAqDBParameters)
    begin
      pParametros['currency'].AsString := 'US Dollar';
      pParametros['country'].AsString := 'USA';
    end);

  ShowMessage(lRegistrosAfetados.ToString);
end;

Dúvidas? Por favor, não hesite em me contatar através do mail tatu@taturs.com, ou ainda por drop@aquasoft.com.br.

Abraço e até o próximo post!

Episódio Anterior Próximo Episódio

DROP Wiki (English) #2 - Connecting to DBMSs

Você pode ler este post em português clicando  aqui . Continuing the DROP Wiki Series, the current topic is the connection establishment w...