Imprimir este capítuloImprimir este capítulo

Livro 3 - Projeto Agenda de Contatos - Parte 3

3. Biblioteca Room e persistência de dados

3.4. Classe ContactsDatabase (Room database)

O que é uma Room database?

Room database é uma camada acima do banco de dados SQLite. A biblioteca Room simplifica a manipulação de banco de dados e se encarrega das tarefas comuns acessar ou criar um banco de dados SQLite.

  • Room usa os objetos DAO para fazer consultas a seu banco de dados.
  • Por padrão, não se deve fazer operações demoradas na thread principal do aplicativo, tais como as consultas SQL a um banco de dados, pois isso fará com que a interface gráfica trave. Você já deve ter visto a mensagem do Android: "O aplicativo está demorando e responder deseja encerrar?", não queremos isso em nossos aplicativos! .Portanto, para evitar essa situação devemos SEMPRE realizar operações demoradas em segundo plano. Felizmente, o casamento entre a biblioteca Room e classe LiveData
    aplica essa regra automaticamente e executa todas as consultas em uma thread em segundo plano.
  • Room oferece verificações em tempo de compilação para os comandos SQL. Sem mais erros de sintaxe em tempo de execução!
  • Sua classe Room deve ser declarada como abstrata e extender a classe RoomDatabase.
  • Normalmente, você precisará apenas de uma única instância da Room database em todo aplicativo. Assim, é comum empregar o padrão de projeto Singletonpara garantir a existência de apenas um objeto acessando o banco de dados.

Implementação da classe ContactsDatabase

A criação da classe ContactsDatabase usa anotações para declarar as tabelas do banco de dados e o padrão Singleton para garantir a existência de apenas uma instância da classe em todo o aplicativo. Além disso, nesta fase de construção do aplicativo vamos criar um callback, que nada mais é que um método para ser executado junto a criação do banco de dados, para gerar automaticamente alguns valores iniciais para o nosso banco.

package ...

import android.content.Context;
import android.os.AsyncTask;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;

@Database(entities = {Contact.class}, version = 1, exportSchema = false)
public abstract class ContactsDatabase extends RoomDatabase {
public abstract ContactsDAO contactsDao();

private static volatile ContactsDatabase INSTANCE;

static ContactsDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (ContactsDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
ContactsDatabase.class, "contacts_database")
// remover essa linha na versão final
.addCallback(sRoomDatabaseCallback)
.build();
}
}
}
return INSTANCE;
}

private static RoomDatabase.Callback sRoomDatabaseCallback =
new RoomDatabase.Callback(){

@Override
public void onOpen (@NonNull SupportSQLiteDatabase db){
super.onOpen(db);
new PopulateDbAsync(INSTANCE).execute();
}
};
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
private final ContactsDAO mDao;

PopulateDbAsync(ContactsDatabase db) {
mDao = db.contactsDao();
}

@Override
protected Void doInBackground(final Void... params) {
mDao.deleteAll();
Contact word = new Contact("Hello", "", "");
mDao.insert(word);
word =
new Contact("World", "", "");
mDao.insert(word);
return null;
}
}
}
  • Todo banco de dados deve usar a herança da classe RoomDatabase
  • @Database
    • Anotação para identificar a criação de uma Room database. Você deve declarar todas as entidades para usar no banco e também o número de versão. Cada entidade vai ser usada para criar as tabelas no banco de dados.
  • Declaramos todos os objetos DAO para a manipulação das consultas ao banco de dados provendo um método abstrato para cada @DAO
  • O padrão Singleton exige que a classe possua uma referência a uma instância dela mesma e um método getDatabase. Dessa forma, para obter um novo objeto ContactDatabase deve ser solicitado esse método get onde ele verifica a existência de uma instância existente e retorna esse valor ou, se é a primeira vez que o método é invocado (logo a classe ainda não foi instânciada) então ele cria uma instância do objeto
  • O método databaseBuilder é o responsável pela efetiva criação do banco de dados. Ele cria para o contexto da aplicação um banco de dados a partir da definição da classe ContactsDatabase e nomeia-o para "contacts_database"
  • Após a criação do banco é configurado uma ação para executar através do método addCallBack. A ação a ser executada irá através de uma AsyncTask adicionar alguns valores iniciais para o banco de dados.