跳转至

开始使用Room

更新日期 2023-6-19
  • 2023-6-19 更新按条件删除
  • 2023-6-14 更新例子
  • 2021-9-1 更新格式
  • 2021-6-26 创建文档

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

大致分为以下几个步骤:

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

本文介绍Room的用法。引入Room;准备数据,设计结构;数据库操作。介绍常见的崩溃情况。

具体步骤

引入Room

在模块gradle引入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方法。

User类
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中可以对数据表进行设置。

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

数据库管理者

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

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

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注解。

UserDao
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("DELETE FROM user WHERE createTime=:ts;")
void deleteByStartTs(long ts);

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

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

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

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

如果要倒序,加个DESC即。例如 ORDER BY start_timestamp DESC;

使用@Insert注解。

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

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

AppDatabase
DbMgr
User
UserDao

使用

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

HandlerThread
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

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

选择app进程

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类,但是没有升级数据库版本,会报错

修改了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)

相关操作

🧑🏻‍💻 示例代码

本站说明

一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。如有疑问和建议,欢迎在下方评论~

📖AndroidTutorial 📚AndroidTutorial 🙋反馈问题 🔥最近更新 🍪投喂作者

Ads