Motivação

Quando desenvolvemos aplicativos mobile, estes podem ter dois tipos de armazenamento: uma base de dados nativa, que fica localizada no próprio aparelho, ou então uma base localizada em um servidor de aplicação em um servidor externo. Nesse último caso, é necessário que o dispositivo esteja conectado à Internet para ter acesso às funções do software. Hoje em dia, uma outra alternativa bastante comum é manter duas bases de dados, ou seja, uma no aparelho e outra no servidor. Dessa forma, o usuário pode continuar trabalhando no aplicativo localmente e quando houver disponibilidade de conexão, seus dados locais podem ser transferidos para o servidor ou então os dados atualizados do servidor podem ser copiados para a base local.

Servidores DataSnap são um exemplo de tecnologia que pode ser utilizada para esse fim e, dentro desse contexto é importante conhecer as maneiras que permitem essa sincronização entre cliente e servidor. A execução chamada de Array DML é uma técnica que submete uma única instrução em SQL para a inserção de novos dados, utilizando um vetor de parâmetros que contém os novos registros. Essa técnica reduz a quantidade de comunicação entre o servidor e o cliente, o que garante uma melhor velocidade no tempo de execução dos comandos.

Passo 1: Criando o método no servidor

Para este exemplo, devemos criar uma nova aplicação DataSnap e dentro da unit ServerMethodsUnit adicionar os componentes de acesso a dados para buscar os registros no banco. Para isso, serão necessários quatro componentes: FDConnection, FDQuery, FDPhysMSSQLDriverLink e FDStanStorageJSONLink. O primeiro é utilizado para realizar a conexão com a base, o segundo para executar a instrução SQL, o terceiro representa o driver de acesso ao SGBD (sistema gerenciador de banco de dados) e, por fim, o último diz respeito ao formato de dados que trafegará pela rede (neste caso, arquivos). A Figura 1 apresenta esses componentes.

Componentes de acesso a dados
Figura 1. Componentes de acesso a dados

Além disso, é também necessário um método remoto nesta mesma unit para que os dados sejam retornados para a aplicação cliente. Seu código pode ser visto na Listagem 1.

Listagem 1. Função para buscar os dados do servidor

public
  function GetClientes(): TFDJSONDataSets;
end;
function TServerMethods1.GetClientes: TFDJSONDataSets;
begin
  FDQuery1.Active := False;
  FDQuery1.SQL.Add('select idcliente, nome, genero, escolaridade from clientes');
  Result := TFDJSONDataSets.Create;
  TFDJSONDataSetsWriter.ListAdd(Result, FDQuery1);
end;
  • Linhas 01, 02 e 03: Definição da função GetClientes() dentro da seção public da unit. O tipo de retorno do método é TFDJSONDataSets e na cláusula uses do arquivo o namespace Data.FireDACJSONReflect deve ser declarado;
  • Linha 06: A query é fechada para a execução de um novo comando;
  • Linha 07: Definimos o comando em SQL para buscar os dados tabela de clientes;
  • Linha 08: Criamos o objeto que armazenará o resultado da consulta;
  • Linha 09: Utilizando a classe TFDJSONDataSetsWriter, adicionamos à lista de retorno a variável Result, passando como parâmetro o componente FDQuery1, para que os dados relativos à consulta SQL sejam retornados.

Passo 2: Criando o método no cliente

A Figura 2 apresenta a interface gráfica da aplicação cliente, a qual é composta somente um um botão (TButton) e três componentes: FDConnection, que realiza a conexão com uma base de dados local; FDMemTable, que representa uma tabela em memória e que receberá os dados do servidor; e FDStanStorageJSONLink, para receber os dados no formato JSON. Em aplicações mobile, o componente de conexão pode estar ligado a uma base no SQLite, que é comumente utilizada para esse tipo de aplicativo. Quando o botão for clicado, os dados do servidor serão carregados na tabela em memória (FDMemTable1) e depois será feito o insert em massa para inserir todos os registros na base de dados local. O código da Listagem 2 apresenta esse processo, o qual é definido no evento OnClick do botão.

Interface gráfica da aplicação cliente
Figura 2. Interface gráfica da aplicação cliente
Listagem 2. Cópia dos dados do servidor para o cliente

procedure TForm2.Button1Click(Sender: TObject);
var
  dsClientes: TFDJsonDataSets;
  qryInsercao: TFDQuery;
  inserts: Integer;
begin
  dsClientes := ClientModule1.ServerMethods1Client.GetClientes();
  if TFDJSONDataSetsReader.GetListCount(dsClientes) = 1 then
  begin
    FDMemTable1.Active := false;
    FDMemTable1.AppendData(TFDJSONDataSetsReader.GetListValue(dsClientes, 0));
    inserts := FDMemTable1.RecordCount;
    qryInsercao := TFDQuery.Create(Self);
    qryInsercao.Connection := FDConnection1;
    qryInsercao.SQL.Add('insert into clientes values (:id, :nome, :genero, :escolaridade)');
    qryInsercao.Params.ArraySize := inserts;
    while not FDMemTable1.Eof do
    begin
      qryInsercao.ParamByName('id').AsInteger := FDMemTable1.FieldByName('idcliente').AsInteger;
      qryInsercao.ParamByName('nome').AsString := FDMemTable1.FieldByName('nome').AsString;
      qryInsercao.ParamByName('genero').AsString := FDMemTable1.FieldByName('genero').AsString;
      qryInsercao.ParamByName('escolaridade').AsInteger := FDMemTable1.FieldByName('escolaridade').AsInteger;
      FDMemTable1.Next;
    end;
    qryInsercao.Execute(inserts, 0);
  end;
end;
  • Linha 03: Declaramos a variável dataset do tipo TFDJSONDataSets, que armazena os registros carregados do servidor. É necessário informar na cláusula uses do formulário as units Data.FireDACJSONReflect e ClientModuleUnit1;
  • Linha 04: Definimos a variável do tipo FDQuery para executar as inserções na base de dados local;
  • Linha 05: Declaramos uma varíavel do tipo inteiro que armazenará a quantidade de registros que foram retornados do servidor;
  • Linha 07: Invocamos o método GetClientes, declarado no servidor e acessado por meio da unit ClientModule1. A variável dsClientes receberá todos os registros provenientes da consulta SQL executada no servidor;
  • Linha 08: Verificamos se algum dataset foi retornado do servidor, para posteriormente executar a cópia dos registros;
  • Linhas 10, 11 e 12: Desativamos a tabela em memória, copiamos os dados do servidor para ela e então a variável inserts recebe a quantidade de registros retornados;
  • Linhas 13, 14 e 15: Criamos a query em tempo de execução, a associamos ao componente de conexão e definimos o comando insert para inserir os dados, recebendo quatro parâmetros (id, nome, genero e escolaridade);
  • Linha 16: A propriedade ArraySize da query recebe a quantidade de registros que serão inseridos por meio de Array DML;
  • Linhas 17 a 24: Por meio de uma estrutura de repetição, percorremos a tabela em memória registro por registro e associamos os parâmetros da query de inserção aos respectivos valores da memory table;
  • Linha 25: Por meio do comando execute, executamos todos os comandos de inserts.

Com isso, temos uma estrutura que primeiramente faz a leitura dos registros do servidor e posteriormente os insere em uma base de dados local. Conforme mecionado anteriormente, esse código pode ser utilizado para sincronizar aplicação cliente e servidora, mantendo-as atualizadas.