O relacionamento Master-Detail é muito utilizado em sistemas para apresentar dados que possuem uma relação de dependência. Alguns exemplos comuns são pedidos e seus itens, clientes e seus endereços ou notas fiscais e seus produtos.
Neste artigo, veremos como configurar esse relacionamento no Delphi utilizando o FireDAC. O foco principal será o vínculo entre os dois DataSets, feito com apenas três propriedades.
O projeto completo utilizado neste exemplo está disponível no GitHub: Code4Delphi/Master-Detail — Master Detail Delphi
O que é um relacionamento Master-Detail?
No relacionamento Master-Detail, um registro do DataSet mestre pode estar relacionado a vários registros do DataSet detalhe.
No exemplo deste artigo:
- FDMemTableMaster representa os registros mestres;
- FDMemTableDetail armazena os registros de detalhe;
- o campo FDMemTableMaster.Id identifica o registro mestre;
- o campo FDMemTableDetail.id_master informa a qual registro mestre cada detalhe pertence.
Assim, quando o usuário navega pelos registros do mestre, o FireDAC apresenta automaticamente apenas os detalhes relacionados ao registro atual.
Componentes utilizados
O formulário do exemplo possui dois TFDMemTable, dois TDataSource e dois TDBGrid:

- FDMemTableMaster → DataSourceMaster → DBGrid1;
- FDMemTableDetail → DataSourceDetail → DBGrid2.
Os dados são criados em memória apenas para facilitar a demonstração. O mesmo conceito pode ser aplicado a outros DataSets do FireDAC, de acordo com a estratégia utilizada para carregar os dados.
📍 Configurando o vínculo Master-Detail 📍
O ponto principal do exemplo está no método ConfigurarVinculoMasterDetail:
procedure TMasterDetailView.ConfigurarVinculoMasterDetail;
begin
FDMemTableDetail.MasterSource := DataSourceMaster;
FDMemTableDetail.MasterFields := 'Id';
FDMemTableDetail.IndexFieldNames := 'id_master';
end;Estamos definindo as propriedades via código, mas você também pode fazer isso de forma RAD, diretamente pelo Object Inspector.
MasterSource
FDMemTableDetail.MasterSource := DataSourceMaster;A propriedade MasterSource pertence ao DataSet detalhe. Ela indica qual TDataSource fornece o registro mestre atual.
Como DataSourceMaster está conectado ao FDMemTableMaster, o FireDAC consegue acompanhar a navegação realizada no DataSet mestre.
MasterFields
FDMemTableDetail.MasterFields := 'Id';A propriedade MasterFields informa qual campo do DataSet mestre será relacionado ao campo definido em IndexFieldNames no detalhe.
IndexFieldNames
FDMemTableDetail.IndexFieldNames := 'id_master';A propriedade IndexFieldNames é configurada no DataSet detalhe e informa qual campo será utilizado para localizar e organizar os registros relacionados ao mestre.
Neste exemplo, id_master funciona como a chave de ligação dentro de FDMemTableDetail. Cada registro de detalhe possui nesse campo o Id do respectivo registro mestre.
Portanto, o vínculo deste exemplo pode ser representado da seguinte forma:
| DataSet mestre | DataSet detalhe |
|---|---|
| FDMemTableMaster.Id | FDMemTableDetail.id_master |
Em outras palavras, o FireDAC mantém visíveis no detalhe somente os registros em que id_master possui o mesmo valor do campo Id do registro mestre atual.
Como o vínculo funciona durante a navegação
Imagine que o registro atual de FDMemTableMaster possui Id = 5. O FireDAC utiliza esse valor para selecionar, em FDMemTableDetail, somente os registros cujo campo id_master também seja igual a 5.
Ao navegar para o mestre de Id = 6, o conteúdo apresentado no DBGrid2 é atualizado automaticamente. Não é necessário percorrer os registros manualmente nem aplicar um novo filtro a cada alteração do registro mestre.
Inserindo os dados de demonstração
O método InserirDadosTemp cria os dois DataSets em memória e adiciona os registros utilizados no exemplo:
FDMemTableMaster.CreateDataSet;
FDMemTableDetail.CreateDataSet;Para cada registro mestre, o código gera de três a dez registros de detalhe. O valor de LMasterId é gravado no campo id_master, estabelecendo a relação entre eles:
FDMemTableDetail.AppendRecord([LDetailId, LMasterId, LQuantidade, LUnitario, LDetailTotal]);Depois de inserir os detalhes, o registro mestre é adicionado com seu identificador e valor total:
FDMemTableMaster.AppendRecord([LMasterId, LMasterTotal]);Ordem de inicialização
No evento OnCreate do formulário, os dados são criados antes da configuração do relacionamento:
procedure TMasterDetailView.FormCreate(Sender: TObject);
begin
Self.InserirDadosTemp;
Self.ConfigurarVinculoMasterDetail;
end;Essa ordem garante que os TFDMemTable já estejam criados e com seus campos disponíveis quando o vínculo for configurado.
⭐ Relacionamentos com mais de um campo
Caso o relacionamento utilize uma chave composta, os campos podem ser informados separados por ponto e vírgula e na mesma ordem:
FDMemTableDetail.IndexFieldNames := 'id_empresa;id_pedido';
FDMemTableDetail.MasterSource := DataSourceMaster;
FDMemTableDetail.MasterFields := 'id_empresa;id';Nesse cenário, id_empresa do detalhe corresponde a id_empresa do mestre, enquanto id_pedido corresponde a id.
⬇️ Download do exemplo
Baixe o projeto, execute o exemplo e navegue pelos registros do primeiro grid para acompanhar a atualização automática dos detalhes:
Acessar o exemplo Master-Detail no GitHub


