從 SQLite 逐步遷移到 Room

簡評:Google 工程師的複雜 SQLite 資料庫 Room 遷移指南。

Room Persistence Library 是 Google 官方提供的數據持久化類庫,提供了 SQLite 的抽象層。官方文檔中強烈推薦使用 Room 代替直接操作 SQLite。如果你決定使用 Room,但資料庫較大或者有複雜查詢時,遷移過程就會比較耗時和麻煩。

這篇文章就是 Google 的一位工程師將 SQLite 遷移到 Room 的步驟拆解成了 PR。

項目場景

想像我們有一個的項目是這樣的:

  • 我們的資料庫有 10 張表,分別對應一個 model 類。比如,對於 users 表,有一個相應的 User 類。
  • 一個繼承自 SQLiteOpenHelper 的 CustomDbHelper。
  • 會在 LocalDataSource 類中通過 CustomDbHelper 進行訪問資料庫的操作。
  • 一些針對 LocalDataSource 的測試。

First PR

我們的第一個 PR 會包含啟用 Room 最低限度的修改。

實現 entity 類

為每張表對應的 model 類增加 @Entity, @PrimaryKey 和 @ColumnInfo 註解。

+ @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; }}

創建 Room 資料庫

創建一個繼承 RoomDatabase 的抽象類。在 @Database 註解中列出所有創建的 entity 類。再增加資料庫版本號並實現一個 Migration:

@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) { // Since we didn』t alter the table, there』s nothing else // to do here. } };

更新 SQLiteOpenHelper 為 SupportSQLiteOpenHelper

起初,我們是在 LocalDataSource 中使用我們自己實現的 CustomOpenHelper。現在我們要改成使用 SupportSQLiteOpenHelper 了,SupportSQLiteOpenHelper 提供了更加簡潔的 API。

public class LocalUserDataSource { private SupportSQLiteOpenHelper mDbHelper; LocalUserDataSource(@NonNull SupportSQLiteOpenHelper helper) { mDbHelper = helper; }

對於插入:

@Overridepublic 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 提供了四個方法:

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 }; // Get the first user from the table ordered alphabetically - 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; }

接下來的 PRs

完成以上步驟我們的數據層實現就已經變為 Room 了,之後可以開始逐步創建 DAO(包括測試),並用 DAO 替換 Cursor 和 ContentValue 的代碼。

上面我們實現的從 users 表獲得按用戶名排序的第一個用戶方法,應該被定義在 UserDao 介面中:

@Daopublic 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(); }}

以上。如果想了解更多關於 Room 的知識,可以閱讀??「擴展閱讀」中的文章(需科學上網)。

原文:Incrementally migrate from SQLite to Room

擴展閱讀:

  • 7 Pro-tips for Room
  • Understanding migrations with Room
  • Testing Room migrations

推薦閱讀:

千元機和旗艦機相比,究竟哪裡縮水了?
Android應用程序通用自動脫殼方法研究

TAG:Android | Android开发 |