No artigo anterior, iniciamos a construção da classe TDaoUIB. Através de um Ctrl+Shift+C, geramos os três métodos abaixo:
[sourcecode language=”delphi”]
function TDaoUib.Inserir(ATabela: TTabela): Integer;
begin

end;

function TDaoUib.Salvar(ATabela: TTabela): Integer;
begin

end;

function TDaoUib.Excluir(ATabela: TTabela): Integer;
begin

end;
[/sourcecode]

Vamos então, neste artigo, implementar o método Excluir.

Método Excluir de TDaoUib

Já criamos os métodos responsáveis por pegar o nome da tabela e os campos da chave primária. Agora, observe a declaração do método Excluir da unidade DaoUib.pas:

[sourcecode language=”delphi”]
function TDaoUib.Excluir(ATabela: TTabela): Integer;
begin

end;
[/sourcecode]

Nele, estou recebendo um parâmetro do tipo TTabela. Desta forma, como eu já terei o nome da tabela, bastará definir quais os tipos dos campos da chave primária, ou seja, se é um campo string, inteiro, data, etc.

Diante destas informações, o método Excluir fica assim definido:

[sourcecode language=”delphi”]
function TDaoUib.Excluir(ATabela: TTabela): Integer;
var
NomeTab: string;
CamposPk: TResultArray;
Sep: string; //separador
Campo: string;

Contexto : TRttiContext;
TipoRtti : TRttiType;
PropRtti : TRttiProperty;
begin
NomeTab := PegaNomeTab(ATabela);

CamposPk := PegaPks(ATabela);

Contexto := TRttiContext.Create;
try
TipoRtti := Contexto.GetType(ATabela.ClassType);

with Qry do
begin
close;
SQL.Clear;
SQL.Add(‘Delete from ‘ + NomeTab);
SQL.Add(‘Where’);
Sep := ”;
// percorrer todos os campos da chave primária
for Campo in CamposPk do
begin
SQL.Add(Sep + Campo + ‘= :’ + Campo);
Sep := ‘ and ‘;
// setar o valor de cada parâmetro
for PropRtti in TipoRtti.GetProperties do
if CompareText(PropRtti.Name, Campo) = 0 then
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
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;
// executa delete
Prepare();
ExecSQL;
Result := RowsAffected;
end;
finally
Contexto.free;
end;
end;
[/sourcecode]

Analisando o código

  • No início do código (linha 12-18), pegamos o nome da tabela, criamos um contexto e pegamos o TipoRtti.
  • Da linha 20 a 25, preparamos nossa query com os comandos SQL.
  • Linha 28 é o início do loop em todos os campos da chave primária.
  • Na linha 33, fazemos um loop em todas as propriedades do objeto passado no parâmetro, comparando (linha 34) se o nome da propriedade é igual ao nome do campo da chave primária.
  • Se sim, iremos definir (linha 36-52) o tipo (TypeKind) do parâmetro (Params.ByNameAsCurrency) de cada campo.
  • Se o tipo não for encontrado, teremos um exceção na linha 58.
  • Na linha 65, executamos a nossa query.
  • O Result da função (linha 66) irá retornar a quantidade de registros afetados.

Olhando para a relação dos tipos informados no case (linha 36-52), veja que faltam alguns campos, como por exemplo, TDateTime e Blob. Segure o Ctrl e clique em cima de TypeKind na linha 36. Iremos acessar a unidade Rtti:
[sourcecode language=”delphi”]

public
function ToString: string; override;
property Handle: PTypeInfo read GetHandle;
// QualifiedName is only available on types declared in interface section of units;
// i.e. IsPublicType is true.
property QualifiedName: string read GetQualifiedName;
property IsPublicType: Boolean read GetIsPublicType;
property TypeKind: TTypeKind read GetTypeKind;
// The size of a location (variable) of this type.
property TypeSize: Integer read GetTypeSize;
property IsManaged: Boolean read GetIsManaged;

[/sourcecode]

Vemos que o TypeKind é do tipo TTypeKind. Segure novamente o Ctrl e clique em cima de TTypeKind. Acessamos a unidade TypInfo:
[sourcecode language=”delphi”]

type
TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray, tkUString,
tkClassRef, tkPointer, tkProcedure);
[/sourcecode]

Note que na relação não tem definido, por exemplo, o formato para campos data.

E aí você se pergunta: Como assim? Não tem um tipo para o formato data? Se é assim, como irei gravar campos deste tipo na minha tabela?

No Delphi, o tipo TDateTime é DoubleTypeKind = tkFloat. Mas não se preocupe com isso agora, visto que veremos como resolver este pequeno problema quando estivermos implementando o método Inserir.

Evitando repetição de código

Bom, poderíamos dizer que o método Excluir está implementado. Mas peço que observe mais uma vez o código. Vou lhe dar 5 segundos… 4… 3… 2… 1.

Percebeu algo? Sim? Não?

Tenho certeza que você percebeu que o case com a definição dos parâmetros da query não só será utilizado no método Excluir, como também no método Inserir e Salvar, não é mesmo? Sendo assim, se seguirmos este pensamento, iremos repetir o mesmo código nestes três métodos.

Para resolver, vamos recortar o case do método Excluir, e no seu lugar chamar uma nova procedure:

[sourcecode language=”delphi”]
function TDaoUib.Excluir(ATabela: TTabela): Integer;
var
NomeTab: string;
CamposPk: TResultArray;
Sep: string; //separador
Campo: string;

Contexto : TRttiContext;
TipoRtti : TRttiType;
PropRtti : TRttiProperty;
begin
NomeTab := PegaNomeTab(ATabela);

CamposPk := PegaPks(ATabela);

Contexto := TRttiContext.Create;
try
TipoRtti := Contexto.GetType( ATabela.ClassType );

with Qry do
begin
close;
SQL.Clear;
sql.Add(‘Delete from ‘ + NomeTab);
sql.Add(‘Where’);
//percorrer todos os campos da chave primária
Sep := ”;
for Campo in CamposPk do
begin
sql.Add(Sep+ Campo + ‘= :’ + Campo);
Sep := ‘ and ‘;
// setando os parâmetros
for PropRtti in TipoRtti.GetProperties do
if CompareText(PropRtti.Name, Campo) = 0 then
begin
ConfigParametro(Qry, PropRtti, Campo, ATabela); // <-- recortamos o case e no seu lugar inserimos uma procedure end; end; //executa delete Prepare(); ExecSQL; Result := RowsAffected; end; finally Contexto.free; end; end; [/sourcecode]

Declare a nova procedure no private da classe:
[sourcecode language=”delphi”]

type
TDaoUib = class(TInterfacedObject, IDaoBase)
private
FDatabase: TUIBDataBase;
FTransacao: TUIBTransaction;
// Este método configura os parâmetros da AQuery.
procedure ConfigParametro(AQuery: TuibQuery; AProp: TRttiProperty; ACampo: string; ATabela: TTabela); <-- aqui public ... [/sourcecode] Dê Ctrl+Shift+C para gerar o método, e em seguida, implemente o código do case:
[sourcecode language=”delphi”]
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
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;
[/sourcecode]

Pronto! Assim, evitamos a repetição desta parte. Poderemos utilizar este mesmo código no método Inserir e Salvar.

Para encerrar, proponho um pequeno desafio

Estamos chegando ao final deste artigo, mas antes quero que novamente analise o método Excluir. Vou deixar um pequeno desafio:

Tem alquma coisa no código que não está lhe agradando?
Será que, ao projetarmos a base deste código para os métodos Inserir e Salvar, não percebemos algo que poderia ser melhorado?

Fica o questionamento. Comentários são sempre bem-vindos!

Abraços e até a próxima!

One thought on “Método Excluir em TDaoUIB – Que tal um ORM Básico? Parte 5”

  1. O artigo e o melhor que já vi, somente uma consideração nas instruções sql eu gosto de utilizar o recurso de parâmetros pois consiste em uma boa pratica de programação.

Deixe um comentário

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