Room
是 Google 官方出品的 ORM(Object-relational mapping) 框架,当然市面上也有很多 ORM 框架,例如 GreenDao
、 OrmLite
、 Litepal
等。个人并没有了解过其它框架,因此也无法比较其优缺点,相对而言, Room
毕竟是官方出品,能够更好的与 LiveData
等框架结合使用,如果是新项目的话,建议使用。
引入
// 这里以 androidx 最新版为例 implementation 'androidx.room:room-runtime:2.1.0-alpha01' kapt 'androidx.room:room-compiler:2.1.0-alpha01'
简单使用
Room
在 Google 的另一个框架 WorkManager
中得到使用,所以这里我就简单的以它为例来简单介绍下 Room
的使用。
Room
简单来说可以分为以下几个部分:
- Model
- DAO(Data Access Object)
- DataBase 类
- 入口类 Room
首先我们需要建立 Model 对象, 添加 @Entity 注解
// 用 @Index 来标示索引
@Entity(indices = {@Index(value {"schedule_requested_at"})})
public class WorkSpec {
// 用 @ColumnInfo 来标明数据库表的列名, 用 @PrimaryKey 来标示 主键
@ColumnInfo(name = "id")
@PrimaryKey
@NonNull
public String id;
@ColumnInfo(name = "state")
@NonNull
public State state = ENQUEUED;
@ColumnInfo(name = "worker_class_name")
@NonNull
public String workerClassName;
// ...
// 用 @Embedded 来聚合字段,这里 Constraints 的多个字段,在 数据库表里与 workerClassName 等字段平级
@Embedded
@NonNull
public Constraints constraints = Constraints.NONE;
//...
public WorkSpec(@NonNull String id, @NonNull String workerClassName) {
//...
}
public WorkSpec(@NonNull WorkSpec other) {
//...
}
}
// 通过 @ForeignKey 来指明外键, 以及在父 Model delete 与 update 时的行为(NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE)
@Entity(foreignKeys = {
@ForeignKey(
entity = WorkSpec.class,
parentColumns = "id",
childColumns = "work_spec_id",
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE)},
primaryKeys = {"tag", "work_spec_id"},
indices = {@Index(value = {"work_spec_id"})})
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkTag {
@NonNull
@ColumnInfo(name = "tag")
public final String tag;
@NonNull
@ColumnInfo(name = "work_spec_id")
public final String workSpecId;
public WorkTag(@NonNull String tag, @NonNull String workSpecId) {
this.tag = tag;
this.workSpecId = workSpecId;
}
}
一般而言,每个 Model 都会有其对应的 DAO 类,集成了所有对这个 Model 的操作,如 WorkSpec
对应的 WorkSpecDao
, Room
的 DAO 类一般都声明为 interface
, 然后加上 @Dao 注解,具体的实现类则由代码自动生成。
@Dao
public interface WorkSpecDao {
@Insert(onConflict = IGNORE)
void insertWorkSpec(WorkSpec workSpec);
// @Query 并不是指查询数据库,而是执行数据库语句
@Query("DELETE FROM workspec WHERE id=:id")
void delete(String id);
@Query("SELECT * FROM workspec WHERE id=:id")
WorkSpec getWorkSpec(String id);
//...
// 使用 @Transaction 标示使用 transition
@Transaction
@Query("SELECT id, state, output FROM workspec WHERE id=:id")
WorkSpec.WorkStatusPojo getWorkStatusPojoForId(String id);
// 可以返回 LiveData, 当数据变动后,重新执行查询,获取新数据
@Transaction
@Query("SELECT id, state, output FROM workspec WHERE id IN (:ids)")
LiveData<List<WorkSpec.WorkStatusPojo>> getWorkStatusPojoLiveDataForIds(List<String> ids);
}
当准备好所有的 Model 和 DAO 后,我们就需要把它放入 DataBase 的管理中:
// 我们需要把所有的 model 对象 全都方式 @Database 的 entities 中,增删改 model 后,我们应该更新 version
// sqlite 只支持 NULL、INTEGER、REAL、TEXT、BLOB 这些类型,如果是 Date 或者自定义的枚举等类型,则需要声明 @TypeConverters 来做类型转换了
@Database(entities = {
Dependency.class,
WorkSpec.class,
WorkTag.class,
SystemIdInfo.class,
WorkName.class},
version = 4)
@TypeConverters(value = {Data.class, WorkTypeConverters.class})
public abstract class WorkDatabase extends RoomDatabase {
// 获取 WorkSpecDao
public abstract WorkSpecDao workSpecDao();
// ...
}
剩下的就是如何使用这个 DataBase 类了,它是一个抽象类,我们真正需要的是由代码生成的子类,那如何获取呢?这个时候 Room
这个类就该出场了。也不得不感叹下,通过注解来做代码生成真好,一堆复杂可重复的东西都被隐藏在水下了。
Room
构造 DataBase 实例是通过 Builder 的方式来构建的,我们来看看 WorkDatabase
的构建:
public static WorkDatabase create(Context context, boolean useTestDatabase) {
RoomDatabase.Builder<WorkDatabase> builder;
if (useTestDatabase) {
// 可以通过 inMemoryDatabaseBuilder 来构建内存Db,可用于测试
builder = Room.inMemoryDatabaseBuilder(context, WorkDatabase.class)
.allowMainThreadQueries();
} else {
builder = Room.databaseBuilder(context, WorkDatabase.class, DB_NAME);
}
return builder.addCallback(generateCleanupCallback())
.addMigrations(WorkDatabaseMigrations.MIGRATION_1_2)
.addMigrations(
new WorkDatabaseMigrations.WorkMigration(context, VERSION_2, VERSION_3))
.addMigrations(MIGRATION_3_4)
.fallbackToDestructiveMigration()
.build();
}
通过 builder, 我们可以添加 Callback,可以添加每个版本的升级降级策略, 可以启用 WAL 模式等。一般应用构建好 DataBase 应该以单例的形式存在于应用中。
DataBase 的实例化
实现我们看看 RoomDataBase$Builder
的 build 方法:
public static class Builder<T extends RoomDatabase> {
public T build() {
//...
if (mQueryExecutor == null) {
// 如果使用者没有提供 Executor,则使用框架默认的 IOThreadExecutor, 所以默认所有通过 DAO 执行的操作都会在子线程执行
mQueryExecutor = ArchTaskExecutor.getIOThreadExecutor();
}
// Migration 相关
if (mFactory == null) {
mFactory = new FrameworkSQLiteOpenHelperFactory();
}
DatabaseConfiguration configuration =
new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
mCallbacks, mAllowMainThreadQueries, mJournalMode.resolve(mContext),
mQueryExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade, mMigrationsNotRequiredFrom);
// 真正构造 DataBase 实例
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
// 初始化 DataBase
db.init(configuration);
return db;
}
}
注解生成器会为我们生成一个带 _Impl
后缀的类,我们的 DB 名为 WorkDatabase
,那么生成类就为 WorkDatabase_Impl
。 所以真正构造实例时通过反射去构造的。
static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
final String fullPackage = klass.getPackage().getName();
String name = klass.getCanonicalName();
final String postPackageName = fullPackage.isEmpty()
? name
: (name.substring(fullPackage.length() + 1));
final String implName = postPackageName.replace('.', '_') + suffix;
try {
final Class<T> aClass = (Class<T>) Class.forName(
fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
return aClass.newInstance();
} catch (Exception e) {
// 各种 rethrow
}
}
至此,对于业务开发者而言,了解到此已经足够了, Room
已经将 sqlite 的大部分东西都隐藏起来了,但如果我们想写出更为准确和高效的东西,我们依旧需要继续升入,看看我们写的每一行代码具体都做了些什么,这个我们下一篇博文再详细介绍。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 『互联网架构』软件架构-分布式架构(14)
- 『互联网架构』软件架构-电商系统架构(上)(69)
- 『互联网架构』软件架构-电商系统架构(中)(70)
- 『互联网架构』软件架构-电商系统架构(下)(71)
- 『互联网架构』软件架构-电商系统架构发展历程(68)
- 阿里P8架构师谈:淘宝技术架构从1.0到4.0的架构变迁!附架构资料
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java Servlet&JSP经典实例
(美)佩里 / 朱涛江、邹红霞、林琪 / 中国电力出版社 / 2005-7 / 86.00元
本书将用于帮助指导Java web开发人员的日常任务,提供典型的web相关问题的快速解决方案。本书集中介绍了如何用Java初始化某些与web相关的任务,而不是教会读者如何使用Java语言,或者事无巨细地解释servlet和JSP API。书中包含了大量关于复杂的日常开发任务的技巧,这些技巧涵盖了许多与Servlet 2.4和JSP 2.0规范相关联的新特性,包括ServletRequestList......一起来看看 《Java Servlet&JSP经典实例》 这本书的介绍吧!