- 原文地址:Incrementally migrate from SQLite to Room
- 原文做者:Florina Muntenescu
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:IllllllIIl
- 校对者:tanglie1993, jaymz1439
经过可管理的 PR 将复杂的数据库迁移到 Roomhtml
你已经据说过 Room 了吧 —— 或许你已经看过文档,看过一个或两个视频,而且决定开始整合 Room 到你的项目中。若是你的数据库只有几张表和简单查询的话,你能够很容易地跟着下面这 7 个步骤,经过较小改动的相似 pull request 操做迁移到 Room。前端
不过,若是你的数据库较大或者有复杂的查询操做的话,实现全部 entity 类,DAO 类,DAO的测试类而且替换 SQLiteOpenHelper
的使用就会耗费不少时间。你最终会须要一个大改动的 pull request,去实现这些和检查。让咱们看看你怎么经过可管理的 PR(pull request),逐步从 SQLite 迁移到 Room。java
第一个 PR:建立你的 entity 类,RoomDatabase,而且更新你自定义的 SQLiteOpenHelper 为 SupportSQLiteOpenHelper。android
其他的 PR:建立 DAO 类去代替有 Cursor 和 ContentValue 的代码。ios
咱们考虑有如下这些状况:git
SQLiteOpenHelper
的 CustomDbHelper
。LocalDataSource
类,这个是经过 CustomDbHelper
访问数据库的类。LocalDataSource
类的测试。你第一个 PR 会包含设置 Room 所需的最小幅度改动操做。github
若是你已经有每张表数据的 model 对象类,就只用添加 @Entity
, @PrimaryKey
和 @ColumnInfo
的注解。sql
+ @Entity(tableName = "users")
public class User {
+ @PrimaryKey
+ @ColumnInfo(name = "userid")
private int mId;
+ @ColumnInfo(name = "username")
private String mUserName;
public User(int id, String userName) {
this.mId = id;
this.mUserName = userName;
}
public int getId() { return mId; }
public String getUserName() { return mUserName; }
}
复制代码
建立一个继承 RoomDatabase
的抽象类。在 @Database
注解中,列出全部你已建立的 entity 类。如今,咱们就不用再建立 DAO 类了。数据库
更新你数据库版本号并生成一个 Migration 对象。若是你没改数据库的 schema,你仍须要生成一个空的 Migration 对象让 Room 保留已有的数据。后端
@Database(entities = {<all entity classes>},
version = <incremented_sqlite_version>)
public abstract class AppDatabase extends RoomDatabase {
private static UsersDatabase INSTANCE;
static final Migration MIGRATION_<sqlite_version>_<incremented_sqlite_version>
= new Migration(<sqlite_version>, <incremented_sqlite_version>) {
@Override public void migrate(
SupportSQLiteDatabase database) {
// 由于咱们并无对表进行更改,
// 因此这里没有什么要作的
}
};
复制代码
一开始,咱们的 LocalDataSource
类使用 CustomOpenHelper
进行工做,如今我要把它更新为使用 **SupportSQLiteOpenHelper**
,这个类能够从 RoomDatabase.getOpenHelper()
得到。
public class LocalUserDataSource {
private SupportSQLiteOpenHelper mDbHelper;
LocalUserDataSource(@NonNull SupportSQLiteOpenHelper helper) {
mDbHelper = helper;
}
复制代码
由于 SupportSQLiteOpenHelper
并非直接继承 SQLiteOpenHelper
,而是对它的一层包装,咱们须要更改得到可写可读数据库的调用方式,并使用 SupportSQLiteDatabase
而再也不是 SQLiteDatabase
。
SupportSQLiteDatabase db = mDbHelper.getWritableDatabase();
复制代码
SupportSQLiteDatabase
是一个数据库抽象层,提供相似 SQLiteDatabase
中的方法。由于它提供了一个更简洁的 API 去执行插入和查询数据库的操做,代码相比之前也须要作一些改动。
对于插入操做,Room 移除了可选的 nullColumnHack
参数。使用 SupportSQLiteDatabase.insert
代替 SQLiteDatabase.insertWithOnConflict
。
@Override
public void insertOrUpdateUser(User user) {
SupportSQLiteDatabase db = mDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_NAME_ENTRY_ID, user.getId());
values.put(COLUMN_NAME_USERNAME, user.getUserName());
- db.insertWithOnConflict(TABLE_NAME, null, values,
- SQLiteDatabase.CONFLICT_REPLACE);
+ db.insert(TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE,
+ values);
db.close();
}
复制代码
要查询的话,SupportSQLiteDatabase
提供了4种方法:
Cursor query(String query);
Cursor query(String query, Object[] bindArgs);
Cursor query(SupportSQLiteQuery query);
Cursor query(SupportSQLiteQuery query, CancellationSignal cancellationSignal);
复制代码
若是你只是简单地使用原始的查询操做,那在这里就没有什么要改的。若是你的查询是较复杂的,你就得经过 SupportSQLiteQueryBuilder
建立一个 SupportSQLiteQuery
。
举个例子,咱们有一个 users
表,只想得到表中按名字排序的第一个用户。下面就是实现方法在SQLiteDatabase
和 SupportSQLiteDatabase
中的区别。
public User getFirstUserAlphabetically() {
User user = null;
SupportSQLiteDatabase db = mDbHelper.getReadableDatabase();
String[] projection = {
COLUMN_NAME_ENTRY_ID,
COLUMN_NAME_USERNAME
};
// 按字母顺序从表中获取第一个用户
- Cursor cursor = db.query(TABLE_NAME, projection, null,
- null, null, null, COLUMN_NAME_USERNAME + “ ASC “, “1”);
+ SupportSQLiteQuery query =
+ SupportSQLiteQueryBuilder.builder(TABLE_NAME)
+ .columns(projection)
+ .orderBy(COLUMN_NAME_USERNAME)
+ .limit(“1”)
+ .create();
+ Cursor cursor = db.query(query);
if (c !=null && c.getCount() > 0){
// read data from cursor
...
}
if (c !=null){
cursor.close();
}
db.close();
return user;
}
复制代码
若是你没有对你的 SQLiteOpenHelper 实现类进行测试的话,那我强烈推荐你先测试下再进行这个迁移的工做,避免产生相关 bug。
既然你的数据层已经在使用 Room,你能够开始逐渐建立 DAO 类(附带测试)并经过 DAO 的调用替代 Cursor
和 ContentValue
的代码。
像在 users
表中按名字顺序查询第一个用户这个操做应该定义在 UserDao
接口中。
@Dao
public interface UserDao {
@Query(“SELECT * FROM Users ORDERED BY name ASC LIMIT 1”)
User getFirstUserAlphabetically();
}
复制代码
这个方法会在 LocalDataSource
中被调用。
public class LocalDataSource {
private UserDao mUserDao;
public User getFirstUserAlphabetically() {
return mUserDao.getFirstUserAlphabetically();
}
}
复制代码
在单一一个 PR 中,把 SQLite 迁移一个大型的数据库到 Room 会生成不少新文件和更新事后的文件。这须要必定时间去实现,所以致使 PR 更难检查。在最开始的 PR,先使用 RoomDatabase
提供的 OpenHelper 从而让代码最小程度地改动,而后在接下来的 PR 中才逐渐建立 DAO 类去替换 Cursor
和 ContentValue
的代码。
想了解 Room 的更多相关信息,请阅读下面这些文章:
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。