Introdução à persistência de dados com Room
Todo desenvolvedor Android sabe como é complicado trabalhar com SQLite no Android, boilerplate para todos os lados, códigos acoplados e SQLs enormes, tudo isso gera uma enorme perda de produtividade e bugs que assustariam até o Frankenstein. Pensando nisto, desenvolvedores optam por sacrificar a performance da aplicação em troca de uma produtividade melhor, utilizando ORMs já conhecidos no mercado ou o Realm, mas e se existisse uma outra alternativa?
Em 2017 durante o Google I/O, foram lançadas um conjunto de bibliotecas para o Android, o Android Architecture Componentes. Entre elas, uma me chamou bastante atenção... Uma biblioteca para persistência de dados chamada Room!
Room nada mais é que uma biblioteca para mapeamento de objetos SQLite em Java que cria uma camada de abstração sobre o SQLite, tornando desnecessária a escrita de boa parte do SQL, eliminando os problemas existentes ao usar o SQLite padrão do Android.
Existem 3 componentes para se construir uma aplicação com Room: Entity, DAO e Database.
- Entity representa os dados de uma tabela, referenciada por uma annotation em uma classe de dados.
- DAO define os métodos de acesso ao banco, usando annotations para vincular o SQL com o método.
- Database é a classe que contém referências dos DAOs, é o meio de acesso principal para a conexão com o BD.
Parece simples... Mas é!
Como exemplo de implementação, vamos utilizar uma aplicação que está em meu Github
Mãos na massa
Primeiro, adicione as dependências em seu build.gradle.
implementation "android.arch.persistence.room:runtime:1.0.0" annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
Agora crie uma classe Java chamada
Person
, está será a Entity, classe que representará a tabelaPerson
no BD.@Entity public class Person { @PrimaryKey(autoGenerate = true) private long id; @ColumnInfo(name = "first_name") private String firstName; @ColumnInfo(name = "last_name") private String lastName; private int age; private String email; public Person() {} public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
Toda
Entity
deve conter a annotation@Entity
, note também que há outras annotations.@PrimaryKey
indica que o atributo id deve ser uma primary key,@ColumnInfo
define propriedades específicas da coluna, como o nome dela por exemplo, esta annotation não é obrigatória, Room automaticamente cria a coluna com o nome do atributo, caso queira que ele crie com outro nome, então use a annotation@ColumnInfo
com a propriedade name.Crie uma interface chamada
PersonDao
com a annotation@Dao
@Dao public interface PersonDao { @Insert long insert(Person person); @Insert void insertAll(List<Person> personList); @Update int update(Person person); @Delete void delete(Person person); @Query("SELECT * FROM person") List<Person> getAll(); @Query("SELECT * FROM person WHERE id = :idperson") Person getById(int idperson); }
Room utiliza 4 annotations no DAO,
@Insert
para inserir dados na tabela,@Update
para atualizar dados na tabela,@Delete
para remover dados e@Query
para executar SQL, note que há dois métodos com@Insert
, isto é para demonstrar que é possível fazer inserção de um único objeto e também de uma lista de objetos.Crie uma classe chamada AppDatabase que estende a RoomDatabase, a classe deve conter a annotation
@Database
@Database(entities = {Person.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { private static AppDatabase INSTANCE; public abstract PersonDao personDao(); public static AppDatabase getAppDatabase(Context context) { if(INSTANCE == null) { INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "mydb") //.fallbackToDestructiveMigration() //.allowMainThreadQueries() .build(); } return INSTANCE; } public static void destroyInstance() { INSTANCE = null; } }
A classe
AppDatabase
terá a tarefa de gerenciar o BD, contendo a versão do banco (declarado na propriedade version da annotation@Database
), asEntitys
(declarado na propriedade entity da annotation@Database
), todos os DAOs, migrations (exemplos em um novo artigo!) e outras propriedades do banco. Note que criei a classe como um singleton, porém, não é obrigatório, fiz um singleton para não ter necessidade de instanciar a classe sempre que for usar o BD.Deixei algumas opções comentadas,
.fallbackToDestructiveMigration()
removerá os dados do BD sempre que houver uma alteração na versão, isso facilitará seus testes durante o desenvolvimento, pois o Room faz cache dos dados..allowMainThreadQueries()
permite que o acesso ao banco seja executado na Main Thread, use esta opção apenas caso queira fazer alguns testes e está com preguiça de criar threads, NÃO USE ISTO EM PRODUÇÃO OU UMA TERRÍVEL MALDIÇÃO CAIRÁ SOBRE SUA APLICAÇÃO (vulgo ANR).Tudo pronto, agora faça o teste, tente inserir um objeto
Person
!final Person person = new Person(); person.setFirstName("João"); person.setLastName("Das Neves"); person.setAge(30); person.setEmail([email protected]); new Thread(new Runnable() { @Override public void run() { AppDatabase db = AppDatabase.getAppDatabase(getBaseContext()); db.personDao().insert(person); finish(); } }).start();
Conclusão
Room é uma tentativa do Google de tornar o desenvolvimento de aplicações Android mais produtivo e menos suscetivo a erros, eliminando a necessidade de usar ORMs pesados ou dar preferência ao SQLite nativo. Erros são gerados durante a compilação (escreveu SQL errado? Não compila!), há um padrão organizacional com annotations e interfaces, há integração com LiveData (próximo artigo?!) e rxJava e também há total integração com o SQLite nativo do Android, dando possibilidade de migrar seu banco nativo aos poucos.
Google recomenda que se utilize o Room, inclusive, para substituir a maneira antiga de se gerenciar um banco SQLite.
Nos vemos no meu próximo artigo!