Pular para conteúdo

Mapeamento Híbrido

O recurso de Mapeamento Híbrido é o "super poder" do BridgeFrameWork para lidar com sistemas legados. Ele permite que você use SQL customizado para performance ou complexidade, mas ainda trabalhe com objetos tipados na sua aplicação.

O Problema

Muitas vezes, uma query de relatório, um dashboard ou até mesmo um Grid complexo precisa de 5 JOINs, GROUP BY e sub-selects. Tentar escrever isso com métodos de ORM (Find, Where) é doloroso e ineficiente.

A Solução: SQL no Model, Objetos no Controller

1. No Model

Crie um método no seu Model específico que retorna um TFDMemTable. Aqui você tem liberdade total para escrever o SQL que precisar.

function TPedidoModel.ObterPedidosAtrasados(ADias: Integer): TFDMemTable;
const
  CSQL = 'SELECT p.* ' + 
         'FROM PEDIDO p ' + 
         'JOIN CLIENTE c ON c.ID = p.ID_CLIENTE ' + 
         'WHERE p.STATUS = ''A'' ' + 
         'AND p.DATA < %s';
begin
  // Executa o SQL formatado e retorna um TFDMemTable
  Result := Self.FindCustom(Format(CSQL, [
    QuotedStr(FormatDateTime('yyyy-mm-dd', Now - ADias))
  ]));
end;

2. No Controller (Intermediário)

Implemente um método no Controller que encapsule o acesso ao Model. Isso mantém o isolamento das camadas.

type
  TPedidoController = class(TBaseController<TPedido>)
  public
    function BuscarPendencias(ADias: Integer): TFDMemTable;
  end;

function TPedidoController.BuscarPendencias(ADias: Integer): TFDMemTable;
begin
  // Cast seguro pois sabemos que o Model de TPedidoController é um TPedidoModel
  Result := (Model as TPedidoModel).ObterPedidosAtrasados(ADias);
end;

3. Na View (Consumindo)

Na sua tela ou rotina, trabalhe com o Controller específico (tipado).

procedure TRelatorioController.ProcessarAtrasados;
var
  LLista: TObjectList<TPedido>;
  LDataSet: TFDMemTable;
  LController: TPedidoController; // Tipo específico
begin
  LLista := TObjectList<TPedido>.Create;

  // Obtém a instância já com o tipo correto (pode exigir cast se o Registry retornar IController)
  LController := TControllerRegistry.Instance.Get<TPedido> as TPedidoController;

  try
    // 1. Obtém o DataSet através do método do Controller
    LDataSet := LController.BuscarPendencias(30);

    // 2. Mapeia para Objetos
    // Como LController é TPedidoController (que herda de TBaseController), 
    // temos acesso direto ao LoadFromDataSet sem casts adicionais.
    LController.LoadFromDataSet(LLista, LDataSet);

    // 3. Itera sobre objetos fortemente tipados
    for var LPedido in LLista do
    begin
      if LPedido.Valor > 1000 then
        NotificarGerente(LPedido);
    end;

  finally
    LController.Free; // Se não for gerenciado pelo Registry/Interface
    LLista.Free;
    LDataSet.Free;
  end;
end;

Regras de Ouro

  1. Colunas: O seu SQL deve retornar colunas com os mesmos nomes definidos nos atributos [Column] da sua entidade. Colunas extras no SQL são ignoradas. Colunas faltantes no SQL ficarão com valor default no objeto.
  2. Performance: O mapeamento é feito em memória e é muito rápido. O gargalo será a execução do seu SQL no banco.