Android架构组件Room的使用详解
作者:simplepeng 发布时间:2022-04-30 14:59:24
Room其实就是一个orm,抽象了SQLite的使用,但是它作为Android的亲儿子orm,并且原生支持LiveData和Rxjava嵌套使用,学习一下还是不错的。
Room有3个主要组件
Database :数据库
Entity : 代表数据库一个表结构
Dao : 包含访问数据库的方法
简单使用
添加Google Maven仓库
allprojects {
repositories {
jcenter()
google()
}
}
添加依赖
dependencies {
// Room
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
}
定义数据表实体类
班级表
@Entity(tableName = "tb_class")
public class ClassEntity {
@PrimaryKey
private long id;
}
学生表
//指示数据表实体类
@Entity(tableName = "tb_student",//定义表名
indices = @Index(value = {"name", "sex"}, unique = true),//定义索引
foreignKeys = {@ForeignKey(entity = ClassEntity.class,
parentColumns = "id",
childColumns = "class_id")})//定义外键
public class StudentEntity {
@PrimaryKey //定义主键
private long id;
@ColumnInfo(name = "name")//定义数据表中的字段名
private String name;
@ColumnInfo(name = "sex")
private int sex;
@Ignore//指示Room需要忽略的字段或方法
private String ignoreText;
@ColumnInfo(name = "class_id")
private String class_id;
//setter and getter
}
Entity注解可选参数
public @interface Entity {
//定义表名
String tableName() default "";
//定义索引
Index[] indices() default {};
//设为true则父类的索引会自动被当前类继承
boolean inheritSuperIndices() default false;
//定义主键
String[] primaryKeys() default {};
//定义外键
ForeignKey[] foreignKeys() default {};
}
Index索引注解可选参数
public @interface Index {
//定义需要添加索引的字段
String[] value();
//定义索引的名称
String name() default "";
//true-设置唯一键,标识value数组中的索引字段必须是唯一的,不可重复
boolean unique() default false;
}
ForeignKey外键注解可选参数
public @interface ForeignKey {
//引用外键的表的实体
Class entity();
//要引用的外键列
String[] parentColumns();
//要关联的列
String[] childColumns();
//当父类实体(关联的外键表)从数据库中删除时执行的操作
@Action int onDelete() default NO_ACTION;
//当父类实体(关联的外键表)更新时执行的操作
@Action int onUpdate() default NO_ACTION;
//在事务完成之前,是否应该推迟外键约束
boolean deferred() default false;
//给onDelete,onUpdate定义的操作
int NO_ACTION = 1;
int RESTRICT = 2;
int SET_NULL = 3;
int SET_DEFAULT = 4;
int CASCADE = 5;
@IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
@interface Action {
}
}
定义Dao类
@Dao
public interface StudentDao {
@Query("SELECT * FROM StudentEntity")
List<StudentEntity> getAll();
@Query("SELECT * FROM StudentEntity WHERE id IN (:ids)")
List<StudentEntity> getAllByIds(long[] ids);
@Insert
void insert(StudentEntity... entities);
@Delete
void delete(StudentEntity entity);
@Update
void update(StudentEntity entity);
}
@insert, @Update都可以执行事务操作,定义在OnConflictStrategy注解类中
public @interface Insert {
//定义处理冲突的操作
@OnConflictStrategy
int onConflict() default OnConflictStrategy.ABORT;
}
public @interface OnConflictStrategy {
//策略冲突就替换旧数据
int REPLACE = 1;
//策略冲突就回滚事务
int ROLLBACK = 2;
//策略冲突就退出事务
int ABORT = 3;
//策略冲突就使事务失败
int FAIL = 4;
//忽略冲突
int IGNORE = 5;
}
定义数据库
@Database(entities = {StudentEntity.class}, version = 1)
public abstract class RoomDemoDatabase extends RoomDatabase {
public abstract StudentDao studentDao();
}
生成数据库实例
RoomDemoDatabase database = Room.databaseBuilder(getApplicationContext(), RoomDemoDatabase.class, "database_name")
.build();
生成数据库实例的其他操作
Room.databaseBuilder(getApplicationContext(),
RoomDemoDatabase.class, "database_name")
.addCallback(new RoomDatabase.Callback() {
//第一次创建数据库时调用,但是在创建所有表之后调用的
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
}
//当数据库被打开时调用
@Override
public void onOpen(@NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
}
})
.allowMainThreadQueries()//允许在主线程查询数据
.addMigrations()//迁移数据库使用,下面会单独拿出来讲
.fallbackToDestructiveMigration()//迁移数据库如果发生错误,将会重新创建数据库,而不是发生崩溃
.build();
数据库迁移(升级)
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");
}
};
创建嵌套对象
有时,您希望将一个实体或普通的以前的Java对象(POJO)作为数据库逻辑中的一个完整的整体来表示,即使该对象包含几个字段。在这些情况下,您可以使用@Embedded来表示一个对象,您希望将其分解为表中的子字段。然后可以像对其他单个列一样查询嵌入式字段
class Address {
public String street;
public String state;
public String city;
@ColumnInfo(name = "post_code")
public int postCode;
}
@Entity
class User {
@PrimaryKey
public int id;
public String firstName;
@Embedded
public Address address;
}
这样user表中的字段就包含了 id , firstName , street , state , city , 和 post_code
注意 :嵌入式字段还可以包含其他嵌入式字段
如果一个实体具有相同类型的多个内嵌字段,则可以通过设置前缀属性(prefix)使每个列保持惟一。然后将所提供的值添加到嵌入对象中每个列名的开头
@Embedded(prefix = "foo_")
Coordinates coordinates;
和 LiveData 一起使用
添加依赖
// ReactiveStreams support for LiveData
implementation "android.arch.lifecycle:reactivestreams:1.0.0"
修改返回类型
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);
}
和RxJava一起使用
添加依赖
// RxJava support for Room
implementation "android.arch.persistence.room:rxjava2:1.0.0"
修改返回类型
@Dao
public interface MyDao {
@Query("SELECT * from user where id = :id LIMIT 1")
public Flowable<User> loadUserById(int id);
}
直接游标访问
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
public Cursor loadRawUsersOlderThan(int minAge);
}
类型转换
定义转换类,@TypeConverter注解定义转换的方法
public class Converters {
@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();
}
}
@TypeConverters注解,告知数据库要依赖哪些转换类
@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
使用这些转换器,您可以在其他查询中使用您的自定义类型,正如您将使用基本类型一样,如下代码所示
@Entity
public class User {
...
private Date birthday;
}
@Dao
public interface UserDao {
...
@Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
List<User> findUsersBornBetweenDates(Date from, Date to);
}
输出模式
在编译时,将数据库的模式信息导出到JSON文件中,这样可有利于我们更好的调试和排错
build.gradle
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
}
您应该将导出的JSON文件(表示数据库的模式历史记录)存储在您的版本控制系统中,因为它允许为测试目的创建您的数据库的旧版本
总结
以上所述是小编给大家介绍的Android架构组件Room的使用详解网站的支持!
来源:https://juejin.im/post/5a4228036fb9a044ff31b8ca?utm_source=tuicool&utm_medium=referral


猜你喜欢
- 在进行一些小型APP的开发,或者是对拍照界面没有自定义要求时,我们可以用调起系统相机的方式快速完成拍照需求和不需读写权限进行读写操作的方案一
- merge结合include优化android布局,效果不知道,个人感觉使用上也有很大的局限,不过还是了解一下,记录下来。布局文件都要有根节
- 在android studio中存储数据有三个方法,分别是:(1)简单存储——SharedPreferences(2)文件存储:内部存储——
- 前提最近发现各个频道推荐了很多ULID相关文章,这里对ULID的规范文件进行解读,并且基于Java语言自行实现ULID,通过此实现过程展示U
- 隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。但是,因为隐式转换不需要程序员将一种类型显式强制转换为另一种类型,所以
- 存储访问框架,简称:SAF, 就是系统文件选择器+文件操作API。先选择文件,在用文件操作API处理文件。系统文件选择器,就和Windows
- 1.保存对象到文件中Java语言只能将实现了Serializable接口的类的对象保存到文件中,利用如下方法即可:public static
- maven插件主要是为maven中生命周期中的阶段服务的,maven中只是定义了3套生命周期,以及每套生命周期中有哪些阶段,具体每个阶段中执
- 一、创建字符串创建字符串的方式有三种:// 方式一String str = "Hello Bit";// 方式二Stri
- 前言消息推送功能可以说移动APP不可缺少的功能之一,一般简单的推送我们可以使用第三方推送的SDK,比如极光推送、信鸽推送等,但是对于消息聊天
- 前言前面的例子都是多个线程在做相同的操作,比如4个线程都对共享数据做tickets–操作。大多情况下,程序中需要不同的线程做不同的事,比如一
- Android 自定义View实现抽屉效果说明这个自定义View,没有处理好多点触摸问题View跟着手指移动,没有采用传统的scrollBy
- PS:本文使用jdk1.7解析1.Object类 的equals 方法 /** &
- 一、引言想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需求。一般就来讲就两种实现方式,view或者surfaceview。下面
- 1.下载jdk8 1)官网下载地址Java8下载官网地址:http://www.oracle.com/technetwork/java/ja
- 最近设计要求要一个圆形进度条渐变的需求:1.画圆形进度条2.解决渐变最终实现效果代码package com.view;import andr
- 我们知道,Maven 是通过仓库对依赖进行管理的,当 Maven 项目需要某个依赖时,只要其 POM 中声明了依赖的坐标信息,Maven 就
- public class MenuEx extends Activity { private static final String TAG
- 本文实例为大家分享了android利用handler实现倒计时的具体代码,供大家参考,具体内容如下xml<?xml version=&
- AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Map