接入方法
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);
}
需要注意的是:當只需要查找類中的幾列時:
- 需要使用@SupperssWarnings注解
- 對于不需要查找的列,其類型如果為基本數據類型,則需要轉換為包裝類,修改其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框架的話,當你在之后的版本中新增了表或者改動了某些表結構的話,你就需要對數據庫的版本號進行相應的更新,現在整理兩種更新方式:
- 簡單粗暴作死型:
@Database(entities = {User.class}, version = 3)
public abstract class UsersDatabase extends RoomDatabase
database = Room.databaseBuilder(context.getApplicationContext(),
UsersDatabase.class, "Sample.db")
//添加下面這一行
.fallbackToDestructiveMigration()
.build();
這種方式會清空數據庫中的數據,所以要使用這種方式之前一定要慎重考慮。fallbackToDestructiveMigration會將所有表全部丟棄。
- 正確姿勢:
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,再有什么情況會更新的~~~