M/D (3 níveis em cascata) com Firedac + CacheUpdate

26/04/2022

0

Pessoal, bom dia!

Sobre as configurações do M/D com o FireDAC, acredito que tudo seja bastante simples e intuitivo. No meu caso, utilizo o PostgreSQL e tudo aparentemente roda com perfeição.

Recentemente, tive a necessidade de fazer um M/D em três níveis: mestre->detalhe->detalhe->detalhe. O comportamento é bem estranho e parece que estou deixando alguma coisa passar, pois não estão funcionando os recursos de propagar nos detalhes e ao alterar um mestre (seja o principal ou um dos detalhes mestres), o seu detalhe imediato perde dados.

Abaixo, o exemplo dos dois métodos usados para configurar o M/D dos datasets e mais abaixo, as consultas realizadas e configuradas (todas com as FKs devidamente definidas).

O esquema é um cadastro de tabelas salariais com a seguinte estrutura (em PostgreSQL):
Master -> tabelas_salariais [ id pk ]
Detalhe 1 -> tabelas_salariais_classes [ id pk, idTabela fk->tabelas_salariais(id) ]
Detalhe 2 -> tabelas_salariais_niveis [ id pk, idClasse fk->tabelas_salariais_classes(id) ]
Detalhe 3 -> tabelas_salariais_valores [ id pk, idNivel fk->tabelas_salariais_niveis(id) ]


Métodos para configuração:

class procedure TUtil.Dados.prepararConsultaMestre(mestre: TFDRdbmsDataSet);
begin

  mestre.CachedUpdates := true;
  mestre.SchemaAdapter := TFDSchemaAdapter.Create( mestre );
  mestre.UpdateOptions.CheckRequired := false;

end;

class procedure TUtil.Dados.prepararConsultaDetalhes(detalhes: TFDRdbmsDataSet; dsMestre: TDataSource; campoMestre, camposIndice: string);
begin

  detalhes.MasterSource := dsMestre;
  detalhes.MasterFields := campoMestre;
  detalhes.IndexFieldNames := camposIndice;
  detalhes.CachedUpdates := true;
  detalhes.SchemaAdapter := TFDSchemaAdapter( TFDRdbmsDataSet( dsMestre.DataSet ).SchemaAdapter );
  detalhes.FetchOptions.DetailCascade := true;
  detalhes.UpdateOptions.CheckRequired := false;

end;


Consultas:

  ///  Tabela salarial
  tabela := TFDQuery.Create( Self );
  tabela.SQL.Add( ''select * from tabelas_salariais where id = :id'' );
  tabela.par( id ); <- o método par já faz a configuração para o parâmetro
  TUtil.Dados.prepararConsultaMestre( tabela );
  tabela.Open;

  ///  Classes de tabelas
  classe := TFDQuery.Create( Self );
  classe.SQL.add( ''select * from tabelas_salariais_classes where idTabela = :id order by codigo'' );
  classe.par( id ); <- o método par já faz a configuração para o parâmetro
  TUtil.Dados.prepararConsultaDetalhes( classe, ds, ''id'', ''idTabela'' );
  classe.Open;

  ///  Níveis salariais
  nivel := TFDQuery.Create( Self );
  nivel.SQL.Add( ''select * from tabelas_salariais_niveis where idClasse = :id order by codigo'' );
  nivel.ParamByName( ''id'' ).DataType := ftInteger;
  TUtil.Dados.prepararConsultaDetalhes( nivel, dsClasse, ''id'', ''idClasse'' );
  nivel.Open;

  ///  Valores salariais
  valor := TFDQuery.Create( Self );
  valor.SQL.Add( ''select * from tabelas_salariais_valores where idNivel = :id order by iniValidade desc'' );
  valor.ParamByName( ''id'' ).DataType := ftInteger;
  TUtil.Dados.prepararConsultaDetalhes( valor, dsNivel, ''id'', ''idNivel'' );
  valor.Open;

Agradeço antecipadamente qualquer dica ou sugestão, bem como indicar algum possível erro na estrutura.
Júlio Ferreira

Júlio Ferreira

Responder

Posts

27/04/2022

Emerson Nascimento

os datasources estão conectados aos datasets?
no trecho das consultas você está criando os objetos em tempo de execução. em que momento está sendo feita a ligação do dataset com o datasource?

nomeie os datasource de modo que possamos identificar a relação com o dataset (dsClasse, dsNivel).
///  Tabela salarial
tabela := TFDQuery.Create( Self );
tabela.SQL.Add( ''select * from tabelas_salariais where id = :id'' );
tabela.par( id ); <- o método par já faz a configuração para o parâmetro
TUtil.Dados.prepararConsultaMestre( tabela );
dsTabela.dataset := tabela; // faz a ligação com o datasource
tabela.Open;
 
///  Classes de tabelas
classe := TFDQuery.Create( Self );
classe.SQL.add( ''select * from tabelas_salariais_classes where idTabela = :id order by codigo'' );
classe.par( id ); <- o método par já faz a configuração para o parâmetro
TUtil.Dados.prepararConsultaDetalhes( classe, dsTabela, ''id'', ''idTabela'' );
dsClasse.dataset := classe; // faz a ligação com o datasource
classe.Open;
 
///  Níveis salariais
nivel := TFDQuery.Create( Self );
nivel.SQL.Add( ''select * from tabelas_salariais_niveis where idClasse = :id order by codigo'' );
nivel.ParamByName( ''id'' ).DataType := ftInteger;
TUtil.Dados.prepararConsultaDetalhes( nivel, dsClasse, ''id'', ''idClasse'' );
dsNivel.dataset := nivel; // faz a ligação com o datasource
nivel.Open;
 
///  Valores salariais
valor := TFDQuery.Create( Self );
valor.SQL.Add( ''select * from tabelas_salariais_valores where idNivel = :id order by iniValidade desc'' );
valor.ParamByName( ''id'' ).DataType := ftInteger;
TUtil.Dados.prepararConsultaDetalhes( valor, dsNivel, ''id'', ''idNivel'' );
dsValor.dataset := valor; // faz a ligação com o datasource
valor.Open;
Responder

27/04/2022

Júlio Ferreira

os datasources estão conectados aos datasets?
no trecho das consultas você está criando os objetos em tempo de execução. em que momento está sendo feita a ligação do dataset com o datasource?

nomeie os datasource de modo que possamos identificar a relação com o dataset (dsClasse, dsNivel).


Olá Emerson, boa tarde!
Sim, os datasources estão definidos em modo de design. Na verdade, por uma falha minha na postagem, removi a linha que define a consulta aos seus respectivos datasources, mas considere isso como ok.

Obrigado.
Responder

27/04/2022

Júlio Ferreira

A codificação correta seria essa, levando em consideração que os objetos TDataSource estão definidos em design.

///  Tabela salarial
tabela := TFDQuery.Create( Self );
tabela.SQL.Add( ''select * from tabelas_salariais where id = :id'' );
tabela.par( id ); <- o método par já faz a configuração para o parâmetro
TUtil.Dados.prepararConsultaMestre( tabela );
tabela.Open;

ds.DataSet := tabela;
  
///  Classes de tabelas
classe := TFDQuery.Create( Self );
classe.SQL.add( ''select * from tabelas_salariais_classes where idTabela = :id order by codigo'' );
classe.par( id ); <- o método par já faz a configuração para o parâmetro
TUtil.Dados.prepararConsultaDetalhes( classe, dsTabela, ''id'', ''idTabela'' );
classe.Open;

dsClasse.DataSet := classe;
  
///  Níveis salariais
nivel := TFDQuery.Create( Self );
nivel.SQL.Add( ''select * from tabelas_salariais_niveis where idClasse = :id order by codigo'' );
nivel.ParamByName( ''id'' ).DataType := ftInteger;
TUtil.Dados.prepararConsultaDetalhes( nivel, dsClasse, ''id'', ''idClasse'' );
nivel.Open;

dsNivel.DataSet := nivel;
  
///  Valores salariais
valor := TFDQuery.Create( Self );
valor.SQL.Add( ''select * from tabelas_salariais_valores where idNivel = :id order by iniValidade desc'' );
valor.ParamByName( ''id'' ).DataType := ftInteger;
TUtil.Dados.prepararConsultaDetalhes( valor, dsNivel, ''id'', ''idNivel'' );
valor.Open;

dsValor.DataSet := valor;
Responder

27/04/2022

Júlio Ferreira

Não consigo apagar e nem editar uma postagem, coloquei um trecho errado.
O mestre é o ds e não dsTabela.

TUtil.Dados.prepararConsultaDetalhes( classe, ds, ''id'', ''idTabela'' );
Responder

Assista grátis a nossa aula inaugural

Assitir aula

Saiba por que programar é uma questão de
sobrevivência e como aprender sem riscos

Assistir agora

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar