Já conseguimos utilizar os métodos de inclusão, alteração e exclusão da classe TDaoUib, conforme pôde ser visto no último artigo.
Porém, você deve ter notado a dependência excessiva (forte acoplamento) existente para efetuar tais operações. Outro ponto importante a ser destacado é que, apesar de não ter sido notado tão claramente devido a utilização de apenas uma tabela, se em determinado momento decidirmos trocar de componente, ou seja, em vez do Uib optarmos pelo IBX, tudo que for relativo a TDaoUib teria que ser modificado para que fosse possível utilizar o novo componente.
Temos que mudar esse quadro o quanto antes, senão ficaremos engessados! E o que queremos é justamente o oposto, não é mesmo? 😉
Ajustes necessários em TDaoUib
Queremos abstrair (simplificar) o uso dos componentes de acesso de tal forma que lá na ponta, o usuário final (desenvolvedor que utilizará este código) terá o trabalho facilitado, bastando informar alguns dados básicos para que o código esteja pronto para uso. Para isso, devemos:
- Alterar o nome da variável FTransacao para FTransaction (como definimos o nome do database em inglês, vamos manter o padrão).
- Alterar a declaração do construtor para passar apenas o nome e local onde se encontra o banco de dados.
- Os objetos, FDatabase e FTransaction, serão criados no construtor, bem como suas configurações iniciais.
Segue código completo de DaoUib.pas:
[sourcecode language=”delphi”]
unit DaoUib;
interface
uses Base, Rtti, Atributos, uib, system.SysUtils, System.Classes;
type
TDaoUib = class(TInterfacedObject, IDaoBase)
private
// conexao com o banco de dados
FDatabase: TUIBDataBase;
FTransaction: TUIBTransaction;
// Este método configura os parâmetros da AQuery.
procedure ConfigParametro(AQuery: TuibQuery; AProp: TRttiProperty; ACampo: string; ATabela: TTabela);
procedure FechaQuery;
function ExecutaQuery: Integer;
public
//query para execução dos comandos crud
Qry : TUIBQuery;
constructor Create(ADatabaseName: string);
function Inserir(ATabela: TTabela): Integer;
function Salvar(ATabela: TTabela): Integer;
function Excluir(ATabela: TTabela): Integer;
function InTransaction: Boolean;
procedure StartTransaction;
procedure Commit;
procedure RollBack;
end;
implementation
{ TDaoUib }
uses Vcl.forms, dialogs, System.TypInfo;
constructor TDaoUib.Create(ADatabaseName: string);
begin
inherited Create;
FDatabase := TUIBDataBase.Create(Application);
//configurações iniciais da conexão
with FDatabase do
begin
DatabaseName := ADatabaseName;
Params.Add(‘sql_dialect=3’);
Params.Add(‘lc_ctype=ISO8859_1’);
Connected := True;
end;
FTransaction := TUIBTransaction.Create(Application);
//configurações iniciais da transacao
with FTransaction do
begin
Database := FDatabase;
DefaultAction := etmCommit;
end;
Qry := TUIBQuery.Create(Application);
Qry.DataBase := FDatabase;
Qry.Transaction := FTransaction;
end;
function TDaoUib.InTransaction: Boolean;
begin
Result := FTransaction.InTransaction;
end;
procedure TDaoUib.StartTransaction;
begin
FTransaction.StartTransaction;
end;
procedure TDaoUib.RollBack;
begin
FTransaction.RollBack;
end;
procedure TDaoUib.Commit;
begin
FTransaction.Commit;
end;
procedure TDaoUib.ConfigParametro(AQuery: TUIBQuery; AProp: TRttiProperty;
ACampo: string; ATabela: TTabela);
begin
with AQuery do
begin
case AProp.PropertyType.TypeKind of
tkInt64,
tkInteger:
begin
Params.ByNameAsInteger[ACampo] := AProp.GetValue(ATabela).AsInteger;
end;
tkChar,
tkString,
tkUString:
begin
Params.ByNameAsString[ACampo] := AProp.GetValue(ATabela).AsString;
end;
tkFloat:
begin
if CompareText(AProp.PropertyType.Name, ‘TDateTime’) = 0 then
Params.ByNameAsDateTime[ACampo] := AProp.GetValue(ATabela).AsType<TDateTime>
else
Params.ByNameAsCurrency[ACampo] := AProp.GetValue(ATabela).AsCurrency;
end;
tkVariant:
begin
Params.ByNameAsVariant[ACampo] := AProp.GetValue(ATabela).AsVariant;
end;
else
raise Exception.Create(‘Tipo de campo não conhecido: ‘ + AProp.PropertyType.ToString);
end;
end;
end;
procedure TDaoUib.FechaQuery;
begin
Qry.Close;
Qry.SQL.Clear;
end;
function TDaoUib.ExecutaQuery: Integer;
begin
with Qry do
begin
Prepare();
ExecSQL;
Result := RowsAffected;
end;
end;
function TDaoUib.Excluir(ATabela: TTabela): Integer;
var
Comando: TFuncReflexao;
begin
//crio uma variável do tipo TFuncReflexao – um método anônimo
Comando := function(ACampos: TCamposAnoni): Integer
var
Campo: string;
PropRtti: TRttiProperty;
begin
FechaQuery;
with Qry do
begin
sql.Add(‘Delete from ‘ + ACampos.NomeTabela);
sql.Add(‘Where’);
//percorrer todos os campos da chave primária
ACampos.Sep := ”;
for Campo in ACampos.PKs do
begin
sql.Add(ACampos.Sep+ Campo + ‘= :’ + Campo);
ACampos.Sep := ‘ and ‘;
// setando os parâmetros
for PropRtti in ACampos.TipoRtti.GetProperties do
if CompareText(PropRtti.Name, Campo) = 0 then
begin
ConfigParametro(Qry, PropRtti, Campo, ATabela);
end;
end;
end;
Result := ExecutaQuery;
end;
//reflection da tabela e execução da query preparada acima.
Result := ReflexaoSQL(ATabela, Comando);
end;
function TDaoUib.Inserir(ATabela: TTabela): Integer;
var
Comando: TFuncReflexao;
begin
Comando := function(ACampos: TCamposAnoni): Integer
var
Campo: string;
PropRtti: TRttiProperty;
begin
FechaQuery;
with Qry do
begin
sql.Add(‘Insert into ‘ + ACampos.NomeTabela);
sql.Add(‘(‘);
//campos da tabela
ACampos.Sep := ”;
for PropRtti in ACampos.TipoRtti.GetProperties do
begin
SQL.Add(ACampos.Sep + PropRtti.Name);
ACampos.Sep := ‘,’;
end;
sql.Add(‘)’);
//parâmetros
sql.Add(‘Values (‘);
ACampos.Sep := ”;
for PropRtti in ACampos.TipoRtti.GetProperties do
begin
SQL.Add(ACampos.Sep + ‘:’ + PropRtti.Name);
ACampos.Sep := ‘,’;
end;
sql.Add(‘)’);
//valor dos parâmetros
for PropRtti in ACampos.TipoRtti.GetProperties do
begin
Campo := PropRtti.Name;
ConfigParametro(Qry, PropRtti, Campo, ATabela);
end;
end;
Result := ExecutaQuery;
end;
//reflection da tabela e execução da query preparada acima.
Result := ReflexaoSQL(ATabela, Comando);
end;
function TDaoUib.Salvar(ATabela: TTabela): Integer;
var
Comando: TFuncReflexao;
begin
Comando := function(ACampos: TCamposAnoni): Integer
var
Campo: string;
PropRtti: TRttiProperty;
begin
FechaQuery;
with Qry do
begin
sql.Add(‘Update ‘ + ACampos.NomeTabela);
sql.Add(‘set’);
//campos da tabela
ACampos.Sep := ”;
for PropRtti in ACampos.TipoRtti.GetProperties do
begin
SQL.Add(ACampos.Sep + PropRtti.Name + ‘=:’+PropRtti.Name);
ACampos.Sep := ‘,’;
end;
sql.Add(‘where’);
//parâmetros da cláusula where
ACampos.Sep := ”;
for Campo in ACampos.PKs do
begin
sql.Add(ACampos.Sep+ Campo + ‘= :’ + Campo);
ACampos.Sep := ‘ and ‘;
end;
//valor dos parâmetros
for PropRtti in ACampos.TipoRtti.GetProperties do
begin
Campo := PropRtti.Name;
ConfigParametro(Qry, PropRtti, Campo, ATabela);
end;
end;
Result := ExecutaQuery;
end;
//reflection da tabela e execução da query preparada acima.
Result := ReflexaoSQL(ATabela, Comando);
end;
end.
[/sourcecode]Como a nossa classe já tem um objeto para conexão e um para a transação, não precisamos mais dos componentes UIB instanciados em nosso formulário principal, o frmMain:
Exclua UIBDatabase1 e UIBTransaction1.
Eu quero Generics, Eu quero Interfaces…
Eu irei propor dois modelos para você, caro leitor, ambos com um objetivo em comum: resolver o problema do forte acoplamento existente. O primeiro será baseado em Generics e o segundo em Interface + Singleton (Design Pattern). No final deste artigo irei pedir a sua opinião a respeito dos modelos apresentados. Portanto, continue lendo…
Modelo 1: Classe TDaoGenerico
Abra a unidade Base.pas e abaixo da interface IDaoBase, coloque:
[sourcecode language=”delphi”]
…
IDaoBase = interface
[‘{D06AAE8D-D5F7-47E7-BF11-E26687C11900}’]
function Inserir(ATabela: TTabela): Integer;
function Salvar(ATabela: TTabela): Integer;
function Excluir(ATabela: TTabela): Integer;
function InTransaction: Boolean;
procedure StartTransaction;
procedure Commit;
procedure RollBack;
end;
//DAO genérico
TDaoGenerico<T: IDaoBase> = class
private
FCon: T; //classe de conexao uib, ibx, …
procedure SetCon(const Value: T);
public
property Con: T read FCon write SetCon;
end;
…
[/sourcecode]Dê Ctrl+Sift+C para gerar o SetCon, que ficará com o seguinte código:
[sourcecode language=”delphi”]
procedure TDaoGenerico<T>.SetCon(const Value: T);
begin
FCon := Value;
end;
[/sourcecode]Pronto! Simples assim!
Agora, iremos inserir um datamodule em nosso projeto. Assim, tudo que for relativo a banco de dados faremos neste local.
Salve como udmPrin (Ctrl+S). No nome, coloque dmPrin:
Por fim, configure o auto-create forms (Ctrl+Shift+11) da seguinte forma:
O datamodule deve ser o primeiro.
Em public de dmPrin, crie uma variável do tipo IDaoBase e uma variável da nossa classe genérica:
[sourcecode language=”delphi”]
unit udmPrin;
interface
uses
System.SysUtils, System.Classes, uib, Base, DaoUib;
type
TdmPrin = class(TDataModule)
private
{ Private declarations }
public
{ Public declarations }
Conexao: IDaoBase;
Dao: TDaoGenerico<TDaoUib>;
end;
…
[/sourcecode]
No OnCreate, faça:
[sourcecode language=”delphi”]
procedure TdmPrin.DataModuleCreate(Sender: TObject);
begin
// configuração da nossa conexão – utilizando DaoUib
Conexao:= TDaoUib.Create(‘C:\Users\Luiz\Documents\RAD Studio\Projects\Persistencia\Bd\BANCOTESTE.FDB’);
// DAO genérico
Dao := TDaoGenerico<TDaoUib>.Create;
Dao.Con:= TDaoUib(Conexao);
end;
[/sourcecode]Analisando o código:
- Linha 4, o tipo de conexão é criado, que neste caso é da classe TDaoUib. Note que agora estou passando o caminho e nome do banco de dados no Create desta classe.
- Linha 7, criamos o nosso DAO genérico. Como estamos passando T como sendo TDaoUib, somente objetos derivados desta classe serão aceitos.
- Linha 8, setamos a propriedade Con com o objeto criado na Linha 4.
Com isso, evitaremos a dependência de algumas unidades durante o CRUD (Ah! Eu não esqueci do único método que está faltando:R – de Read. Veremos como recuperar[consultar] dados nos próximos artigos). Precisaremos apenas adicionar o datamodule e a classe que representa a tabela que estamos alterando. É bom lembrar que, por enquanto, temos apenas uma tabela chamada Teste. Nossa classe genérica recebe T do tipo IDaoBase e a propriedade Con será desse tipo. Vê-lo funcionando irá facilitar o entendimento.
Preparando o Teste
Vamos começar pelo formulário frmTesteAtributos:> Como já testamos o botão Nome Tabela e Chave Primária, não há mais necessidade de tê-los neste formulário. Portanto, vamos excluí-los (exclua também os códigos destes botões):
Tire a unidade Atributos da cláusula uses deste formulário. Ficaremos apenas com os botões de inclusão, alteração e exclusão, conforme acima. Abaixo, vemos o código do botão inserir:
[sourcecode language=”delphi”]
procedure TfrmTesteAtributos.btnInserirClick(Sender: TObject);
var
ATab: TTeste;
Dao: IDaoBase;
Registros: Integer;
begin
ATab := TTeste.Create;
try
Dao := TDaoUib.Create(dmPrin.UIBDataBase1, dmPrin.UIBTransaction1);
with ATab do
begin
id := 1;
Estado := ‘MA’;
Descricao := ‘MARANHÃO’;
Habitantes := 6569683;
RendaPerCapta := 319;
end;
Registros := Dao.Inserir(ATab);
Memo1.Lines.Add(Format(‘Registro inserido: %d’, [Registros]));
Memo1.Lines.Add(Format(‘id: %d, nome: %s’,[ATab.Id, atab.Descricao]));
finally
ATab.Free;
end;
end;
[/sourcecode]Não precisamos mais do objeto Dao (linhas 4 e 9). Consequentemente, também não será mais necessário a unidade Base e nem tão pouco da unidade DaoUib na cláusula uses. Como resultado teremos uma menor dependência (acoplamento fraco). Teremos então, na cláusula uses (abaixo de implementation) apenas as unidades:
[sourcecode language=”delphi”]
implementation
{$R *.dfm}
uses
Teste, udmPrin;
[/sourcecode]Abaixo, código dos três botões. Note que já implementa o controle de transações.
[sourcecode language=”delphi”]
procedure TfrmTesteAtributos.btnInserirClick(Sender: TObject);
var
ATab: TTeste;
Registros: Integer;
begin
ATab := TTeste.Create;
try
with ATab do
begin
id := 1;
Estado := ‘MA’;
Descricao := ‘MARANHÃO’;
Habitantes := 6569683;
RendaPerCapta := 319;
end;
dmPrin.Dao.Con.StartTransaction;
try
Registros := dmPrin.Dao.Con.Inserir(ATab);
dmPrin.Dao.Con.Commit;
Memo1.Lines.Add(Format(‘Registro inserido: %d’, [Registros]));
Memo1.Lines.Add(Format(‘id: %d, nome: %s’,[ATab.Id, atab.Descricao]));
except
on E: Exception do
begin
dmPrin.Dao.Con.RollBack;
ShowMessage(‘Ocorreu um problema ao executar operação: ‘ + e.Message);
end;
end;
finally
ATab.Free;
end;
end;
procedure TfrmTesteAtributos.btnSalvarClick(Sender: TObject);
var
ATab: TTeste;
Registros: Integer;
begin
ATab := TTeste.Create;
try
with ATab do
begin
id := 1;
Estado := ‘MA’;
Data := Now;
Descricao := ‘MARANHÃO (ALTERADO)’;
Habitantes := 6569683;
RendaPerCapta := 319;
end;
dmPrin.Dao.Con.StartTransaction;
try
Registros := dmPrin.Dao.Con.Salvar(ATab);
dmPrin.Dao.Con.Commit;
Memo1.Lines.Add(Format(‘Registro inserido: %d’, [Registros]));
Memo1.Lines.Add(Format(‘id: %d, nome: %s’,[ATab.Id, atab.Descricao]));
except
on E: Exception do
begin
dmPrin.Dao.Con.RollBack;
ShowMessage(‘Ocorreu um problema ao executar operação: ‘ + e.Message);
end;
end;
finally
ATab.Free;
end;
end;
procedure TfrmTesteAtributos.btnExcluirClick(Sender: TObject);
var
ATab: TTeste;
Registros: Integer;
begin
ATab := TTeste.Create;
try
ATab.Id := 1;
dmPrin.Dao.Con.StartTransaction;
try
Registros := dmPrin.Dao.Con.Excluir(ATab);
dmPrin.Dao.Con.Commit;
Memo1.Lines.Add(Format(‘Registro excluido: %d’, [Registros]));
except
on E: Exception do
begin
dmPrin.Dao.Con.RollBack;
ShowMessage(‘Ocorreu um problema ao executar operação: ‘ + e.Message);
end;
end;
finally
ATab.Free;
end;
end;
[/sourcecode]Faça o teste: inclua, altere e exclua. Você percebeu que não é necessário fechar o programa para ver os dados no BD? Sim, os dados já estão sendo persistidos após efetuarmos o Commit no banco.
Modelo 2: Classe TDaoSingleton
Para ficar claro, e assim visualizar melhor as diferenças existentes entre o modelo 1 e o 2, façamos o seguinte:
- Crie uma pasta chamada “Persistencia”;
- Feche o Delphi e mova a pasta do nosso projeto para dentro da pasta criada;
- Renomeie a pasta do projeto para “Generico”;
- Copie a pasta “Generico” (Ctrl+C) e cole (Ctrl+V) dentro de “Persistencia”. Irá criar uma cópia;
- Renomeie a nova pasta (a “cópia”) para Interface.
Desta forma teremos 2 projetos: um para o modelo 1 (Generico) e o outro para o modelo 2 (Interface). Espero ter conseguido deixar claro esta parte. No Delphi, abra o projeto que está na pasta “Interface”. Feito isso, abra a unidade Base.pas. Iremos remover a classe TDaoGenerico e no seu lugar colocaremos uma interface e uma classe, a TDaoSingleton. Ela irá utilizar o Padrão Singleton (teremos uma implementação simples do padrão, mas é bom olhar o comentário que fiz no post Design Pattern: Singleton). Então, vamos lá:
[sourcecode language=”delphi”]
unit Base;
interface
uses DB, SysUtils, Classes, System.Generics.Collections;
type
IBaseDados = interface
[‘{9B0F9364-AB16-4C12-B4B7-4E2287840232}’]
end;
ITransacao = interface
[‘{2F1DCA7A-E7F4-4EC3-BDB2-22B99C8CA7DB}’]
end;
TTabela = class(TObject)
end;
IDaoBase = interface
[‘{D06AAE8D-D5F7-47E7-BF11-E26687C11900}’]
function Inserir(ATabela: TTabela): Integer;
function Salvar(ATabela: TTabela): Integer;
function Excluir(ATabela: TTabela): Integer;
function InTransaction: Boolean;
procedure StartTransaction;
procedure Commit;
procedure RollBack;
end;
IDaoSingleton = interface
[‘{0E2A5F26-3BBB-4022-8C2F-9BD88AD7CA89}’]
end;
TDaoSingleton = class(TInterfacedObject, IDaoSingleton)
private
FCon: IDaoBase;
class var FInstancia: IDaoSingleton;
procedure SetCon(const Value: IDaoBase);
public
class function Get: TDaoSingleton; static;
property Con: IDaoBase read FCon write SetCon;
end;
implementation
{ TDaoSing }
class function TDaoSingleton.Get: TDaoSingleton;
begin
If not Assigned(FInstancia) Then
FInstancia := TDaoSingleton.Create;
Result := FInstancia as TDaoSingleton;
end;
procedure TDaoSingleton.SetCon(const Value: IDaoBase);
begin
FCon := Value;
end;
end.
[/sourcecode]Muito parecido com a classe genérica, porém com esta não precisaremos nos preocupar com instanciação e destruição. Abra o dmPrin e faça as seguintes alterações:
[sourcecode language=”delphi”]
unit udmPrin;
interface
uses
System.SysUtils, System.Classes, uib, Base, DaoUib;
type
TdmPrin = class(TDataModule)
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Conexao: IDaoBase;
end;
var
dmPrin: TdmPrin;
implementation
{%CLASSGROUP ‘System.Classes.TPersistent’}
{$R *.dfm}
procedure TdmPrin.DataModuleCreate(Sender: TObject);
begin
// configuração da conexão – utilizando DaoUib
Conexao := TDaoUib.Create(‘C:\Users\Luiz\Documents\RAD Studio\Projects\Persistencia\Bd\BANCOTESTE.FDB’);
// dao genérico
TDaoSingleton.Get.Con := Conexao;
end;
end.
[/sourcecode]
- Observe que não precisamos mais do objeto chamado Dao.
- Na linha 33, setamos a propriedade Con com o objeto Conexao (TDaoUib).
No formulário frmTesteAtributos, passamos a utilizar TDaoSingleton em vez de TDaoGenerico:
[sourcecode language=”delphi”]
implementation
{$R *.dfm}
uses
Teste, Base;
procedure TfrmTesteAtributos.btnInserirClick(Sender: TObject);
var
ATab: TTeste;
Registros: Integer;
begin
ATab := TTeste.Create;
try
with ATab do
begin
id := 1;
Estado := ‘MA’;
Descricao := ‘MARANHÃO’;
Habitantes := 6569683;
RendaPerCapta := 319;
end;
TDaoSingleton.Get.Con.StartTransaction;
try
Registros := TDaoSingleton.Get.Con.Inserir(ATab);
TDaoSingleton.Get.Con.Commit;
Memo1.Lines.Add(Format(‘Registro inserido: %d’, [Registros]));
Memo1.Lines.Add(Format(‘id: %d, nome: %s’,[ATab.Id, atab.Descricao]));
except
on E: Exception do
begin
TDaoSingleton.Get.Con.RollBack;
ShowMessage(‘Ocorreu um problema ao executar a operação: ‘ + e.Message);
end;
end;
finally
ATab.Free;
end;
end;
procedure TfrmTesteAtributos.btnSalvarClick(Sender: TObject);
var
ATab: TTeste;
Registros: Integer;
begin
ATab := TTeste.Create;
try
with ATab do
begin
id := 1;
Estado := ‘MA’;
Data := Now;
Descricao := ‘MARANHÃO (ALTERADO SINGLETON)’;
Habitantes := 6569683;
RendaPerCapta := 319;
end;
TDaoSingleton.Get.Con.StartTransaction;
try
Registros := TDaoSingleton.Get.Con.Salvar(ATab);
TDaoSingleton.Get.Con.Commit;
Memo1.Lines.Add(Format(‘Registro inserido: %d’, [Registros]));
Memo1.Lines.Add(Format(‘id: %d, nome: %s’,[ATab.Id, atab.Descricao]));
except
on E: Exception do
begin
TDaoSingleton.Get.Con.RollBack;
ShowMessage(‘Ocorreu um problema ao executar a operação: ‘ + e.Message);
end;
end;
finally
ATab.Free;
end;
end;
procedure TfrmTesteAtributos.btnExcluirClick(Sender: TObject);
var
ATab: TTeste;
Registros: Integer;
begin
ATab := TTeste.Create;
try
ATab.Id := 1;
TDaoSingleton.Get.Con.StartTransaction;
try
Registros := TDaoSingleton.Get.Con.Excluir(ATab);
TDaoSingleton.Get.Con.Commit;
Memo1.Lines.Add(Format(‘Registro excluido: %d’, [Registros]));
except
on E: Exception do
begin
TDaoSingleton.Get.Con.RollBack;
ShowMessage(‘Ocorreu um problema ao executar a operação: ‘ + e.Message);
end;
end;
finally
ATab.Free;
end;
end;
[/sourcecode]Além de trocar a classe TDaoGenerico pela classe TDaoSingleton, no uses deixamos de utilizar a unidade udmPrin e em seu lugar, utilizamos a unidade Base.
Teste novamente…
Passando a bola
Agora é com você! Quero que analise os dois modelos atentamente e depois me diga:
- Qual dos 2 gostou mais?
- Pontos positivos e negativos de cada um?
- Tem alguma sugestão para melhorar o que foi feito?
- Gostaria de continuar a utilizar os componentes TUIBDatabase e TUIBTRansaction diretamente, instanciando o componente no datamodule e passando eles por parâmetro, como era feito anteriormente?
Vou aguardar o seu comentário! Depois continuaremos o ORM básico com o modelo mais aceito.
Obrigado e até o próximo artigo!
[poll id=”2″]
1) Eu gostei mais do primeiro (Genérico).
2) O Genérico: + Diminui a quantidade de units na uses (diminui o acoplamento).
+ Permite criar mais de uma instância do Dao (pode ser útil em alguns casos, eu trabalho com SQL Server e crio duas conexões em meu projeto, uma para a database principal e outra para minha database de controle de CEP)
– Mais complexa de entender (pra mim que sou iniciante nessa técnica).
– Código um pouco maior.
Interface: + Código menor e mais fácil de entender.
+ Garantia de que só existirá 1 instância da conexão (Singleton).
– Mais acoplado que o genérico
3) Eu não entendi bem o motivo de ter colocado a conexão e a transação como público, eu deixaria como private.
Eu adicionaria uma opção (poderia ser um enum) para que fosse possível escolher a qual banco vamos conectar (se é produção, teste, desenvolvimento por exemplo), nesse caso acho que talvez a string de conexão fosse melhor, para não ter que colocar no datamodule 3 conexões e só usar 1.
4) Eu deixaria passando a conexão (o componente) mesmo no local da string para criar a conexão, acho que assim é mais fácil para manter ou se for usada uma opção para escolher a qual banco vamos conectar, neste caso, acho que o mais indicado é a string mesmo.
Excelente sua participação!
Que venha mais!
Com relação ao motivo da conexão e transação ser pública: a princípio eu tinha uma ideia diferente para eles, mas no fim, acabou não sendo necessária essa modificação. Vou mudar isso no próximo artigo.
Vamos lá pessoal! Façam como o Ricardo… a discussão será benéfica para o nosso ORM básico. Portanto, participem.
Ricardo, inseri uma enquete no final do artigo… vote.
Para não causar confusão, alterei o artigo.
Voltei a conexão (FDataBase) como era anteriormente, ou seja, no private. Quando lancei o artigo, dia 22/10, eu havia dito que era para excluir esta variável e que iriamos permitir acesso direto a um objeto do tipo TUIBDataBase. Mas no final , ficou sem função permitir o acesso direto à conexão.
Quem baixou os fontes anteriormente, baixe-os novamente.
Olha, por enquanto não vou opinar, mas estou acompanhando e entendendo bem melhor o Generics no Delphi. Continue postando..Valeu pelos artigos!!
Valeu Rafael!
Mas não deixe de votar. Finalizada a votação, passaremos a adotar o modelo ganhador da enquete.
Temos que definir um rumo para o ORM.
Bom Luiz, como havia dito no facebook, o modelo em si, pra mim não faz muita diferença. Qualquer um dos dois será bem vindo, alias, fica a dica… o porque não os dois ? Ja que é um ORM, pode-se implementar os dois modelos e cada um usa como quiser. Agora, a minha preocupação é quanto a abertura da base de dados, se ela será aberta uma unica vez e posteriormente sua utilização com as querys devidas ou se a cada vez que precisar usa uma query, iria abrir a base. No meu ponto de vista, a segunda opção causaria uma lentidão no software, toda vez que precisa-se de uma informação, imagine isso com acesso via Internet por exemplo, na qual todos nós sabemos que o protocolo do Firebird não é la essas coisas. Uma outra dica interessante que o amigo Ricardo colocou, seria termos uma string de conexão (Produção, Teste, Desenvolvimento, etc)… acho isso muito legal, principalmente se pensarmos em Multi-empresa, sei que não seria o caso… mas poderiamos adaptar !
Eu voto no Generics! Acho que os Frameworks existentes por aí, se baseiam em Generics.
Obrigado Rafael.
Muito obrigado, Douglas!
Poderemos, claro, deixar os dois modelos ativos no mesmo projeto. Só penso se isso não traria alguma confusão… ter uma forma somente simplificaria o trabalho por parte de quem for usar o código. Porém, isso não seria algo tão importante a ponto de parar o projeto, visto que ambos os modelos conseguiram atingir o objetivo inicial – conectar à base!
Chamei o tema à tona justamente para ter um feedback e claro debater as ideias propostas no artigo. Quanto mais debatermos, mas familiar o assunto se tornará (interface, generics, singleton, etc).
No nível atual em que se encontra o projeto, creio que o papel mais importante ele já é capaz de fazer, ou seja, nos livrar da montagem do SQL. Claro que, à media que o ORM avançar, novos requisitos e necessidades surgirão! Como por exemplo, a sugestão da string de conexão!
Luiz, eu voto no modelo Generics, já pensando no futuro do ORM, eu sugiro o uso de cash de objetos Query e Coneccion usando Dicionary, para evitar ficar criando e destruindo objetos para a mesma tabela.
Obrigado João Carvalho!
Interessante a sua sugestão. A ideia aqui é manter o projeto no básico, sem tornar o código complexo demais. É ter no final, um código simples e de fácil utilização.
Portanto, nos brinde com mais detalhes. Desenvolva sua ideia para que possamos emitir uma opinião mais precisa.
Gostaria de parabeniza-lo pela sua iniciativa, seguinte o generics eu sei que funciona no D2010, mas esses novo padrao tambem funciona, ou só aparti do XE2?
a principio fico com generics pois os frameworks de outras linguagem tambem usam fica menos dificil de compreender.
Olá Leandro
Métodos anônimos e generics já estão no Delphi desde a versão 2009. A nova RTTI a partir do 2010.
Desta forma, se você estiver utilizando a versão 2010, creio que não terá problemas. Qualquer coisa avisa, assim poderemos ver o que pode ser feito.
Abraços.
Luiz, já tem alguma previsão de quando sai a próxima parte?
Olá Ricardo
Quero entregar amanhã, 30/10.
Pessoal
Tive um contratempo e o próximo artigo vai ter um pequeno atraso. Espero até sexta resolver umas pendências aqui e assim ter tempo disponível para terminar o artigo.
Beleza, fico na espera!!
Certo, também estou na espera.
Luiz, boa noite, meus parabéns… nunca vi um post, tão bom e pratico como este!, muito bem explicado, parabéns mesmo.
Vlw Manoel Neto e obrigado pela visita.
É uma pena eu não poder continuar com as postagens, visto que estou envolvido em alguns projetos que têm tirado todo o meu tempo.
Caro, não poderia deixar de agradecer!
Excelente dissertação! Faço esse comentário após ler até o Post da Parte 9…
Raras vezes encontrei um material de tão excelente qualidade, um material que nos levasse do início ao fim do raciocínio sem deixar nada para traz, e o mais importante, totalmente funcional!
Vale lembrar que até aqui, já estou executando os testes convertendo o ORM para trabalhar com FireDAC. Funcionando Legal!
Se possível, poderia disponibilizar o Source para um Merge?
Abraço e muito sucesso por ai! Vamos aos próximos Posts, ansioso para ver as demais maravilhas… Hehehe!
Opa, fala Charles. Obrigado pelo comentário.
Com relação ao merge, até disponibilizei os fontes no github, porém, no momento, estou envolvido na criação de um curso para este espaço: http://www.cursos.luizsistemas.com.br e fora que a Receita irá eliminar a NFe 3.1 em agosto, então já viu, né! rsrss A correria como sempre está uma doidera aqui.
Então me falta tempo para voltar a abrir este código e ver todas as atualizações que ainda não subi para o git. Só para você ter ideia, já prorroguei a abertura das inscrições do curso por 3 vezes.