接入方法
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í):
- 需要使用@SupperssWarnings注解
- 對(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)在整理兩種更新方式:
- 簡(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ì)將所有表全部丟棄。
- 正確姿勢(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ì)更新的~~~