[譯]從 SQLite 逐步遷移到 Room

從 SQLite 逐步遷移到 Room

經過可管理的 PR 將複雜的數據庫遷移到 Roomhtml

你已經據說過 Room 了吧 —— 或許你已經看過文檔,看過一個兩個視頻,而且決定開始整合 Room 到你的項目中。若是你的數據庫只有幾張表和簡單查詢的話,你能夠很容易地跟着下面這 7 個步驟,經過較小改動的相似 pull request 操做遷移到 Room。前端

不過,若是你的數據庫較大或者有複雜的查詢操做的話,實現全部 entity 類,DAO 類,DAO的測試類而且替換 SQLiteOpenHelper 的使用就會耗費不少時間。你最終會須要一個大改動的 pull request,去實現這些和檢查。讓咱們看看你怎麼經過可管理的 PR(pull request),逐步從 SQLite 遷移到 Room。java

文長不讀的話,能夠看下面的歸納點:

第一個 PR:建立你的 entity 類,RoomDatabase,而且更新你自定義的 SQLiteOpenHelper 爲 SupportSQLiteOpenHelperandroid

其他的 PR:建立 DAO 類去代替有 Cursor 和 ContentValue 的代碼。ios

項目設置

咱們考慮有如下這些狀況:git

  • 咱們的數據庫有 10 張表,每張有一個相應的 model 對象。例如,若是有 users 表的話,咱們有相應的 User 對象。
  • 一個繼承自 SQLiteOpenHelperCustomDbHelper
  • LocalDataSource 類,這個是經過 CustomDbHelper 訪問數據庫的類。
  • 咱們有一些對 LocalDataSource 類的測試。

第一個 PR

你第一個 PR 會包含設置 Room 所需的最小幅度改動操做。github

建立 entity 類

若是你已經有每張表數據的 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; }
}
複製代碼

建立 Room 數據庫

建立一個繼承 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) {
           // 由於咱們並無對錶進行更改,
           // 因此這裏沒有什麼要作的 
         }
    };
複製代碼

更新使用 SQLiteOpenHelper 的類

一開始,咱們的 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 表,只想得到表中按名字排序的第一個用戶。下面就是實現方法在SQLiteDatabaseSupportSQLiteDatabase 中的區別。

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。

其他的 PR

既然你的數據層已經在使用 Room,你能夠開始逐漸建立 DAO 類(附帶測試)並經過 DAO 的調用替代 CursorContentValue 的代碼。

像在 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 類去替換 CursorContentValue 的代碼。

想了解 Room 的更多相關信息,請閱讀下面這些文章:


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索