Olá! Saudades?

Ok, Ok, Estou em débito com vocês, caríssimos leitores! Estive realmente assoberbado aqui e acabei passando um longo tempo sem atualizar o nosso “projetim“. Ainda me encontro com a água no pescoço, mas juntei forças para mais um post.

Na última atualização, foi definido que passaríamos a utilizar generics para o nosso projeto. Agora irei demonstrar como será simples utilizar outro set de componentes, ou seja, estaremos mudando de UIB para IBX. Para isso, devemos implementar uma nova classe, a TDaoIbx.

Abra o explorer, localize a pasta classes e copie o arquivo DaoUib.pas, cole e renomeie para DaoIbx.pas:

orm-ibxunit1

orm-ibxunit2

Abra DaoIbx.pas no Delphi e troque tudo que for relacionado ao outro componente para IBX, conforme código abaixo:

unit DaoIbx;

interface

uses Base, Rtti, Atributos, system.SysUtils, System.Classes, IB, 
IBQuery, IBDatabase;

type
  TDaoIbx = class(TDaoBase)
  private
    // conexao com o banco de dados
    FDatabase: TIBDatabase;
    // transação para crud
    FTransaction: TIbTransaction;
    // transação para consultas
    FTransQuery: TIBTransaction;

  protected
    // métodos responsáveis por setar os parâmetros
    procedure QryParamInteger(ARecParams: TRecParams); override;
    procedure QryParamString(ARecParams: TRecParams); override;
    procedure QryParamDate(ARecParams: TRecParams); override;
    procedure QryParamCurrency(ARecParams: TRecParams); override;
    procedure QryParamVariant(ARecParams: TRecParams); override;

    //métodos para setar os variados tipos de campos
    procedure SetaCamposInteger(ARecParams: TRecParams); override;
    procedure SetaCamposString(ARecParams: TRecParams); override;
    procedure SetaCamposDate(ARecParams: TRecParams); override;
    procedure SetaCamposCurrency(ARecParams: TRecParams); override;

    function ExecutaQuery: Integer; override;
    procedure FechaQuery; override;
  public
    //query para execução dos comandos crud
    Qry: TIBQuery;

    constructor Create(ADatabaseName: string);

    function Inserir(ATabela: TTabela): Integer; override;
    function Salvar(ATabela: TTabela): Integer;  override;
    function Excluir(ATabela: TTabela): Integer; override;
    function Buscar(ATabela:TTabela): Integer; override;

    function InTransaction: Boolean; override;
    procedure StartTransaction; override;
    procedure Commit; override;
    procedure RollBack; override;
  end;

implementation

{ TDaoIbx }

uses Vcl.forms, dialogs, System.TypInfo;

constructor TDaoIbx.Create(ADatabaseName: string);
begin
  inherited Create;

  FDatabase := TIBDatabase.Create(Application);
  //configurações iniciais da conexão
  with FDatabase do
  begin
    DatabaseName := ADatabaseName;
    Params.Add('user_name=sysdba');
    Params.Add('password=masterkey');
    LoginPrompt := false;
    Connected := True;
  end;

  //configurações iniciais da transacao para consultas
  FTransQuery := TIBTransaction.Create(Application);
  with FTransQuery do
  begin
    DefaultDatabase := FDatabase;
    Params.Add('read_committed');
    Params.Add('rec_version');
    Params.Add('nowait');
  end;
  FDatabase.DefaultTransaction := FTransQuery;

  //configurações iniciais da transacao para crud
  FTransaction := TIBTransaction.Create(Application);
  with FTransaction do
  begin
    DefaultDatabase := FDatabase;
    Params.Add('read_committed');
    Params.Add('rec_version');
    Params.Add('nowait');
  end;

  Qry := TIBQuery.Create(Application);
  Qry.DataBase := FDatabase;
  Qry.Transaction := FTransaction;
end;

procedure TDaoIbx.QryParamCurrency(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    TIBQuery(Qry).ParamByName(Campo).AsCurrency := Prop.GetValue(Tabela).AsCurrency;
  end;
end;

procedure TDaoIbx.QryParamDate(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    TIBQuery(Qry).ParamByName(Campo).AsDateTime := Prop.GetValue(Tabela).AsType<TDateTime>;
  end;
end;

procedure TDaoIbx.QryParamInteger(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    TIBQuery(Qry).ParamByName(Campo).AsInteger := Prop.GetValue(Tabela).AsInteger;
  end;
end;

procedure TDaoIbx.QryParamString(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    TIBQuery(Qry).ParamByName(Campo).AsString := Prop.GetValue(Tabela).AsString;
  end;
end;

procedure TDaoIbx.QryParamVariant(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    TIBQuery(Qry).ParamByName(Campo).Value := Prop.GetValue(Tabela).AsVariant;
  end;
end;

procedure TDaoIbx.SetaCamposCurrency(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    Prop.SetValue(Tabela, TIBQuery(Qry).FieldByName(Campo).AsCurrency);
  end;
end;

procedure TDaoIbx.SetaCamposDate(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    Prop.SetValue(Tabela, TIBQuery(Qry).FieldByName(Campo).AsDateTime);
  end;
end;

procedure TDaoIbx.SetaCamposInteger(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    Prop.SetValue(Tabela, TIBQuery(Qry).FieldByName(Campo).AsInteger);
  end;
end;

procedure TDaoIbx.SetaCamposString(ARecParams: TRecParams);
begin
  inherited;
  with ARecParams do
  begin
    Prop.SetValue(Tabela, TIBQuery(Qry).FieldByName(Campo).AsString);
  end;
end;

function TDaoIbx.InTransaction: Boolean;
begin
  Result := FTransaction.InTransaction;
end;

procedure TDaoIbx.StartTransaction;
begin
  FTransaction.StartTransaction;
end;

procedure TDaoIbx.RollBack;
begin
  FTransaction.RollBack;
end;

procedure TDaoIbx.Commit;
begin
  FTransaction.Commit;
end;

procedure TDaoIbx.FechaQuery;
begin
  Qry.Close;
  Qry.SQL.Clear;
end;

function TDaoIbx.ExecutaQuery: Integer;
begin
  with Qry do
  begin
    Prepare();
    ExecSQL;
    Result := RowsAffected;
  end;
end;

function TDaoIbx.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
              ConfiguraParametro(PropRtti, Campo, ATabela, Qry);
            end;
      end;
    end;
    Result := ExecutaQuery;
  end;

  //reflection da tabela e execução da query preparada acima.
  Result := ReflexaoSQL(ATabela, Comando);
end;

function TDaoIbx.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;
        ConfiguraParametro(PropRtti, Campo, ATabela, Qry);
      end;
    end;
    Result := ExecutaQuery;
  end;

  //reflection da tabela e execução da query preparada acima.
  Result := ReflexaoSQL(ATabela, Comando);
end;

function TDaoIbx.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;
        ConfiguraParametro(PropRtti, Campo, ATabela, Qry);
      end;
    end;
    Result := ExecutaQuery;
  end;

  //reflection da tabela e execução da query preparada acima.
  Result := ReflexaoSQL(ATabela, Comando);
end;

function TDaoIbx.Buscar(ATabela: TTabela): Integer;
var
  Comando: TFuncReflexao;
  Dados: TIBQuery;
begin
  Dados := TIBQuery.Create(nil);
  try
    //crio uma variável do tipo TFuncReflexao - um método anônimo
    Comando := function(ACampos: TCamposAnoni): Integer
    var
      Campo: string;
      PropRtti: TRttiProperty;
    begin
      with Dados do
      begin
        Database := FDatabase;
        sql.Add('select * 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
                ConfiguraParametro(PropRtti, Campo, ATabela, Dados, True);
              end;
        end;
        Open;
        Result := RecordCount;
        if Result > 0 then
        begin
          for PropRtti in ACampos.TipoRtti.GetProperties do
          begin
            Campo := PropRtti.Name;
            SetaDadosTabela(PropRtti, Campo, ATabela, Dados);
            ACampos.Sep := ',';
          end;
        end;
      end;
    end;

    //reflection da tabela e abertura da query preparada acima.
    Result := ReflexaoSQL(ATabela, Comando);
  finally
    Dados.Free;
  end;
end;

Adicione esta unit ao projeto.

Pronto! Agora veremos como iremos utilizar esta unidade.

Digamos que depois de um certo tempo de uso, resolvemos adotar o IBX para acesso ao banco de dados. Eu mesmo já passei por isso no passado e digo que não foi uma tarefa muito agradável e nem tão pouco simples de ser feita. Para falar a verdade, passei algumas madrugadas em branco trocando componentes e fazendo ajustes para possibilitar o acesso ao banco. Agora, com este projeto, vou mostrar que de forma muito rápida, apenas trocando a classe, já estarei acessando o novo set de componentes.

Abra o datamodule dmPrin.

Vamos trocar TDaoUib por TDaoIbx. Lembre-se de adicionar ao uses a unit DaoIbx. Segue código do datamodule:

unit udmPrin;

interface

uses
  System.SysUtils, System.Classes, uib, Base, DaoUib, DaoIBX;

type
  TdmPrin = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Conexao: IDaoBase;
    Dao: TDaoGenerico<TDaoIbx>;
  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');

  // configuração da conexão - utilizando IBX
  Conexao := TDaoIbx.Create('C:\Users\Luiz\Documents\RAD Studio\Projects\Persistencia\Bd\BANCOTESTE.FDB');

  // dao genérico
  Dao := TDaoGenerico<TDaoIbx>.Create;

  Dao.Con := TDaoIbx(Conexao);
end;

procedure TdmPrin.DataModuleDestroy(Sender: TObject);
begin
  Dao.Free;
end;

Sobre o código, temos:

  • Linha 17 – Dao é agora do tipo TDaoIbx;
  • Linha 32 – comentei esta linha para deixar o registro de como era feito em UIB;
  • Linha 35 – Instanciamos nossa conexão IBX.

E quanto ao formulário que executa os comandos CRUD, quais alterações deverão ser feitas? NENHUMA!

É isso mesmo, nenhuma alteração!

Basta executar e testar:

orm-ibxteste1

orm-ibxteste2

orm-ibxteste3

orm-ibxteste4

Foi reportado que há um erro quando se faz pesquisa na chave primária utilizando campos string. Veremos isso numa outra oportunidade.

Antes de encerrar, gostaria de falar um pouco sobre uma novidade que, ao meu ver, demonstra uma mudança nos rumos da Embarcadero no que tange ao acesso a dados no Delphi. Como todos já devem saber, a Embarcadero lançou recentemente o FireDac, um novo conjunto de acesso a dados (ou seria velho?!? AnyDac ressurge!) com ampla variedade de recursos, como por exemplo, acesso nativo a diversos bancos de dados com alto desempenho. Eu andei analisando e me pareceu muito interessante. Aí surgem os questionamentos:

  • Mas essa já não era uma promessa do DbExpress?
  • Então, com o FireDac ainda temos a necessidade de criar um ORM? Quem já assistiu aos vídeos que estão rolando na net sabe o por quê desta pergunta.

E aí pessoal, quais são suas impressões a respeito?

Código fonte:

Persistencia15042013.rar

Abraços.

Desenvolvedor de software desde 1995. Em 1998, abriu sua própria empresa, a Lukas Sistemas, desde então passou a atender diversas empresas, principalmente autopeças. Apaixonado por Delphi, porém não o impede de flertar com outras linguagens sempre que possível. Mora na cidade de Balsas/MA com sua esposa e dois filhos.

5 thoughts on “TDaoIBX – Que tal um ORM Básico? Parte 11”

  1. Parabéns pela aula esta me ajudando muito.
    Gostaria que se possível criasse um TDaoDBX (DBExpress) como seria.

    Obrigado pela Atenção;

    1. Ok, eu não tinha a intenção de implementar Dbx, mas vou colocar na programação.

      Mas antes, ainda é necessário lapidar as classes já criadas, comparando as duas e retirando o que for desnecessário, tipo Tony Stark no Homem de Ferro 1.

      Obrigado pela visita.

  2. Muito legal saber que você ainda vai continuar esse projeto.
    Esse projeto é muito interessante e voltarei a acompanhar os posts, pois assim como nos posts anteriores tenho certeza que tenho muito a aprender com essas técnicas que você utiliza.
    Parabéns pelo belo trabalho.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.