Cache local

O sistema armazena os dados de duas formas, em banco de dados relacional e em cache local.

  • O cache local é uma cópia de algumas tabelas do banco de dados salvas localmente para se ter um acesso mais rápido a alguns dados.
  • A maioria dos sistemas usa o cache local para guardar os dados com maior frequência de leitura e menor incidência de alterações. No sistema, a configuração que define se uma tabela deve ou não ir para o cache local fica nos arquivos de definição de classe (x-class ou x-model).
  • O cache será armazenado no local onde o Engine estiver em execução, ou seja, se um usuário possui o Engine executando em sua máquina, lá haverá um armazenamento em cache de algumas tabelas.
  • Devido ao seu tamanho e natureza de grande incidência de edições, as tabelas de movimentação não são armazenadas no cache.

Manipulando um DataSet

Criado um DataSet

Criaremos um DataSet e colocaremos valores dele. Sempre imagine o modelo de uma tabela.

  • Uma das formas de se criar um DataSet é instanciá-lo a partir da classe DataSet.

    const teste = new DataSet();`
    
  • Após a instância do dataset, precisamos definir sua estrutura, ou seja, os seus campos e respectivos tipos. Fazendo um paralelo do dataset com uma tabela, estamos criando as suas colunas, definindo o nome e o tipo de valor que será guardado em cada uma. Após montarmos a estrutura de nosso dataset, temos que finalizar a criação invocando o método create(). Observe:

    teste.createField("CODIGO", "int32");
    teste.createField("NOME", "string", 30);
    teste.createField("ABREVIACAO", "string", 10);
    teste.createField("BANCO", "int32");
    teste.createField("SALDO", "int32");
    teste.create("CLIENTES");
    
  • Após a estrutura definida, preencheremos os campos do nosso DataSet. A sintaxe é simples. Para adicionarmos uma nova linha (registro), utilizaremos o método append().

  • Para adicionarmos os valores nos campos do novo registro usamos o método setField. Veremos abaixo com mais detalhes.

  • Para acessar os valores, usamos diferentes métodos a depender do tipo da variável que se quer obter. São eles os métodos str, num, bool, dbkey e date. Também pode ser usado o método val, mas este com variação do tipo de retorno. O método retorna o valor do campo informado ou do último campo de uma expressão caso o primeiro campo dessa expressão contenha a chave de um registro que faça parte do cache local. Exemplos:

    const nome = ds.str('USUARIO.NOME');
    const idade = ds.num('USUARIO.IDADE');
    const testeCodigo = teste.num('CODIGO');
    
  • Existe outra maneira de acessar valores tanto para leitura quanto escrita, com o formato teste.<nome do campo>, como por exemplo teste.codigo. No entanto, recomenda-se usar da maneira explicada acima.

  • Após cada inserção temos que confirmar as mudanças chamando o método post(). Toda chamada ao método append(), logo após a definição dos valores dos campos, tem que ser finalizada com uma chamada ao método post(), uma espécie de commit da inserção daquele registro. Para melhor entendimento, analise o código abaixo:

    teste.append();       // << Início da inserção do primeiro registro
    teste.setField('CODIGO', 1);
    teste.setField('NOME', 'Alberto Albuquerque');
    teste.setField('ABREVIACAO', 'AA');
    teste.setField('BANCO', 1);
    teste.setField('SALDO', 500);
    teste.post();         // << Fim da inserção do primeiro registro
    
    teste.append();       // << Início da inserção do segundo registro
    teste.setField('CODIGO', 2);
    teste.setField('NOME', 'Breno Angelo Camelo');
    teste.setField('ABREVIACAO', 'BAC');
    teste.setField('BANCO', 2);
    teste.setField('SALDO', -300);
    teste.post();         // << Fim da inserção do segundo registro
    

Percorrendo um DataSet

Quando falamos em percorrer um DataSet, queremos dizer que “visitaremos” registro a registro do DataSet. Esse processo é usado para muitos usos como o de alterar valores, verificar se um determinado campo tem um valor esperado, etc.

Existem duas maneiras de se percorrer um DataSet usando as estruturas de laço da linguagem. Uma é com o while e a outra com o for. Veremos cada uma a seguir com exemplos diferentes.

Percorrendo com o while

Percorreremos todas os registros desse DataSet e apagaremos aqueles cujo campo ABREVIACAO é igual a “MA”. Para fazer isso, usaremos uma estruturas de repetição while e algumas funções da classe DataSet. Acompanhe o exemplo abaixo:

teste.first();
while (!teste.eof){
    if (teste.s == 'MA'){
    teste.del();
    } else {
    teste.next();
    }
}
  • Explicando mais detalhadamente o código acima:

    1. O método teste.first() posiciona o cursor no primeiro registro do DataSet.
    2. O Atributo teste.eof, quando verdadeiro, indica que foi invocado um teste.last() ou que, estando no último registro, foi invocado um teste.next().
    3. O método teste.next() posiciona o cursor no próximo registro do DataSet. Conforme vimos no item acima, se o registro posicionado já for o último, ao se invocar o teste.next(), o atributo teste.eof passa a ter valor lógico verdadeiro.
  • Observe que a função teste.del(), ao ser invocada, além de deletar o registro, já posiciona o cursor no próximo registro, dispensando a chamada de um teste.next() para esta finalidade.

Percorrendo com o for

Como exemplo, alteraremos os campos ABREVIACAO e NOME quando o campo CODIGO for maior que 5.

for (teste.first(); !teste.eof; teste.next()) {
    if (teste.num('CODIGO') > 5) {
    teste.setField('ABREVIACAO', 'CBP');
    teste.setField('NOME', 'Caio Barcelos Pereira');
    }
}

Manipulando o banco de dados

Consultando o banco de dados

Temos dois métodos principais de comunicação com o banco de dados:

  • database.query() Responsável por enviar queries ao banco e retornar um DataSet das consultas. Este método é utilizado para realizar o comando SELECT no banco de dados.
  • database.executeSQL() Responsável por enviar queries ao banco e não retornar nenhum valor. Este método é útil quando se deseja enviar um comando do tipo INSERT, DELETE, CREATE, DROP, etc. Observem que estes comandos não retornam um DataSet como resultado de sua execução.

Alterando informações do banco de dados através de Data Sets

Conheceremos agora o método database.applyUpdates(). Neste exemplo, vamos assumir que a tabela iTeste está vazia e que é uma tabela do sistema. A execução da consulta abaixo fará com que seja preparado um DataSet com a mesma estrutura da tabela iTeste.

const teste = database.query('SELECT * FROM iTeste');

Como dito anteriormente, nossa tabela não possui nenhum registro, então o DataSet teste estará vazio. Um DataSet vazio equivale a uma tabela sem registros.

Adicionaremos um registro no DataSet vazio e, após criar este registro, utilizaremos o método database.applyUpdates() para refletir, no banco de dados, as ações que foram feitas no DataSet, criando finalmente o registro no banco de dados.

A gravação ocorre na tabela correta pois o DataSet teste está vinculado à tabela iTeste desde sua criação. Segue o exemplo completo abaixo:

OBS: Para usar este método de gravação, a tabela em uso precisa, necessariamente, ter sido definida dentro do sistema, ou seja, através de um arquivo x-model ou x-class. A definição do campo iKey é apenas para exemplo. Dentro de nosso sistema, a definição do valor que ficará no campo iKey é feita de forma automática e transparente.

const teste = database.query('SELECT * FROM iTeste');

teste.append();
teste.setField('ikey', 2);
teste.setField('iClass', 99);
teste.setField('iCode', 'RW');
teste.setField('iName', 'Ricardo Wagner');
teste.post();

database.applyUpdates(teste);

Para alterar e apagar um registro, os passos são os mesmos, ou seja:

  • Preparar o DataSet a partir de uma consulta ao banco de dados;
  • Modificar o DataSet ao incluir, alterar ou apagar registros;
  • Invocar o método database.applyUpdates().

Acompanhe o trecho abaixo em que editamos e apagamos o registro que inserimos na tabela iTeste.

// Modificando o registro inserido
var teste = database.query('SELECT * FROM iTeste WHERE iKey = 2');
teste.setField('iClass', 99);
teste.setField('iCode', 'PL');
teste.setField('iName', 'Pedro de Lara');
teste.post();
database.applyUpdates(teste);

// Apagando o registro adicionado  
dsTeste = database.query('SELECT iKey, iVersion FROM iTeste WHERE iKey = 2');
dsTeste.del();
database.applyUpdates(teste);

Manipulando o cache local

O Cache Local não é como um banco de dados Oracle, Postgres ou SqlServer. Você não pode enviar consultas diretamente a ele como uma query normal a um banco de dados relacional. Todavia, podemos realizar as operações básicas, como criar tabela, pegar uma tabela, apagar uma tabela, etc. As tabelas retornadas do Cache Local serão tratadas como DataSets quando retornadas para a nossa aplicação.

Tabelas do Sistema e Cache Local

Mostraremos abaixo, com base naquela nossa tabela criada no tutorial, como devemos, e o que podemos fazer, com o Cache Local.

  • Pegaremos a nossa tabela iTeste do Cache Local. A mesma, quando foi criada pelo arquivo que define a classe, foi definida que faria parte do cache.

Para trazer a iTeste do Cache, usaremos o objeto global dbCache, que é uma instancia da Classe DBCache.

if ( dbCache.hasTable('iTeste') ) {
  ds = dbCache.getTable('iTeste');
}

No código acima verificamos se a nossa tabela iTeste está gravada no cache local, caso esteja pegamos essa tabela como um DataSet e colocamos na variável ds.

Perceba que o tempo de busca ao DataSet foi mínimo. Já que não existe conexão com o banco de dados.

Após o retorno, temos um DataSet normal com todas as seus métodos e propriedades. Agora é só aprofundar um pouco mais os seus conhecimentos sobre DataSet.

Para manter o Cache sempre sincronizado com o banco de dados, há uma execução independente que é ativada depois que uma requisição é feita ao Engine que age como Servidor de Aplicação e a cada período de 30 segundos (este intervalo pode ser configurado).

Banco de Dados Local

O Cache também pode funcionar como um banco de dados local. Você pode criar, editar, deletar tabelas, bases de dados, etc. Tudo isso de uma maneira prática e rápida usando apenas duas classes:

  • IdoDB: Responsável pelas tabelas do banco local. Excluir, Pegar e Verificar se existe tabelas são algumas funções que essa classe exerce.
  • IdoDBManager: Responsável pela base de dados. Excluir, Criar, Verificar se existe e Carregar são algumas das funções que essa classe exerce.

Após explicarmos o dever de cada classe veremos como criar uma base de dados e uma tabela no nosso cache local.

Para começar criaremos uma base de dados invocando o método createDataBase() de um objeto já instanciado globalmente chamado de idoDBManager.

idoDBManager.createDatabase('TESTE');

Agora damos um load da base de dados para uma variável. Essa função retornará uma instância da classe IdoDB.

const idoDB = idoDBManager.loadDatabase('TESTE');

Criaremos um DataSet passando como parâmetro o objeto idoDB.

const ds = new DataSet(idoDB);
ds.createField('Codigo', 'int32');
ds.createField('Nome', 'string', 10);
ds.createField('Valor', 'int32');
ds.create('Clientes');

Inseriremos os registros no DataSet.

ds.append();
ds.first();
ds.setField('CODIGO', 1)
ds.setField('NOME', 'João');
ds.setField('VALOR', 100);
ds.post();

Pronto, temos uma tabela guardada no banco de dados local. Vamos deleta-la?

idoDB.dropTable('Clientes');

Agora apagando a base de dados:

idoDBManager.deleteDatabase('TESTE');