Android使用WCDB+Room ORM

接入方法

1、在接入 Room 的基礎上,gradle 里加上 WCDB 的 room 組件

 dependencies {
    implementation 'com.tencent.wcdb:room:1.0.8'  // 代替 room-runtime,同時也不需要再引用 wcdb-android
    annotationProcessor 'android.arch.persistence.room:compiler:1.1.1' // compiler 需要用 room 的
} 

2、代碼里面,打開 RoomDatabase 時,指定 WCDBOpenHelperFactory 作為 openFactory

QLiteCipherSpec cipherSpec = new SQLiteCipherSpec()  // 指定加密方式,使用默認加密可以省略
        .setPageSize(4096)
        .setKDFIteration(64000);

WCDBOpenHelperFactory factory = new WCDBOpenHelperFactory()
        .passphrase("passphrase".getBytes())  // 指定加密DB密鑰,非加密DB去掉此行
        .cipherSpec(cipherSpec)               // 指定加密方式,使用默認加密可以省略
        .writeAheadLoggingEnabled(true)       // 打開WAL以及讀寫并發,可以省略讓Room決定是否要打開
        .asyncCheckpointEnabled(true);        // 打開異步Checkpoint優化,不需要可以省略

AppDatabase db = Room.databaseBuilder(this, AppDatabase.class, "dbName") //dbName可以使用單獨的名字或者絕對路徑
                //.allowMainThreadQueries()   // 允許主線程執行DB操作,一般不推薦
                .openHelperFactory(factory)   // 重要:使用WCDB打開Room
                .build();

實際換數據庫的時候,由于無法打開數據庫,導致線程阻塞很久,最后解決方式是刪除了原有的數據庫,重新創建

使用 WCDB 其他功能

Room 使用了 SupportSQLiteDatabase 接口來提供底層操作的抽象,Room 所有相關的 API 返回的都是 SupportSQLiteDatabase 接口,如需要使用 WCDB 其他功能(比如 Repair)一般需要 SQLiteDatabase 接口,可以通過下面的方式取得。

// MyDatabase 為生成的 RoomDatabase
MyDatabase db = Room.databaseBuilder(...)
        .openHelperFactory(new WCDBOpenHelperFactory(...))
        .build();

// 用這個方法獲取 SQLiteDatabase 接口
SQLiteDatabase sqlite = ((WCDBDatabase)db.getOpenHelper().getWritableDatabase()).getInnerDatabase();

// 使用 sqlite

或者在初始化時設置 callback

MyDatabase db = Room.databaseBuilder(...)
        .openHelperFactory(new WCDBOpenHelperFactory(...))

        // 添加初始化回調接口
        .addCallback(new RoomDatabase.Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        // 從 SupportSQLiteDatabase 獲取 SQLiteDatabase
                        SQLiteDatabase sqlite = ((WCDBDatabase)db).getInnerDatabase();

                        // 做其他事
                    }
                })

        .build();

上述功能暫時沒用過

ROOM數據庫使用

ROOM數據庫中三個主要組成部分

1、Entity

@Entity
public class User {
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "first_name")
    public String firstName;//如果表中的name跟變量名不同,可以自行設置

    @ColumnInfo(name = "last_name")
    public String lastName;
}

2、Dao

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);//

    @Delete
    void delete(User user);
}

3、DataBase

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

創建數據庫

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

數據庫的增刪查改CRUD

1、Insert

@Dao
public interface MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public void insertUsers(User... users);

    @Insert
    public void insertBothUsers(User user1, User user2);

    @Insert
    public void insertUsersAndFriends(User user, List<User> friends);
}

如果@INSERT方法只接收一個參數,它可以返回一個long,這是插入項的新rowId。如果參數是數組或集合,則應該返回long[]或list<long>。

2、Update

@Dao
public interface MyDao {
    @Update
    public void updateUsers(User... users);
}

可以讓此方法返回一個int值,指示數據庫中更新的行數。

3、Delete

@Dao
public interface MyDao {
    @Delete
    public void deleteUsers(User... users);
}

可以讓此方法返回一個int值,指示從數據庫中刪除的行數。

4、Query

每個@Query方法都在編譯時進行驗證,因此如果查詢有問題,則會發生編譯錯誤,而不是運行時失敗。
Room還驗證查詢的返回值,以便如果返回對象中的字段名稱與查詢響應中的相應列名不匹配,Room將通過以下兩種方式之一提醒您:

  • 如果只有某些字段名匹配,則會發出警告。
  • 如果沒有匹配的字段名,則會產生錯誤。
@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge")
    public User[] loadAllUsersOlderThan(int minAge);
}

需要注意的是:當只需要查找類中的幾列時:

  1. 需要使用@SupperssWarnings注解
  2. 對于不需要查找的列,其類型如果為基本數據類型,則需要轉換為包裝類,修改其get方法。如:
//原get方法
get int getLib_id(){
 return lib_id;
};
//將基本類型改為包裝類
get Integer getLib_id(){
if(this.lib_id == null) return 0;
 return lib_id;
};

數據庫升級

當開發中使用了Google的Room框架的話,當你在之后的版本中新增了表或者改動了某些表結構的話,你就需要對數據庫的版本號進行相應的更新,現在整理兩種更新方式:

  1. 簡單粗暴作死型:
@Database(entities = {User.class}, version = 3)
public abstract class UsersDatabase extends RoomDatabase
database = Room.databaseBuilder(context.getApplicationContext(),
                        UsersDatabase.class, "Sample.db")
                 //添加下面這一行
                .fallbackToDestructiveMigration()
                .build();

這種方式會清空數據庫中的數據,所以要使用這種方式之前一定要慎重考慮。fallbackToDestructiveMigration會將所有表全部丟棄。

  1. 正確姿勢:

a) 修改數據庫版本號

@Database(entities = {User.class}, version = 2)
public abstract class UsersDatabase extends RoomDatabase

b) 創建Migration,1和2分別代表上一個版本和新的版本

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
      //此處對于數據庫中的所有更新都需要寫下面的代碼
        database.execSQL("ALTER TABLE users "
                + " ADD COLUMN last_update INTEGER");
    }
};

c)把migration 添加到 Room database builder

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
         //增加下面這一行
        .addMigrations(MIGRATION_1_2)
        .build();

注:SQLite的ALTER TABLE命令非常局限,只支持重命名表以及添加新的字段。

總結

使用WCDB結合ROOM數據庫,可以大大減少代碼量,但需要在注解中使用sql語句對數據庫進行增刪查改。使用過程中可能會遇到各種問題,這里總結一下我遇到的坑:

  • 運行時報錯:

Android dependency 'android.arch.core:runtime' has different version for the compile (1.0.0) and runtime (1.1.1) classpath.

解決方法:

將implementation 'com.tencent.wcdb:room:1.0.8' 的implementation
改為 api 'com.tencent.wcdb:room:1.0.8'

  • 如果只是build了數據庫,但是沒有操作的話,數據庫是不會創建到本地的,如果在文件夾里沒找到,大家不要方(:з」∠)
  • delete和insert操作,如果沒有對應的數據可能會crash掉,建議使用之前先檢查一下有沒有數據。
  • 持續掉坑ing,再有什么情況會更新的~~~
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容