- 原文地址: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、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。