Olá!
Estou de volta para mais um artigo desta série.
Gostaria de lembrar que por enquanto não estou disponibilizando os fontes. Mesmo porque, ainda não temos um código que faça jus o download. OK, confesso: quero que você quebre um pouco a cabeça, forçando a mente a trabalhar, melhorando a assimilação do conteúdo. 😡 Mas não se preocupe, em breve começarei a disponibilizar o link com os fontes.
Ajustes no código já trabalhado
Antes de iniciar, quero falar de uma pequena mudança que fiz no código do artigo anterior. Em Atributos.pas, eu alterei a classe TCampos, que antes era assim:
[sourcecode language=”delphi”]
TCampos = class(TCustomAttribute)
private
FTipo: TFieldTipo;
FPK: Boolean;
public
constructor Create(ATipo: TFieldTipo; APk: Boolean);
function IsPk: Boolean; virtual;
end;
[/sourcecode]
Agora, está assim:
[sourcecode language=”delphi”]
TCampos = class(TCustomAttribute)
public
function IsPk: Boolean; virtual;
end;
[/sourcecode]
Na function IsPK do TCamposPK:
[sourcecode language=”delphi”]
Resul := True;
[/sourcecode]
Na unit Teste.pas, acima da property ID:
[sourcecode language=”delphi”]
…
public
[TCampoPk]
property Id: Integer read FId write SetId;
…
[/sourcecode]
Excluí o construtor e os campos internos (FTipo e FPK). O Constructor agora não tem mais parâmetros.
A alteração foi feita para simplificar um pouco as coisas e tirar campos que não estavam sendo utilizados. Como estamos no início, pode ser que eu volte atrás, mas por enquanto vamos levar da maneira que está.
Aqui está o código completo da unit Atributo.pas:
[sourcecode language=”delphi”]
unit Atributos;
interface
uses
Rtti;
type
TNomeTabela = class(TCustomAttribute)
private
FNomeTabela: string;
public
constructor Create(ANomeTabela: string);
property NomeTabela: string read FNomeTabela write FNomeTabela;
end;
TCampos = class(TCustomAttribute)
public
function IsPk: Boolean; virtual;
end;
TCampoPk = class(TCampos)
public
function IsPk: Boolean; override;
end;
implementation
{ TCampos }
function TCampos.IsPk: Boolean;
begin
Result := False;
end;
{ TCampoPk }
function TCampoPk.IsPk: Boolean;
begin
Result := True;
end;
{ TNomeTabela }
constructor TNomeTabela.Create(ANomeTabela: string);
begin
FNomeTabela := ANomeTabela;
end;
end.
[/sourcecode]
Unidade Base
Temos que ter em mente que quanto mais abstrato for, melhor será o nosso ORM. Visto que, devemos tornar o processo de troca de um conjunto de componentes de acesso por outro o mais transparente possível. Vamos lá!
No Delphi, crie uma nova unit:
E salve com o nome de Base.pas.
O que nós queremos é poder ter acesso ao nosso banco de dados (Firebird) utilizando componentes como o IBX, UIB, DbExpress… sem tantos traumas, não é mesmo? Então, esta unit será a nossa base (é mesmo???) para alcançar este objetivo.
Vamos criar nossa primeira classe em Base.pas:
[sourcecode language=”delphi”]
unit Base;
interface
uses Classes;
type
IBaseDados = interface
[‘{9B0F9364-AB16-4C12-B4B7-4E2287840232}’]
end;
[/sourcecode]
Na verdade, é uma interface e tem o objetivo de ser uma interface padrão para todos componentes databases (TUIBDataBase – UIB, TIBDatabase – IBX, etc.). Nela vemos o código GUID (identificador global único [globally unique identifier]) gerado através das teclas Ctrl+Shift+G.
Vamos criar mais uma interface:
[sourcecode language=”delphi”]
ITransacao = interface
[‘{2F1DCA7A-E7F4-4EC3-BDB2-22B99C8CA7DB}’]
end;
[/sourcecode]
Esta interface será utilizada para os componentes “Transactions”, para controle das nossas transações.
Abaixo destas interfaces, vamos inserir um alias:
[sourcecode language=”delphi”]
…
type
IBaseDados = interface
[‘{9B0F9364-AB16-4C12-B4B7-4E2287840232}’]
end;
ITransacao = interface
[‘{2F1DCA7A-E7F4-4EC3-BDB2-22B99C8CA7DB}’]
end;
TTabela = class(TObject)
end;
…
[/sourcecode]
TTabela será uma classe sem propriedades e campos por nós definidos (pelo menos, não por enquanto). É claro que ela herda as propriedades e métodos de TObject.
Você pode estar se perguntando: já que não tem diferença para o TObject, o que justifica criar este alias?
Bom, o fato de necessitarmos do alias é que ele será utilizado em várias partes do nosso projeto, hora sendo uma classe pai de alguma outra classe hora sendo um parâmetro. Então, queremos restringir o uso destas classes, ou seja, que o parâmetro seja do tipo TTabela e não de outra classe qualquer derivada de TObject. Entendido? Ok!
Abra a unit Teste.pas. Altere a classe TTeste:
[sourcecode language=”delphi”]
unit Teste;
interface
uses Base, Rtti, Atributos;
type
[TNomeTabela(‘Teste’)]
TTeste = class (TTabela)
private
FHabitantes: Integer;
FDescricao: string;
FRendaPerCapta: Currency;
FId: Integer;
FData: TDateTime;
FEstado: string;
procedure SetData(const Value: TDateTime);
procedure SetDescricao(const Value: string);
procedure SetEstado(const Value: string);
procedure SetHabitantes(const Value: Integer);
procedure SetId(const Value: Integer);
procedure SetRendaPerCapta(const Value: Currency);
public
[TCampoPk]
property Id: Integer read FId write SetId;
property Estado: string read FEstado write SetEstado;
property Descricao: string read FDescricao write SetDescricao;
property Data: TDateTime read FData write SetData;
property Habitantes: Integer read FHabitantes write SetHabitantes;
property RendaPerCapta: Currency read FRendaPerCapta write SetRendaPerCapta;
end;
implementation
{ TTeste }
procedure TTeste.SetData(const Value: TDateTime);
begin
FData := Value;
end;
procedure TTeste.SetDescricao(const Value: string);
begin
FDescricao := Value;
end;
procedure TTeste.SetEstado(const Value: string);
begin
FEstado := Value;
end;
procedure TTeste.SetHabitantes(const Value: Integer);
begin
FHabitantes := Value;
end;
procedure TTeste.SetId(const Value: Integer);
begin
FId := Value;
end;
procedure TTeste.SetRendaPerCapta(const Value: Currency);
begin
FRendaPerCapta := Value;
end;
end.
[/sourcecode]
Veja que agora ela descende de TTabela e não mais de TObject (não diretamente). Adicione a unit Base ao uses.
Pronto, agora que nós temos as interfaces para o database e o transaction, vamos criar um DAO abstrato:
[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;
[/sourcecode]
Veja que nossa interface declara os métodos básicos de um CRUD (inserção, deleção e salvamento), faltando apenas a recuperação de dados. Implantaremos isso nos próximos artigos da série. Veja também, que temos os métodos de controle das transações:
- InTransaction: irá verificar se existe transação aberta;
- StartTransaction: irá iniciar uma nova transação;
- Commit: irá efetivar as alterações no banco de dados;
- RollBack: irá cancelar a transação aberta.
Abaixo, código completo de Base.pas:
[sourcecode language=”delphi”]
unit Base;
interface
uses Classes;
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;
implementation
end.
[/sourcecode]
Diagrama atual do nosso projeto:
Chegamos ao fim deste artigo. No próximo, iremos iniciar a criação da classe específica do UIB.
Obrigado e até a próxima!