架构库版本:1.0.0 Alpha 2 - June 2, 2017
Room提供了一个SQLite之上的抽象层,使得在充分利用SQLite功能的前提下顺畅的访问数据库。
对于需要处理大量结构化数据的App来说,把这些数据做本地持久化会带来很大的好处。常见的用例是缓存重要数据块。这样当设备无法连网的时候,用户仍然可以浏览内容。而用户对内容做出的任何改动都在网络恢复的时候同步到服务端。
核心framework内置了对SQL的支持。虽然这些API很强大,但是都很低级,使用起来很花时间和精力:
没有编译时的SQL查询检查机制。当数据表发生改变的时候,需要手动更新受影响的SQL查询。这个过程既耗时又容易出错。
需要写很多公式化的代码在SQL查询与Java对象之间转换。
Room处理了这些相关的事情,同时提供了SQLite之上的抽象层。
Room中有三个主要的组件:
- Database:你可以用这个组件来创建一个database holder。注解定义实体的列表,类的内容定义从数据库中获取数据的对象(DAO)。它也是底层连接的主要入口。
这个被注解的类是一个继承RoomDatabase的抽象类。在运行时,可以通过调用Room.databaseBuilder()
或者 Room.inMemoryDatabaseBuilder()
来得到它的实例。
- Entity:这个组件代表一个持有数据库的一个表的类。对每一个entity,都会创建一个表来持有这些item。你必须在Database类中的entities数组中引用这些entity类。entity中的每一个field都将被持久化到数据库,除非使用了
@Ignore
注解。
注:实体可以有一个空构造函数(如果DAO类可以访问每个持久化字段),或者一个构造函数的参数包含与实体中的字段匹配的类型和名称。Romm还可以使用全部或部分构造函数,例如只接收一些字段的构造函数。
- DAO:这个组件代表一个作为Data Access Objec的类或者接口。DAO是Room的主要组件,负责定义查询(添加或者删除等)数据库的方法。使用
@Database
注解的类必须包含一个0参数的,返回类型为@Dao
注解过的类的抽象方法。Room会在编译时生成这个类的实现。
注:通过DAO而不是query builders或者直接的query语句来处理数据库,可以把数据库的各个部分分离开来。而且DAO还可以让你轻松的使用假的database来测试app。
这些组件以及它们与app其余部分之间的关系如图1:
下面是一个只有一个entity和一个DAO的数据库配置的简单例子:
User.java
@Entity
public class User {
@PrimaryKey
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
UserDao.java
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
AppDatabase.java
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
创建了上面的文件之后,可以使用下面的代码来得到database的实例了:
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
在实例化AppDatabase对象的时候应该遵循单例模式,因为每个Database实例都是相当耗费的,而且也很少需要多个实例。
Entities
当一个类用@Entity注解并且被@Database注解中的entities属性所引用,Room就会在数据库中为那个entity创建一张表。
默认Room会为entity中定义的每一个field都创建一个column。如果一个entity中有你不想持久化的field,那么你可以使用@Ignore来注释它们,如下面的代码所示:
@Entity
class User {
@PrimaryKey
public int id;
public String firstName;
public String lastName;
@Ignore
Bitmap picture;
}
要持久化一个field,Room必须有获取它的渠道。你可以把field写成public,也可以为它提供一个setter和getter。如果你使用setter和getter的方式,记住它们要基于Room的Java Bean规范。
Primary key
每个entity必须至少定义一个field作为主键(primary key)。即使只有一个field,你也必须用@PrimaryKey注释这个field。如果你想让Room为entity设置自增ID,你可以设置@PrimaryKey的autoGenerate属性。如果你的entity有一个组合主键,你可以使用@Entity注解的primaryKeys属性,具体用法如下:
@Entity(primaryKeys = {
"firstName", "lastName"})
class User {
public String firstName;
public String lastName;
@Ignore
Bitmap picture;
}
Room默认把类名作为数据库的表名。如果你想用其它的名称,使用@Entity注解的tableName属性,如下:
@Entity(tab