127

从 SQLite 逐步迁移到 Room

 6 years ago
source link: https://zhuanlan.zhihu.com/p/32177036?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

从 SQLite 逐步迁移到 Room

编程等 2 个话题下的优秀答主
简评: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;
    }

对于插入:

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

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 接口中:

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

以上。如果想了解更多关于 Room 的知识,可以阅读?「扩展阅读」中的文章(需科学上网)。

原文:Incrementally migrate from SQLite to Room

扩展阅读:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK