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:
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.
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:
... 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; ...
Dê Ctrl+Sift+C para gerar o SetCon, que ficará com o seguinte código:
procedure TDaoGenerico<T>.SetCon(const Value: T); begin FCon := Value; end;
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:
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; ...
No OnCreate, faça:
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;
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:
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;
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:
implementation {$R *.dfm} uses Teste, udmPrin;
Abaixo, código dos três botões. Note que já implementa o controle de transações.
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;
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á:
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.
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:
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.
- 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:
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;
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.