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的架构变迁!附架构资料
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Design Patterns
Elisabeth Freeman、Eric Freeman、Bert Bates、Kathy Sierra、Elisabeth Robson / O'Reilly Media / 2004-11-1 / USD 49.99
You're not alone. At any given moment, somewhere in the world someone struggles with the same software design problems you have. You know you don't want to reinvent the wheel (or worse, a flat tire),......一起来看看 《Head First Design Patterns》 这本书的介绍吧!