domingo, 11 de julho de 2010

Campo Auto-incremento Firebird/Interbase

Sempre que visito fóruns, salas de bate papo e outras relacionadas a programação em Delphi, vejo que uma das dúvidas mais comuns é como projetar um bom sistema, e automático, de chave auto-incremento no Firebird/Interbase. Já vi muita coisa e acho que uma das soluções que eu adotei é bem produtiva e muito eficiente. O conceito não é novo mas eu nunca vi assim como vou propor.

Bom um dos primeiros passos para um bom sistema é uma boa padronização do BD. Toda a nomenclatura dos meus BD eu padronizo. Uma delas, é que mais nos interessa: o generator. O meu é formado da seguinte maneira (sempre) "GEN_[tabela]_ID". Seguindo esse mesmo conceito eu também padronizo a nomenclatura dos meus objetos no Delphi. SQLQuery = qryTABELA, DataSetProvider = dspTABELA, ClientDataSet = cdsTABELA. Eu também acho muito mais produtivo separar os DMs dessa forma: um RDM onde vai ficar as Queries e os DataSetProviders, assim facilmente poderemos "transformar" nossa aplicação CS em Multi-Tier, e um DM onde ficam os CDSs. Como a propriedade ProviderName do CDS é uma string ele não vai "ver" os DSP do RDM, assim colocamos o componente TLocalConnection no RDM em um TConnectionBroker no DM e apontamos sua propriedade Connection para o RDM->TLocalConnection. De posse dessas informações podemos prosseguir.

No RDM onde estão todos os DSP, aponte BeforeUpdateRecord de todos os DSP para o mesmo evento.

Essa é a assinatura do evento:

BeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet;

DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind;

var Applied: Boolean);

E no evento codifique assim:

Crie uma variável Campo do tipo TField e uma variável SQLStmt do tipo String e tbm uma variável Tabela do tipo String e por fim uma variável CustomSQLDataSet do tipo TCustomSQLDataSet. Não se esqueça de declarar e unit DB.

case UpdateKind of

ukInsert: // Inserindo um registro

begin

Campo := DeltaDS.Fields[0];

if (Campo.DataType = ftInteger) and Campo.IsNull then

begin

Tabela := UpperCase(IProviderSupport(SourceDS).PSGetTableName);

SQLStmt := Format(SELECT GEN_ID(GEN_%s_ID, 1) FROM RDB$DATABASE, [Tabela]);

SQLCon.Execute(SQLStmt, nil, @CustomSQLDataSet);

if Assigned(CustomSQLDataSet) then

begin

DeltaDS.Edit;

Campo.NewValue := CustomSQLDataSet.Fields[0].AsInteger;

DeltaDS.Post;

SysUtils.FreeAndNil(CustomSQLDataSet);

end;

end;

end;

Veja que em SQLStmt existe GEN_%s_ID, por isso eu uso um padrão de nomenclatura para os meus Generators no banco. Primeiro ele verifica se o campo[0], que eu sempre deixo a primary key, é do tipo Inteiro e se está nulo, porque eu posso ter atribuído o valor da chave antes, num caso em que vc tem Pedido e Ítens de Pedido, o valor da chave tem que ser atribuido antes, depois executo o SQLStmt apontando o ResultSet, que é um ponteiro, para o CustomSQLDataSet.

Se a pesquisa retornar dados o CustomSQLDataSet vai ter o valor do generator incrementado, aí é só atribuir o valor para o Delta.

Pronto, sua aplicação ja tem um campo auto-incremento totalmente automatizado !

Um abraço e até a próxima