Skip to content

开始使用Room

Room 在 SQLite 上提供了一个抽象层。

大致分为以下几个步骤:

  • 引入Room
  • 准备数据,设计结构
  • 数据库操作

具体步骤

引入Room

在模块gradle引入Room

def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"

准备数据,设计结构

新建User.java,给User类添加@Entity注解,表明它是Room的实体类。 类中的字段需要能被其他类(Room)访问,可以使用public,也可以增加getter/setter方法。

import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    public long uid;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;

    @ColumnInfo
    public long createTime;
    // 构造器与toString...
}

这里默认表名为类的名字。如果要使用不同的表名,需要在注解Entity后加上设置。

@Entity(tableName = "other_users")

@PrimaryKey指定了主键。Room要求每个实体至少有1个主键。 这里设置了autoGenerate = true,表明它是自增长的。

@ColumnInfo表示这个字段为一列。默认情况下列名是属性的名称。 也可用@ColumnInfo(name = "last_name")来指定列名。

Entity中可以对数据表进行设置。

更多关于Entity的使用方法,将在实体类介绍里说明。

数据库管理者

新建抽象类AppDatabase.java,它指定了实体类,数据库版本号。

import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

建立数据库管理器单例DbMgr

import android.content.Context;
import androidx.room.Room;

public class DbMgr {
    private static DbMgr mgr;
    public static final String DB_NAME = "rust-fisher-db.db";
    private AppDatabase database;

    private DbMgr() {
    }

    public static DbMgr getMgr() {
        if (mgr == null) {
            synchronized (DbMgr.class) {
                if (mgr == null) {
                    mgr = new DbMgr();
                }
            }
        }
        return mgr;
    }

    public void initDb(Context context) {
        database = Room.databaseBuilder(context, AppDatabase.class, DB_NAME).build();
    }

    public AppDatabase getDatabase() {
        return database;
    }
}

在使用数据库前,我们需要通过Room.databaseBuilder获得数据库对象database。 数据库名称也在这里指定。

在单进程app中,数据库对象建议只创建1个。

数据库操作

在此我们介绍增,删,查的操作。 新建UserDao.java来规定操作。 UserDao是一个接口(interface),添加了@Dao注解。

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;

import java.util.List;

@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);
}

每一个方法上面,都使用了对应的注解,有的还带有sql语句。

使用@Query注解。对应的sql命令是SELECT

查询全部数据,sql语句SELECT * FROM user

条件查询,例如通过指定的uid查询 SELECT * FROM user WHERE uid IN (:userIds)

sql语句和Java方法中的参数用冒号:进行对应。 例如方法中的参数String first,在sql语句中是:first

使用@Insert注解。

使用@Delete注解。例子中的删除操作,可以直接传入一个User对象。

至此,我们用到的类和接口有

AppDatabase
DbMgr
User
UserDao

使用

Room要求不能在主线程中执行数据库的操作。 我们找一个子线程来处理。

private HandlerThread mDbHt = new HandlerThread("db");
private Handler mDbHandler;

    // 启动子线程
    mDbHt.start(); // 初始化数据库的专用操作线程
    mDbHandler = new Handler(mDbHt.getLooper());

执行增加数据的操作

mDbHandler.post(new Runnable() {
    @Override
    public void run() {
        User user = new User("Rust", "Fisher");
        DbMgr.getMgr().getDatabase().userDao().insertAll(user);
    }
});

查询数据

mDbHandler.post(new Runnable() {
    @Override
    public void run() {
        UserDao userDao = DbMgr.getMgr().getDatabase().userDao();
        List<User> userList = userDao.getAll();
    }
});

删除1条记录

mDbHandler.post(new Runnable() {
    @Override
    public void run() {
        UserDao userDao = DbMgr.getMgr().getDatabase().userDao();
        User user = userDao.findByName("Rust", "Fisher");
        userDao.delete(user);
    }
});

先查再删,这里是把最旧的一条数据给删掉了。

在本例中,手机上数据库文件路径为 /data/data/com.rustfisher.tutorial2020/databases/rust-fisher-db.db

运行起来,执行效果

demo1

Database Inspector

在 Android Studio 4.1 及更高版本中,可以用 Database Inspector 在应用运行时检查、查询和修改应用的数据库。

Database Inspector 仅可与 API 级别 26 及更高版本的 Android 操作系统中所包含的 SQLite 库结合使用。它无法处理与app捆绑的其他 SQLite 库。

如需在 Database Inspector 中打开数据库, 需要在模拟器或搭载 API 级别 26 或更高版本的已连接设备上运行app。

从菜单栏中依次选择 View > Tool Windows > Database Inspector。

从下拉菜单中选择正在运行的应用进程。

db

报错记录

如果在主线程执行数据库读写操作,会报错

java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

对构建器调用allowMainThreadQueries(),否则 Room 不支持在主线程上访问数据库

database = Room.databaseBuilder(context, AppDatabase.class, DB_NAME)
        .allowMainThreadQueries()
        .build();

如果修改了Entity类,但是没有升级数据库版本,会报错

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

插入记录时,如果有重复的主键(PrimaryKey),报错

android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: User.uid (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY)

相关操作