# Jetpack 之 Room Database 简介及其使用 站点:答源(https://www.dandroid.cn/) 原文:https://www.dandroid.cn/archives/19545 Canonical:https://www.dandroid.cn/archives/19545 作者:admin 发布日期:2023-09-10 最后更新:2023-09-10 02:19 分类:Jetpack组件 / Room 内容类型:post 说明:本页是原文的纯文本镜像,供大模型与检索器完整读取;不含导航、广告、付费框。引用请以上方「原文」链接为准。 ## 正文 简介 Room是SQLite之上的一个抽象层,通过Room,可以更加丝滑的使用SQLite的全部功能。并且,原本在运行期才能发现的SQL语句错误,现在在编译期就会报错,写过SQL的同学都懂,尤其是使用复杂语句的时候,这使得SQL语句纠错的时间大大缩短。 Room主要由以下三个部分组成: DataBase 用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder().方法获取DataBase实例 Entity 可以看成是数据库中的表 DAO 提供访问数据库的方法 关系图 1. Database 1.1 定义数据库 // entities是数据,有几个元素就建立几张表 @Database(entities = {User.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); } 1.2 获取数据库 AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build(); 2. Entity Entity 默认为每个属性定义一列,可以使用@Ignore注解来忽略;属性必须为public或提供公有的getters和setters。 以User为例: User.java @Entity public class User { @PrimaryKey private int uid; @ColumnInfo(name = "first_name") private String firstName; @ColumnInfo(name = "last_name") private String lastName; // 默认为每个属性定义一列,可以使用@Ignore注解来避免创建此列 @Ignore Bitmap picture; // 属性必须为public或提供公有的getters and setters,为了简洁,在此省略 } 2.1 主键: 使用@PrimaryKey来定义主键, 如果需要一个自增的主键,@PrimaryKey的autoGenerate置为true: @PrimaryKey(autoGenerate = true) private int index; 2.2 复合主键: @Entity(primaryKeys = {"firstName", "lastName"}) public class User { public String firstName; public String lastName; @Ignore Bitmap picture; } 2.3 表名: 默认使用类名作为表名,也可以指定表名 @Entity(tableName = "users") public class User { …… } 2.4 列明 默认使用属性名作为列名,可以用@ColumnInfo注解去重新指定 @Entity(tableName = "users") public class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture; } 2.5 使用索引 @Entity(indices = {@Index("name"), @Index(value = {"last_name", "address"})}) public class User { @PrimaryKey public int id; public String firstName; public String address; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture; } @Entity(indices = {@Index(value = {"first_name", "last_name"}, unique = true)}) public class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture; } 2.6 外键 @Entity(foreignKeys = @ForeignKey(entity = User.class, parentColumns = "id", childColumns = "user_id")) public class Book { @PrimaryKey public int bookId; public String title; @ColumnInfo(name = "user_id") public int userId; } 2.7 混合型Entity 例如有Address类,有三个属性:street、state、city,User表中又包含这个三个列,可以使用@Embedded注解 public class Address { public String street; public String state; public String city; @ColumnInfo(name = "post_code") public int postCode; } @Entity public class User { @PrimaryKey public int id; public String firstName; @Embedded public Address address; } 2.8 复杂类型的使用 属性类型不局限于SQLite数据类型,可以使用使用任意类型,但在使用非SQLite数据类型时,需要定义Converter类,用于将数据转化为数据可以存储的类型 定义Convert类 public class Converter1 { @TypeConverter public static Date fromTimestamp(Long value) { return value == null ? null : new Date(value); } @TypeConverter public static Long dateToTimestamp(Date date) { return date == null ? null : date.getTime(); } } 给Database加注解: @Database(entities = {User.class}, version = 1) @TypeConverters({Converters1.class, Converters2.class}) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); } 3 UserDao.java Dao可以是interface也可以是abstract类,如果是abstract类,需要提供一个参数为RoomDatabase的构造函数。Room会在编译器自动我们生成DAO的实现类 定义增删改查方法: 3.1 增 使用@Insert注解,通常Room会使用一次事务把所有数据都插入数据库中。 @Dao public interface MyDao { @Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(User... users); @Insert public void insertBothUsers(User user1, User user2); @Insert public void insertUsersAndFriends(User user, List friends); } 3.2 删 @Dao public interface MyDao { @Delete public void deleteUsers(User... users); } 3.3 改 @Dao public interface MyDao { @Update public void updateUsers(User... users); } 3.4 查 @Query注解是DAO中最重要的注解,每一个@Query方法都在编译期生成,如果查询语句有误的话,会在编译期报错,而不是运行期操作失败。 3.4.1 简单查询: @Dao public interface MyDao { @Query("SELECT * FROM user") public User[] loadAllUsers(); } 3.4.2 查询语句中带参数,使用":"+参数名 @Dao public interface MyDao { @Query("SELECT * FROM user WHERE age > :minAge") public User[] loadAllUsersOlderThan(int minAge); } @Dao public interface UserDao { @Query("SELECT * FROM user") List getAll(); @Query("SELECT * FROM user WHERE uid IN (:userIds)") List 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); } 3.4.3 返回某几列数据: 有时候,我们只需要Entity的某几个属性,例如,UI上只展示了用户姓名,只取UI需要的数据不仅节约宝贵的资源,并且还能使查询速度更快。Room允许@Query方法返回任意一个POJO,以此来获取部分咧的数据 public class NameTuple { @ColumnInfo(name="first_name") public String firstName; @ColumnInfo(name="last_name") public String lastName; } 3.4.4 使用集合来作为IN操作符的参数 Room能够识别集合参数,并在运行时自动扩展 @Dao public interface MyDao { @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)") public List loadUsersFromRegions(List regions); } 3.4.5 结合LiveData实时更新UI 通常我们查询数据库都要更新到UI上,可以结合使用LiveData来帮助我们更新UI,Dao可以之间返回LiveData的数据。 @Dao public interface MyDao { @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)") public LiveData > loadUsersFromRegionsSync(List regions); } 3.4.6 RxJava查询: @Dao public interface MyDao { @Query("SELECT * from user where id = :id LIMIT 1") public Flowable loadUserById(int id); } 3.4.7 直接获取Cursor 有时候需求需要直接获取Cursor,那么可以这样做: @Dao public interface MyDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") public Cursor loadRawUsersOlderThan(int minAge); } 3.4.8 查询关联表 @Dao public interface MyDao { @Query("SELECT * FROM book " + "INNER JOIN loan ON loan.book_id = book.id " + "INNER JOIN user ON user.id = loan.user_id " + "WHERE user.name LIKE :userName") public List findBooksBorrowedByNameSync(String userName); } 也可以使用POJO @Dao public interface MyDao { @Query("SELECT user.name AS userName, pet.name AS petName " + "FROM user, pet " + "WHERE user.id = pet.user_id") public LiveData > loadUserAndPetNames(); // You can also define this class in a separate file, as long as you add the // "public" access modifier. static class UserPet { public String userName; public String petName; } } @Dao public interface MyDao { @Query("SELECT first_name, last_name FROM user") public List loadFullName(); } 3.5 数据库版本控制 Room使用Migration类版本的升级,Migration定义了两个变量,startVersion和endVersion,Room会调用每一个Migration.migtate()方法,使得数据库能按照正确的顺序升级数据库版本 Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build(); static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, " + "`name` TEXT, PRIMARY KEY(`id`))"); } }; static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE Book " + " ADD COLUMN pub_year INTEGER"); } }; Room Database使用小结 一个抽象类或接口,继承 RoomDataBase,并使用@Database注解。 1).@Database(entities = [Dog::class, Cat::class], version = 1, exportSchema = false) entities 数组中,一个Entity建一张表 2). 如果Entity中有非直接插入表中的数据类型,需要@Convertes注解 Entity ,几张表几个Entity,定义了表名、列名等,属性和表中字段完全对应,并在该类中提供 setter和getter方法来设置并获取该类中的属性。属性类型不局限于SQLite数据类型,可以使用使用任意类型, 但在使用非SQLite数据类型时,需要定义Converter类,用于将数据转化为数据可以存储的类型 Entity注解: 1).@Entity 声明表名、外键等 2).@PrimaryKey 主键 3).@ColumnInfo 列信息,不写的话,列名=变量名 Dao,定义了所有的用户操作,如添加记录、删除记录及查询记录等。为方便管理,最好几张表几个Dao。 Dao工厂类 推荐使用DAO工厂类在没有DAO工厂类的情况下,必须通过创建DAO实现类的实例才能完成数据库操作。这时就必须知道具体的子类,对于后期的修改非常不方便。 --- 由 答源 DAN 底座 1.4.1 生成 · 2026-11-15T22:06:21+00:00