Desenvolvendo em BREW - Parte 3 – Etapa 03

Veja neste artigo o estudo de caso: Persistência no “Fuel Manager”.

Desenvolvendo em BREW - Parte 3 – Etapa 03

Estudo de Caso: Persistência no “Fuel Manager”

Após apresentarmos as principais funcionalidades de cada uma dessas APIs, vamos agora aplicá-las no contexto do estudo de caso Fuel Manager (o código completo da aplicação pode ser encontrado para download no portal da WebMobile).

Para facilitar o manuseio da persistência na nossa aplicação, vamos criar uma classe C++ que será responsável pelo gerenciamento da persistência em registro, de modo que as classes que controlam as telas da aplicação não tenham que conhecer as funções da API de registros em BREW. Para tal, vamos criar um arquivo de cabeçalho DB.h onde definiremos todas as estruturas necessárias para o funcionamento da nossa classe, que também vai se chamar DB.

Como vimos no começo deste artigo, nossos registros possuirão quatro campos: data de abastecimento, litros de gasolina, preço do litro da gasolina, e quilometragem. Para facilitar as buscas, usaremos o campo de data como chave (Data e hora), ou seja, vamos restringir o uso da aplicação permitindo apenas um abastecimento por dia/hora.

Na Listagem 2 temos o código de DB.h. Neste arquivo criamos a classe DB com suas funções-membro, e um tipo enumeração FUEL_MANAGER_FIELDNAME (Listagem 2 - linha 17) para facilitar a criação dos campos de cada registro.

O construtor e o destrutor servirão para inicializar e liberar as variáveis. As funções open e close servem para abrir e fechar a tabela; insert serve para inserir um novo registro e update para atualizações (Listagem 2 – seção public:). Para remoções usaremos o método remove e deleteDB para remover a tabela fisicamente do aparelho (Listagem 2 – seção public:). O leitor pode criar novas funções para realizar buscas pelos outros campos da tabela. Para consultar se a tabela está vazia e a quantidade de registros, devem ser chamados os métodos isEmpty e size (Listagem 2 – seção public:). Grande parte dos métodos retorna valores do tipo int16, como uma forma de indicar ao usuário da classe se a operação foi bem sucedida (retornando zero) ou mal sucedida (retornando -1).

 

Listagem 2. Conteúdo do DB.h

001 #ifndef DB_HEADER

002 #define DB_HEADER

003

004 #include "AEEStdLib.h"

005 #include "AEEDB.h"

006 #include "FuelManager.h" // Header principal da aplicação

007

008 class DB {

009private:

010char baseName[256];

011IShell* pIShell;

012IDatabase* pIDatabase;

013IDBMgr* pIDBMgr;

014AEEDBField pDBFields[4];

015

016//4 campos: data, litros , preço , quilometragem

017typedef enum {

018DATE=AEEDBFIELD_NONE,

019GAS,

020GAS_PRIZE,

021TOTAL_KM

022} FUEL_MANAGER_FIELDNAME;

023

024//função privada

025int16 newRecord(AECHAR * date, float* litres, float* prize,  int32* totalKm);

026

027public:

028

029DB(char* baseName, FuelManager *fuelManager);

030~DB();

031int16 open();

032int16 close();

033int16 insert(AECHAR * date, float* litres, float* prize,  int32* totalKm);

034int16 remove(AECHAR * date);

035int16 update(AECHAR * date, float* litres, float* prize,  int32* totalKm);

036boolean isEmpty();

037int16 size();  

038void deleteDB(const char* baseName);

039 };

040

041 #endif //DB_HEADER

 

Um arquivo BD.cpp é criado para oferecer a implementação das funções declaradas na classe BD definida na Listagem 2. Note que no construtor, precisamos do parâmetro do tipo FuelManager, cuja definição foi dada no primeiro artigo da série “Desenvolvendo em BREW” (Edição 12 da WebMobile). É esse objeto que nos dá acesso ao objeto IShell, necessário para criar uma instância de IDBMgr.

No construtor nós também criamos cada campo de um registro, com auxílio do array pDBFields (Listagem 3). Quando um registro for criado na função newRecord (Listagem 3), bastará tirar uma cópia deste array com MEMCPY, preencher os buffers com o valor do registro e inserir na tabela com IDATABASE_CreateRecord. O destrutor libera o objeto IDBMgr.

 

Listagem 3. Implementação do contrutor e método newRecord do arquivo DB.cpp

001 ...

002

003 DB :: DB(char* bName, FuelManager *fuelManager){

004//Criando o DB manager

005pIDBMgr = NULL;

006pIDatabase = NULL;

007pIShell = fuelManager->a.m_pIShell;

008

009if (ISHELL_CreateInstance(pIShell,AEECLSID_DBMGR, (void **) 010  &pIDBMgr)== SUCCESS){

011//Define date  como char*

012pDBFields[0].fType = AEEDB_FT_STRING;

013pDBFields[0].fName = DATE;

014pDBFields[0].wDataLen =  15 * sizeof(char);

015pDBFields[0].pBuffer = NULL;

016

017//Define gas  como float

018pDBFields[1].fType = AEEDB_FT_DWORD;

019pDBFields[1].fName = GAS;

020pDBFields[1].wDataLen =  sizeof(float);

021pDBFields[1].pBuffer = NULL;

022...

023//O mesmo procedimento para valor e quilometragem

024}

025 }

026 ...

027 Outros Métodos

028 ...

029 int16 DB :: newRecord(AECHAR * date, float* litres, float*

025prize,  int32* totalKm){

026IDBRecord* pIDBRecordOut;

027AEEDBField fieldsOfTheRecord [4];

028pIDBRecordOut = NULL;

029int16 errorCode = 0;

030if (MEMCPY(fieldsOfTheRecord,pDBFields,sizeof (pDBFields)) != 031NULL){

032fieldsOfTheRecord[0].pBuffer=(void*)date;

033fieldsOfTheRecord[1].pBuffer=(void*)litres;

034fieldsOfTheRecord[2].pBuffer=(void*)prize;

035fieldsOfTheRecord[3].pBuffer=(void*)totalKm;

036pIDBRecordOut = IDATABASE_CreateRecord(pIDatabase,fieldsOfTheRecord,4);

037//libera o record

038IDBRECORD_Release( pIDBRecordOut );

039}else {

040errorCode = -1;//Problemas com getField!

041}

042return errorCode;

043 }

 

Na função open, fazemos uso da função IDBMGR_OpenDatabase para abrir nossa tabela (Listagem 4). Caso a mesma já esteja aberta retornamos um código de erro (em nosso exemplo, -1). Já a função close chama IDATABASE_Release para liberar a tabela.

 

Listagem 4. Implementação da função open de DB.cpp

001 int16 DB :: open(){

002int16 errorCode = 0;

003//Criando o database

004if (pIDatabase == NULL){

005pIDatabase = IDBMGR_OpenDatabase(pIDBMgr,baseName,TRUE); 006

007if (pIDatabase == NULL){//já aberto?

008errorCode = -1;

009}

010}

011return errorCode;

012 }

013

014 int16 DB :: close(){

015int16 errorCode = 0;

016if (pIDatabase != NULL){

017IDATABASE_Release(pIDatabase);

018}

019return errorCode;

020 }

 

Para a remoção, busca e atualização de um registro, é necessário navegar entre os registros da tabela iterativamente através de um laço while, chamando IDATABASE_GetNextRecord (Listagem 5, linha 016).  Para cada registro, o campo de data (escolhido como chave) é recuperado através da função IDBRECORD_GetFieldString que nos retorna uma string do tipo  AECHAR* que representa a data do abastecimento. Esse valor recuperado é comparado com a data a ser encontrada (nosso critério de busca).

 

Listagem 5. Implementação do método update no DB.cpp

001 ...

002 int16  DB ::  update(AECHAR * date, float* litres, float* prize, int32* totalKm){

003 

004IDBRecord* pIDBRecord= NULL;

005AEEDBField fieldsOfTheRecord [4];

006AEEDBFieldType iFieldType;

007AEEDBFieldName iFieldName;

008uint16 iFieldLength;

009

010boolean found=FALSE;

011AECHAR* dateFound;

012int16 errorCode = 0;

013

014IDATABASE_Reset(pIDatabase);

015//Navega dentro da tabela

016while((pIDBRecord=IDATABASE_GetNextRecord(pIDatabase))!=NULL)

017{

018...

019//Código de busca do registro semelhante ao remove

020...

021//Uma vez achado o registro atualizamos seu conteúdo

022fieldsOfTheRecord[0].pBuffer=(void*)date;

023fieldsOfTheRecord[1].pBuffer=(void*)litres;

024fieldsOfTheRecord[2].pBuffer=(void*)prize;

025fieldsOfTheRecord[3].pBuffer=(void*)totalKm;

026IDBRECORD_Update(pIDBRecord,fieldsOfTheRecord,4);

027...

028//Após o update o código também é semelhante ao remove

029...

030IDBRECORD_Release( pIDBRecord );

031}//end while

032if (!found){

033errorCode = -1;

034}

035return errorCode;

036 }

037 ...

 

No caso da função remove, ao encontrar o registro desejado chamamos IDBRECORD_Remove; para atualização, tiramos uma cópia do array pDBFields  e chamamos IDBRECORD_Update; para as buscas por litros de gasolina e de álcool, os valores dos campos correspondentes eram obtidos com IDBRECORD_GetField e retornados pela função. Note que sempre que chamamos IDATABASE_GetNextRecord, uma instância de IDBRecord é criada. Isso significa que sempre que esta instância não for mais utilizada pela função temos sempre que liberá-la com IDBRECORD_Release, pois do contrário, o emulador indicará um problema de memory leak ou vazamento de memória (uma vez alocada memória é preciso desalocar quando não for mais necessária) e a aplicação não será encerrada corretamente.

Para as funções isEmpty e size (Listagem 6), fazemos uso da função IDATABASE_GetRecordCount para verificar o número de registros da nossa tabela. Finalmente, a função deleteDB se encarrega de fechar a tabela com IDATABASE_Release e removê-la fisicamente com IDBMGR_Remove.

 

Listagem 6. Implementação de isEmpty() e size() no DB.cpp.

001 ...

002 boolean DB:: isEmpty(){

003return  (IDATABASE_GetRecordCount(pIDatabase) == 0) ? TRUE : 004  FALSE;

005 }

006

007 int16 DB:: size(){

008return IDATABASE_GetRecordCount(pIDatabase);

009 }

010 ...

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados