Ola Pessoal,
Hoje já faz mais de 11 anos que desenvolvo sistemas comerciais. E algo que tenho percebido em 100% das aplicações (ao menos nas que vi) é que sempre, sempre, nos amarramos a alguma tecnologia do momento. Por exemplo: Quem vem acompanhando o delphi desde a suas primeiras versões, teve o desprazer (assim como eu) de usar TTable com Paradox (Égua! Nunca mais).
Voltando ao assunto. Daí surgiram outras novidades! Mais componentes de acesso a dados, controles dataware para simplificar a nossa mão de obra, datamodules e a maldição de jogar os campos no FieldsEditor dos datasets (outra porcaria que por sinal muita gente ainda vem fazendo). Ai! O sonhado BDE começou a dar pau de todo jeito. Ninguém tinha mais sossego na vida. Tinha que adivinhar qual versão do BDE era compatível com Oracle, MSSQL, Firebird, Access, etc...
Com o DBExpress, ADO e Componentes de acesso nativo! Todo mundo queria sair do BDE! E então, novos erros foram encontrados. Isso mesmo! A liberdade custa caro meus amigos. Eis um novo problema! Como sair do BDE se em todo burado tenho TTable, TQuery enfiado no meu sistema? Não teve outro jeito, senão sair trocando tudo no braço. Pois é! Hoje temos novos componentes de acesso a dados que vem com a ferramenta delphi e ainda temos componentes de terceiros FREE e PAGOS que prometem resolver nossos problemas.
Então vem a pergunta! Qdo isso começar a dar problema de novo? Como vou migrar para algo melhor? Vou ter que sair trocando tudo de novo? A resposta é não. Eis uma dica que passei a usar que tem resolvido 99.99% dos meus problemas. Observando o ambiente .NET, tive a feclidade de INVEJAR as Interfaces de Acesso a Dados IDBConnection, IDataReader, etc...
Pensando nisso! Resolvi criar algo mais simples, mas que resolvesse meu problema. O que foi feito então? Criei uma camada simples de acesso a dados. E agora não preciso mais me preocupar se estou usando DBExpress, ADO, BDE, etc... Se um desses providers falhar comigo troco para outro em questão de minutos. Não mais que 2 minutos. Olha só o que foi feito.
1. Criando um novo objeto para executar comandos SQL:
TPLSQL = class(TComponent)
Essa ai vai ser nossa classe que irá acessar os dados do nosso BD. Começarei explicando a sua principal propriedade. A que me diz qual Connection estou usando: BDE, ADO, etc...
uses DB; <- não esqueçam disso.
TPLSQL = class(TComponent)
private
FConn: TCustomConnection;
protected
function get_Conn: TCustomConnection; virtual;
procedure set_Conn(const Value: TCustomConnection); virtual;
public
property Conn: TCustomConnection read get_Conn write set_Conn;
end;
O que é TCustomConnection? Essa é uma classe base dos nossos componentes de acesso a dados como por exemplo: TDataBase, TSQLConnection, TADOConnection e TIBDataBase. Tendo ela como propriedade poderei conectar qualquer um desses. Por Exemplo:
var
obj : TPLSQL;
begin
obj := TPLSQL.Create(Self);
obj.Conn := DataBase1; // se eu estiver usando BDE
ou ainda
obj.Conn := SQLConnection1; // se eu estiver usando TSQLConnection
etc....
2. Criando uma propriedade para nossos comandos SQL:
TPLSQL = class(TComponent)
private
FConn: TCustomConnection;
FSQL: TStrings;
protected
function get_Conn: TCustomConnection; virtual;
procedure set_Conn(const Value: TCustomConnection); virtual;
function get_SQL: TStrings; virtual;
procedure set_SQL(const Value: TStrings); virtual;
public
property Conn: TCustomConnection read get_Conn write set_Conn;
property SQL: TStrings read get_SQL write set_SQL;
constructor Create(AQwner: TComponent); override;
destructor Destroy; override;
end;
implementation
constructor TPLSQL.Create(AQwner: TComponent);
begin
inherited;
FSQL := TStringList.Create; // inicializa
end;
destructor TPLSQL.Destroy;
begin
FSQL.Free; // libera o objeto
inherited;
end;
function TPLSQL.get_SQL: TStrings;
begin
result := FSQL; // metodo de leitura da propriedade SQL
end;
procedure TPLSQL.set_SQL(const Value: TStrings);
begin
FSQL.Assign(Value); // metodo de escrita da propriedade
end;
3. Descobrindo o Componente de acesso a dados associado a propriedade Conn:
Agora temos um problema! Qual componente foi usado? BDE? ADO? Iremos resolver isso com a implementação desses métodos simples.
uses SqlExpr, ADODB, DB;
function isBDE(Conn: TCustomConnection): boolean;
begin
result := Conn is TDatabase;
end;
function isDBExpress(Conn: TCustomConnection): boolean;
begin
result := Conn is TSQLConnection;
end;
function isADO(Conn: TCustomConnection): boolean;
begin
result := Conn is TADOConnection;
end;
4. Executando comandos SQL que não geram Cursor:
Criando nossa exceção padrão. Iremos processar a exceção abaixo em caso de ser usado um Componente para acesso a dados diferente do suportado pela nossa classe. Ou seja, diferente de Ado, BDE e DBExpress.
EDriverSuppor = class(Exception)
public
constructor Create; virtual;
end;
implementation
constructor EDriverSuppor.Create;
begin
inherited Create('Driver de conexão não suportado!');
end;
O próximo passo é criar um novo método, genérico, para executar comandos SQL do tipo INSERT, UPDATE, DELETE, Create Table, Alter Table e etc... Neste caso criaremos uma função que retornará uma Query de acordo com a classe associada a propriedade
Conn do nosso objeto.
TPLSQL = class(TComponent)
private
FConn: TCustomConnection;
FSQL: TStrings;
protected
function get_Conn: TCustomConnection; virtual;
procedure set_Conn(const Value: TCustomConnection); virtual;
function get_SQL: TStrings; virtual;
procedure set_SQL(const Value: TStrings); virtual;
// cria um objeto query de acordo com o componente associado a Conn
function CreateDataSetQuery: TDataSet; virtual;
public
property Conn: TCustomConnection read get_Conn write set_Conn;
property SQL: TStrings read get_SQL write set_SQL;
constructor Create(AQwner: TComponent); override;
destructor Destroy; override;
// essa função processa um comando SQL e retorna o número de linhas afetadas
function ExecuteDirect: integer; virtual;
end;
implementation
function TPLSQL.CreateDataSetQuery: TDataSet;
begin
// se for BDE cira TQuery
if isBDE(Conn) then
result := TQuery.Create(owner)
// se for DBExpress cira TSQLQuery
else if isDBExpress(Conn) then
result := TSQLQuery.Create(owner)
// se for ADO cria TADOQuery
else if isADO(COnn) then
result := TADOQuery.Create(Owner)
else
result := nil;
end;
function TPLSQL.ExecuteDirect: integer;
var
loQry: TDataSet;
begin
try
loQry := CreateDataSetQuery; // implementado logo acima
result := -1;
try
// se nao eh um componente suportado
if not Assigned(loQry) then
raise EDriverSuppor.Create;
// executa como bde
if isBDE(Conn) then
begin
TQuery(loQry).DatabaseName := TDataBase(Conn).DatabaseName;
TQuery(loQry).SQL.Assign(SQL);
TQuery(loQry).ExecSQL;
result := TQuery(loQry).RowsAffected;
end
// executa como ADO
else if isADO(Conn) then
begin
TAdoQuery(loQry).Connection := TADOConnection(Conn);
TAdoQuery(loQry).SQL.Assign(SQL);
result := TAdoQuery(loQry).ExecSQL;
end
// executa como DBExpress
else
begin
TSQLQuery(loQry).SQLConnection := TSQLConnection(Conn);
TSQLQuery(loQry).SQL.Assign(SQL);
result := TSQLQuery(loQry).ExecSQL;
end;
finally
if Assigned(loQry) then
loQry.Free;
end;
except
on e: exception do
raise;
end;
end;
5. Executando comandos SQL que GERAM Cursor:
No passo anterior criamos um método para comandos INSERT, UPDATE e etc. Agora iremos criar um comando que além de processar o script SQL ele irá nos retornar os dados processados por esse comando. Para isso! Iremos precisar criar um método que leia um DataSet genérico e o transforme num OleVariant. Pois esse tipo de dado
pode ser aberto dentro de um ClientDataSet. Vejamos como proceder.
uses Provider;
function getDataSet(Ds: TDataSet): OleVariant;
var
provider: TDataSetProvider;
begin
provider := TDataSetProvider.Create(nil);
try
provider.DataSet := Ds;
Result := provider.Data;
finally
provider.Free;
end;
end;
Agora vamos criar nosso último método da Classe TPLSQL.
TPLSQL = class(TComponent)
private
FConn: TCustomConnection;
FSQL: TStrings;
protected
function get_Conn: TCustomConnection; virtual;
procedure set_Conn(const Value: TCustomConnection); virtual;
function get_SQL: TStrings; virtual;
procedure set_SQL(const Value: TStrings); virtual;
// cria um objeto query de acordo com o componente associado a Conn
function CreateDataSetQuery: TDataSet; virtual;
public
property Conn: TCustomConnection read get_Conn write set_Conn;
property SQL: TStrings read get_SQL write set_SQL;
constructor Create(AQwner: TComponent); override;
destructor Destroy; override;
// essa função processa um comando SQL e retorna o número de linhas afetadas
function ExecuteDirect: integer; virtual;
// essa função processa um comando SQL e retorna os registros do mesmo
function ExecuteReader: OleVariant; virtual;
end;
implementation
function TPLSQL.ExecuteReader: OleVariant;
var
loQry: TDataSet;
begin
try
loQry := CreateDataSetQuery;
result := null;
try
// se eh um componente suportado
if not Assigned(loQry) then
raise EDriverSuppor.Create;
// executa como bde
if isBDE(Conn) then
begin
TQuery(loQry).DatabaseName := TDataBase(Conn).DatabaseName;
TQuery(loQry).SQL.Assign(SQL);
end
// executa como ado
else if isADO(Conn) then
begin
TAdoQuery(loQry).Connection := TADOConnection(Conn);
TAdoQuery(loQry).SQL.Assign(SQL);
end
// executa como dbexpress
else
begin
TSQLQuery(loQry).SQLConnection := TSQLConnection(Conn);
TSQLQuery(loQry).SQL.Assign(SQL);
end;
result := getDataSet(loQry);
finally
if Assigned(loQry) then
loQry.Free;
end;
except
on e: exception do
raise;
end;
end;
6. Vejam alguns exemplos de como usar essa classe
1. Exemplo:
var
loPL: TPLSQL;
begin
loPL:= TPLSQL.Create(Self);
loPL.Conn := Database1; // bde
loPL.SQL.Add('DELETE FROM CLIENTES');
loPL.ExecuteDirect;
loPL.Free;
end;
No exemplo acima, se vc não quiser mais usar BDE no seu sistema basta trocar a linha
loPL.Conn := Database1; // bde
Por:
loPL.Conn := SQLConnection1; // dbexpress
Agora vejamos como retornar dados para um DBGrid
2. Exemplo:
var
loPL: TPLSQL;
begin
loPL:= TPLSQL.Create(Self);
loPL.Conn := ADOConnection1; // ADO
loPL.SQL.Add('SELECT * FROM CLIENTES');
ClienteDataSet.Close;
ClienteDataSet.Data := loPL.ExecuteReader;
ClienteDataSet.Open;
loPL.Free;
end;
Os fontes para download estão disponíveis.
Eles estão em anexo ao artigo.
É só clicar em Download!
Obrigado a todos!
E espero que possam melhorar a classe e divulgá-la.