Android使用WCDB+Room ORM

接入方法

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

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

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

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

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

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

實(shí)際換數(shù)據(jù)庫(kù)的時(shí)候,由于無(wú)法打開數(shù)據(jù)庫(kù),導(dǎo)致線程阻塞很久,最后解決方式是刪除了原有的數(shù)據(jù)庫(kù),重新創(chuàng)建

使用 WCDB 其他功能

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

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

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

// 使用 sqlite

或者在初始化時(shí)設(shè)置 callback

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

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

                        // 做其他事
                    }
                })

        .build();

上述功能暫時(shí)沒(méi)用過(guò)

ROOM數(shù)據(jù)庫(kù)使用

ROOM數(shù)據(jù)庫(kù)中三個(gè)主要組成部分

1、Entity

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

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

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

創(chuàng)建數(shù)據(jù)庫(kù)

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

數(shù)據(jù)庫(kù)的增刪查改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方法只接收一個(gè)參數(shù),它可以返回一個(gè)long,這是插入項(xiàng)的新rowId。如果參數(shù)是數(shù)組或集合,則應(yīng)該返回long[]或list<long>。

2、Update

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

可以讓此方法返回一個(gè)int值,指示數(shù)據(jù)庫(kù)中更新的行數(shù)。

3、Delete

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

可以讓此方法返回一個(gè)int值,指示從數(shù)據(jù)庫(kù)中刪除的行數(shù)。

4、Query

每個(gè)@Query方法都在編譯時(shí)進(jìn)行驗(yàn)證,因此如果查詢有問(wèn)題,則會(huì)發(fā)生編譯錯(cuò)誤,而不是運(yùn)行時(shí)失敗。
Room還驗(yàn)證查詢的返回值,以便如果返回對(duì)象中的字段名稱與查詢響應(yīng)中的相應(yīng)列名不匹配,Room將通過(guò)以下兩種方式之一提醒您:

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

需要注意的是:當(dāng)只需要查找類中的幾列時(shí):

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

數(shù)據(jù)庫(kù)升級(jí)

當(dāng)開發(fā)中使用了Google的Room框架的話,當(dāng)你在之后的版本中新增了表或者改動(dòng)了某些表結(jié)構(gòu)的話,你就需要對(duì)數(shù)據(jù)庫(kù)的版本號(hào)進(jìn)行相應(yīng)的更新,現(xiàn)在整理兩種更新方式:

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

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

  1. 正確姿勢(shì):

a) 修改數(shù)據(jù)庫(kù)版本號(hào)

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

b) 創(chuàng)建Migration,1和2分別代表上一個(gè)版本和新的版本

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
      //此處對(duì)于數(shù)據(jù)庫(kù)中的所有更新都需要寫下面的代碼
        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命令非常局限,只支持重命名表以及添加新的字段。

總結(jié)

使用WCDB結(jié)合ROOM數(shù)據(jù)庫(kù),可以大大減少代碼量,但需要在注解中使用sql語(yǔ)句對(duì)數(shù)據(jù)庫(kù)進(jìn)行增刪查改。使用過(guò)程中可能會(huì)遇到各種問(wèn)題,這里總結(jié)一下我遇到的坑:

  • 運(yùn)行時(shí)報(bào)錯(cuò):

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了數(shù)據(jù)庫(kù),但是沒(méi)有操作的話,數(shù)據(jù)庫(kù)是不會(huì)創(chuàng)建到本地的,如果在文件夾里沒(méi)找到,大家不要方(:з」∠)
  • delete和insert操作,如果沒(méi)有對(duì)應(yīng)的數(shù)據(jù)可能會(huì)crash掉,建議使用之前先檢查一下有沒(méi)有數(shù)據(jù)。
  • 持續(xù)掉坑ing,再有什么情況會(huì)更新的~~~
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容