前言
移動端的數(shù)據(jù)庫選型一直是一個難題唧席,直到前段時間看到了WeMobileDev(微信前端團隊)放出了第三個開源組件-WCDB
WCDB(WeChat DataBase)是微信官方的移動端數(shù)據(jù)庫組件擦盾,致力于提供一個高效嘲驾、易用、完整的移動端存儲方案
微信團隊怎么說
基于SQLCipher
WCDB-iOS/Mac
WCDB-Android
數(shù)據(jù)庫損壞修復(fù)工具WDBRepair
背景
WCDB的出現(xiàn)可以說解決了目前移動端數(shù)據(jù)庫的幾個難點
- 首先在選型上迹卢,FMDB的SQL拼接辽故、難以防止的SQL注入;CoreData雖然可以方便ORM腐碱,但學(xué)習(xí)成本高誊垢,穩(wěn)定性堪憂,而且多線程雞肋症见;另外基于C語言的sqlite我想用的人也應(yīng)該不多喂走;除了上述關(guān)系型數(shù)據(jù)庫之外然后還有一些其他的Key-Value型數(shù)據(jù)庫,如我用過的Realm谋作,對于ObjC開發(fā)者來說芋肠,上手倒是沒什么難度,但缺點顯而易見遵蚜,需要繼承帖池,入侵性強,對于單繼承的OC來說這并不理想吭净,而且對于集合類型不完全支持睡汹,復(fù)雜查詢也比較無力。
-
高效
多線程高并發(fā):WCDB支持多線程讀與讀寂殉、讀與寫并發(fā)執(zhí)行帮孔,寫與寫串行執(zhí)行。
-
批量寫操作性能測試:
批量寫 ops/sec WCDB 458000 FMDB 161000
-
易用 WCDB支持一句代碼即可將數(shù)據(jù)取出并組合為object
WINQ(WCDB語言集成查詢):通過WINQ不撑,開發(fā)者無須為了拼接SQL的字符串而寫一大坨膠水代碼文兢。
ORM(Object Relational Mapping):WCDB支持靈活、易用的ORM焕檬。開發(fā)者可以很便捷地定義表姆坚、索引、約束实愚,并進行增刪改查操作兼呵。
-
像這樣
[database getObjectsOfClass:WCTSampleConvenient.class fromTable:tableName where:WCTSampleConvenient.intValue>=10 limit:20];
-
完整
- 加密:WCDB提供基于SQLCipher的數(shù)據(jù)庫加密。
- 損壞修復(fù):WCDB內(nèi)建了Repair Kit用于修復(fù)損壞的數(shù)據(jù)庫腊敲。
- WCDB提供接口直接獲取SQL的執(zhí)行耗時击喂,可用于監(jiān)控性能。
- 反注入:WCDB內(nèi)建了對SQL注入的保護
ORM
在WCDB內(nèi)碰辅,ORM(Object Relational Mapping)是指
將一個ObjC的類懂昂,映射到數(shù)據(jù)庫的表和索引;
將類的property没宾,映射到數(shù)據(jù)庫表的字段凌彬;
這一過程沸柔。通過ORM,可以達到直接通過Object進行數(shù)據(jù)庫操作铲敛,省去拼裝過程的目的褐澎。
WCDB通過內(nèi)建的宏實現(xiàn)ORM的功能。如下
PS:但我不建議這么做伐蒋,首先要避免在.h文件中引用<WCDB/WCDB.h>,因為你一旦引用工三,就需要改變.m文件為.mm文件,因為WCDB是基于objectiveC++先鱼;你可以使用Category特性將其隔離俭正,在category中引用<WCDB/WCDB.h>,并遵守WCTTableCoding協(xié)議型型,使用WCDB_PROPERTY將聲明綁定到數(shù)據(jù)庫表的字段段审。然后在模型類中引用category全蝶。達到不印象Controller和View的目的闹蒜。這點官方wiki中也有提到,使用文件模板來創(chuàng)建抑淫。具體請見Demo
對于一個已有的ObjC類绷落,
引用WCDB框架頭文件#import <WCDB/WCDB.h>,并定義類遵循WCTTableCoding協(xié)議
WCDB_PROPERTY用于在頭文件中聲明綁定到數(shù)據(jù)庫表的字段始苇。
WCDB_IMPLEMENTATION砌烁,用于在類文件中定義綁定到數(shù)據(jù)庫表的類。同時催式,該宏內(nèi)實現(xiàn)了WCTTableCoding函喉。因此,開發(fā)者無須添加更多的代碼來完成WCTTableCoding的接口
WCDB_SYNTHESIZE荣月,用于在類文件中定義綁定到數(shù)據(jù)庫表的字段管呵。
WCDB_PRIMARY用于定義主鍵
WCDB_PRIMARY_AUTO_INCREMENT 用于定義自增主鍵
WCDB_INDEX用于定義索引
WCDB_UNIQUE用于定義唯一約束
WCDB_NOT_NULL用于定義非空約束
CRUD
得益于ORM的定義,WCDB可以直接進行通過object進行增刪改查(CRUD)操作哺窄。
-
增
//插入 Person *man = [[Person alloc] init]; man.isAutoIncrement = YES; man.name = @"Hello, WCDB!"; man.age = 12; return [database insertObject:man into:TABLE_WCDB_NAME];
-
刪
return [database deleteObjectsFromTable:TABLE_WCDB_NAME where:Person.studentId == studentId];
-
改
Person *person = [[Person alloc] init]; person.name = content; return [database updateRowsInTable:TABLE_WCDB_NAME onProperties:Person.name withObject:person where:Person.studentId == studentId];
-
查
NSArray<Person *> * person = [database getObjectsOfClass:Person.class fromTable:TABLE_WCDB_NAME orderBy:Person.localID.order()];
Transaction
WCDB內(nèi)可通過兩種方式執(zhí)行Transaction(事務(wù))捐下,一是runTransaction:接口
這種方式要求數(shù)據(jù)庫操作在一個BLOCK內(nèi)完成,簡單易用萌业。
另一種方式則是獲取WCTTransaction對象
WCTTransaction對象可以在類或函數(shù)間傳遞坷襟,因此這種方式也更具靈活性。
WINQ
WINQ(WCDB Integrated Query生年,音'wink')婴程,即WCDB集成查詢,是將自然查詢的SQL集成到WCDB框架中的技術(shù)抱婉,基于C++實現(xiàn)排抬。
- 免去拼接SQL字符串懂从、防注入
- 借助IDE代碼提示和編譯器語法檢查
- 對于一個已綁定ORM的類,可以通過className.propertyName的方式蹲蒲,獲得數(shù)據(jù)庫內(nèi)字段的映射
- WINQ的接口包括但不限于:
- 一元操作符:+番甩、-、!等
- 二元操作符:||届搁、&&缘薛、+、-卡睦、*宴胧、/、|表锻、&恕齐、<<、>>瞬逊、<显歧、<=、==确镊、!=士骤、>、>=等
- 范圍比較:IN蕾域、BETWEEN等
- 字符串匹配:LIKE拷肌、GLOB、MATCH旨巷、REGEXP等
- 聚合函數(shù):AVG巨缘、COUNT、MAX采呐、MIN若锁、SUM等
- ...
原理
- 初衷,適應(yīng)WCDB+ORM解決SQL字符串的代碼冗余和難以被編譯器進行語法檢查而造成的錯誤和時間浪費懈万。SQL字符串太容易被注入
- SQL抽象
- 封裝常用操作拴清,覆蓋80%的使用場景
- 暴露底層接口,適配剩余20%的特殊情況
- 定義常用操作
- 特殊場景所暴露的底層接口会通,應(yīng)該以什么形式存在口予?
- SELECT、DISTINCT涕侈、ALL等等大寫字母是keyword沪停,屬于SQL的保留字。
- result-column、``table-or-subquery木张、expr等等小寫字母是token众辨。token可以再進一步地展開其構(gòu)成的語法規(guī)則。
- 將固定的keyword舷礼,封裝為函數(shù)名鹃彻,作為連接。
- 將可以展開的token妻献,封裝為類蛛株,并在類內(nèi)實現(xiàn)其不同的組合。
- 在語法規(guī)則中育拨,WHERE谨履、LIMIT等都接受expr作為參數(shù)。因此熬丧,不管SQL多么復(fù)雜笋粟,StatementSelect也只接受Expr的參數(shù)。而其組合的能力析蝴,則在Expr類內(nèi)實現(xiàn)害捕。
數(shù)據(jù)庫修復(fù)
- 官方的Dump恢復(fù)方案
- 遍歷sqlite_master表,將未損壞的表和已損壞的前半部分讀取出來將dump 出來的SQL語句逐行執(zhí)行嫌变,最終可以得到一個等效的新DB
功率約為30%吨艇。 - 第一頁就損壞后續(xù)無法讀取
- 遍歷sqlite_master表,將未損壞的表和已損壞的前半部分讀取出來將dump 出來的SQL語句逐行執(zhí)行嫌变,最終可以得到一個等效的新DB
- 備份恢復(fù)方案
- COPY
- 在DB完好的時候執(zhí)行.dump
- Backup API: SQLite自身提供的一套備份機制躬它,按 Page 為單位復(fù)制到新 DB腾啥, 支持熱備份。
- 最終選擇Dump + 壓縮冯吓,恢復(fù)成功率達到72%
- 解析B-tree恢復(fù)方案(RepairKit)
- 成功率約為78%
- 不同方案的組合
- RepairKit 嘗試恢復(fù)最新數(shù)據(jù)
- 備份恢復(fù) 遇到錯誤填補漏缺
- Dump 最后的嘗試
For Android
-
基本功能
- 基于SQLCipher的數(shù)據(jù)庫加密
- 使用連接池實現(xiàn)并發(fā)讀寫
- 內(nèi)建 Repair Kit 可用于修復(fù)損壞數(shù)據(jù)
- 針對占用空間大小優(yōu)化的數(shù)據(jù)庫備份/恢復(fù)功能
- 日志輸出重定向以及性能跟蹤接口
- 內(nèi)建用于全文搜索的 mmicu FTS3/4 分詞器
-
接入與遷移
- WCDB for Android 可通過 Maven 或 AAR 包引用倘待,API 接口與 Android SDK 非常相近, 所以將已有的 App 遷移到 WCDB 是相當(dāng)容易的组贺。
- Android 接入與遷移
-
數(shù)據(jù)庫修復(fù)
-
從源碼編譯
- 你可以使用預(yù)編譯的依賴庫(OpenSSL crypto 和 SQLCipher)來編譯 WCDB for Android凸舵, 使用 Gradle 或 Android Studio 皆可。Android Studio 請導(dǎo)入 android 目錄作為 Root Project失尖。
- 編譯 WCDB 需要安裝 Android NDK r11c 或以上啊奄,并在 android/local.properties 上配置好 SDK 與 NDK 路徑。Android Studio 一般會幫你配置好掀潮。
- 如果你需要自己編譯 OpenSSL 等依賴項菇夸,你需要一個 Bash 環(huán)境(Windows 可以安裝 Cygwin 或 MSys)、target 為本機的 C 編譯器(如 GCC)仪吧、Perl 5 以及 Tcl庄新。之后執(zhí)行下面命令即可編譯依賴項。
參考資料