ios swift版 sqlite3详解
iOS中的SQLite3的封裝與詳細應用
?
SQLite是一個開源的嵌入式關系數據庫,特點是易使用、高效、安全可靠、可移植性強。
iOS中的本地持久化存儲
NSUserDefault:一般用于存儲小規模數據、業務邏輯弱的數據。
keychain: 蘋果提供的可逆存儲,因為有著只要app不重裝系統、可以同步iCloud的特性,一般用來對用戶的標識符或者一些需要加密的小數據進行存儲。
歸檔:主要原理是對數據進行序列化、反序列化操作后,寫入、讀出數據。方便便捷易使用,缺點查詢、更改數據耗時耗性能。
數據庫:主要的有三種sqlite3、core data、realm。其中core data只是xcode對sqlite的界面化的封裝原理相似,realm官方文檔.
關于sqlite本文將主要介紹。
SQLite3中主要函數介紹
sqlite3_open(文件路徑,sqlite3 **):文件名若不存在,則會自動創建
sqlite3_close(sqlite3 *):關閉數據庫
sqlite3__finalize(sqlite3_stmt *pStmt): 釋放數據庫
sqlite3_errmsg(sqlite3*):輸出數據庫錯誤
sqlite3__exec(sqlite3 ,const char?sql, sqlite3_callback,void ,char *errmsg):
參數1:open函數得到的指針。
參數2:一條sql語句
參數3:sqlite3_callback是回調,當這條語句執行后,sqlite3會調用你提供的這個函數,回調函數
參數4:void *是自己提供的指針,可以傳遞任何指針到這里,這個參數最終會傳到回調函數里面,如果不需要傳到回調函數里面,則可以設置為NULL
參數5:錯誤信息,當執行失敗時,可以查閱這個指針
sqlite3_prepare_v2(sqlite3?db,const char?zSql, int nByte,sqlite3_stmt?ppStmt,const char?pzTail):
參數3:表示前面sql語句的長度,如果小于0,sqlite會自動計算它的長度
參數4:sqlite3_stmt指針的指針,解析后的sql語句就放在該結構里
參數5:一般設為0
sqlite3_step(sqlite3_stmt*):
參數為sqlite3_prepare_v2中的sqlite3_stmt
返回SQLITE_ROW 表示成功
sqlite3_bind_text(sqlite3_stmt, int, const char, int n, void()(void)):
參數1:sqlite3_prepare_v2中的sqlite3_stmt
參數2:對應行數
參數3:對應行數的值
參數4:對應行數的值的長度,小于0自動計算長度
參數5:函數指針,主要處理特殊類型的析構
sqlite3_key( sqlite3?db, const void?pKey, int nKey)
參數2:密鑰
參數3:密鑰長度
swift與c的類型轉換
int => CInt
char => CChar / CSignedChar
char* => CString
unsigned long = > CUnsignedLong
wchar_t => CWideChar
double => CDouble
T* => CMutablePointer
void* => CMutableVoidPointer
const T* => CConstPointer
const void* => CConstVoidPointer
等等 …
參考地址 http://www.cocoachina.com/industry/20140619/8884.html
創建或者打開數據庫
程序中‘db’不能為空,如果為空,表示打開數據庫失敗或者關閉了數據庫。
@discardableResult ? private func openDB() -> Bool{ ? ?if db == nil { ? ? ? ?
?let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/\(DB_NAME)"? print(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]) ? ? ? ?
?if sqlite3_open(path.cString(using: String.Encoding.utf8)!,&db) != SQLITE_OK { ? ? ? ? ? ?closeDb() ? ? ? ? ? ?
? return false ?}else { ? ? ? ? ? ?//對數據庫進行加密 ? sqlite3_key(db, SAFE_KEY.cString(using: String.Encoding.utf8), Int32(SAFE_KEY.characters.count)) ?} ?sqlite3_busy_handler(db, { (ptr,count) in ? usleep(500000)//如果獲取不到鎖,表示數據庫繁忙,等待0.5秒 ? print("sqlite is locak now,can not write/read.") ? ? ? ? ? ?
? return 1 ? //回調函數返回值為1,則將不斷嘗試操作數據庫。 ? }, &db) ?} ? ? ? ?
return true}
通過sql執行數據庫操作
由于防止多線程操作數據庫,每次執行數據庫操作添加同步鎖。
sqlite3_exec函數基本上支持所有的數據庫執行語句除了含有特殊類型的數據(二進制),含有特殊類型的數據會采用另一種方式處理下面會闡述。
查詢數據庫
按照數據庫的行數依次查詢,輸出sql條件的所有數據
public ?func querySql(sql:String) -> [[String:Any]]? {objc_sync_enter(self)if ?!self.openDB() {objc_sync_exit(self)return nil}var arr:[[String:Any]] = []var ?statement: OpaquePointer? = nilif sqlite3_prepare_v2(db,sql.cString(using: String.Encoding.utf8)!,-1,&statement,nil) == SQLITE_OK {while sqlite3_step(statement) == SQLITE_ROW {let columns = sqlite3_column_count(statement)var row:[String:Any] = Dictionary()for i in 0..<columns {let type = sqlite3_column_type(statement, i)let chars = UnsafePointer<CChar>(sqlite3_column_name(statement, i))let name = ?String.init(cString: chars!, encoding: String.Encoding.utf8)var value: Anyswitch type {case SQLITE_INTEGER:value = sqlite3_column_int(statement, i)case SQLITE_FLOAT:value = sqlite3_column_double(statement, i)case SQLITE_TEXT:let chars = UnsafePointer<CUnsignedChar>(sqlite3_column_text(statement, i))value = String.init(cString: chars!)case SQLITE_BLOB:let data = sqlite3_column_blob(statement, i)let size = sqlite3_column_bytes(statement, i)value = NSData(bytes:data, length:Int(size))default:value = ""()? ? ? }row.updateValue(value, forKey: "\(name!)")}arr.append(row)}}sqlite3_finalize(statement)objc_sync_exit(self)if arr.count == 0 {return nil}else{return arr}}
引入事務,加快數據庫寫入
事務(Transaction)是一個對數據庫執行工作單元。事務(Transaction)是以邏輯順序完成的工作單位或序列,可以是由用戶手動操作完成,也可以是由某種數據庫程序自動完成。
事務(Transaction)是指一個或多個更改數據庫的擴展。例如,如果您正在創建一個記錄或者更新一個記錄或者從表中刪除一個記錄,那么您正在該表上執行事務。重要的是要控制事務以確保數據的完整性和處理數據庫錯誤。
實際上,您可以把許多的 SQLite 查詢聯合成一組,把所有這些放在一起作為事務的一部分進行執行
BEGIN TRANSACTION 開啟一個事務
COMMIT TRANSACTION 提交事務是否成功
ROLLBACK TRANSACTION 回滾事務,當數據庫事務操作失敗后,還原之前的操作。
注意:事務并不能批量優化查詢速度。
public func doTransaction(exec: ((_ db:OpaquePointer)->())?) {objc_sync_enter(self)if ?!self.openDB() {objc_sync_exit(self)return}if exec != nil {? var err: UnsafeMutablePointer<Int8>? = nilif sqlite3_exec(db, "BEGIN TRANSACTION", nil, nil, &err) == SQLITE_OK {exec!(db!)if sqlite3_exec(db, "COMMIT TRANSACTION", nil, nil, &err) == SQLITE_OK {print("提交事務成功")
? ?}else {print("提交事務失敗原因\(err)")if let error = String(validatingUTF8:sqlite3_errmsg(db)) {print("execute failed to execute ?Error: \(error)")}if sqlite3_exec(db, "ROLLBACK TRANSACTION", nil, nil, &err) == SQLITE_OK {print("回滾事務成功")}}}else {if sqlite3_exec(db, "ROLLBACK TRANSACTION", nil, nil, &err) == SQLITE_OK {print("回滾事務成功")}}sqlite3_free(err)}objc_sync_exit(self) }
SQLite3支持有限的 ALTER TABLE 操作
SQLite 有有限地 ALTER TABLE 支持。你可以使用它來在表的末尾增加一列,可更改表的名稱。 如果需要對表結構做更復雜的改變,則必須重新建表。重建時可以先將已存在的數據放到一個臨時表中,刪除原表, 創建新表,然后將數據從臨時表中復制回來。在增加表列時,需注意:因為app在市場上存在許多版本,各個版本的數據庫表的結構可能存在梯度的差異,代碼中使用就需要加入版本控制了。例如代碼中添加一個‘id’字段。
let defaults = UserDefaults.standard let version = defaults.value(forKey: USER_SQL_VERSION)//控制刪除數據庫的版本記錄 let update = defaults.value(forKey: USER_SQL_UPDATE)//控制增加數據庫字段的版本記錄 if let version = version ,(version as! String) == USER_SQL_VERSION_CODE {if let update = update as? String{if ?let intUpdate = Int(update.replacingOccurrences(of: ".", with: "")) {if intUpdate > 100 {if let _ = SQLiteTable.shared.querySql(sql: "select id from \(USER_TABLENAME)"){} else {SQLiteTable.shared.execSql(sql: "ALTER TABLE \(USER_TABLENAME) ?ADD COLUMN ?\("id") INTEGER DEFAULT 0 IF NOT EXISTS")}}}}defaults.setValue(USER_SQL_UPDATE_CODE, forKey: USER_SQL_UPDATE)}else {SQLiteTable.shared.dropTable(tableName: USER_TABLENAME)defaults.setValue(USER_SQL_VERSION_CODE, forKey: USER_SQL_VERSION) }SQLite3中添加索引
? ?/// 創建一張表////// - Parameters:/// ? - tableName: 表名/// ? - data: 數據字段/// ? - dataArray: 添加索引的字段/// - Returns: 是否成功 ? public func createTable(tableName:String, andColoumName data:[String:String] ,andAddIndex dataArray:[String]) -> Bool {let result = self.createTable(tableName: tableName, andColoumName: data)dataArray.forEach { (str) inself.execSql(sql: "CREATE INDEX IF NOT EXISTS index_\(str) ?ON \(tableName) (\(str))")}return result}處理Dada類型數據
sqlite3__exec函數并不是萬能的,它就無法處理二進制數據。處理二進制數據,需要的是另一種方法。
先對表的數據解析然后綁定到結構體中,然后對數據庫進行INSERT DELETE 或者 UPDATE操作。
SQLite3的一些小坑
1.SQL 標準規定,在字符串中,單引號需要使用逃逸字符(”),即在一行中使用兩個單引號。
2.每次操作完數據庫記得關閉數據庫,防止多個數據庫混淆。
SQLite3中查詢效率優化
1.添加索引 在demo中測試發現,插入50000行數據,同時做查詢時發現,添加索引耗時76s,不添加花費256s。添加索引需注意:
添加索引的原則是為了查詢更加快,但是一張表內不能創建太多索引,因為索引只增加了相應的 select 的效率,但同時也降低了 insert 及 update 的效率,一個表的索引數最好不要超過6個。
在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使 用,并且應盡可能的讓字段順序與索引順序相一致。
2.在使用索引字段時,需要避免使用OR/BETWEEN/LIKE這些語法,這樣會避免使用索引而對表進行全局掃描。如demo中使用like語法與‘==’插入50000行數據,同時做查詢時,對比發現分別耗時546s和76s。
SQLite3加密
使用SQLCipher鏈接地址這個第三方庫對數據庫進行加密。
sqlite3_key 加密函數
sqlite3_rekey 修改密碼
最后
點擊鏈接地址進入觀看完整代碼。
具體請參考:?https://github.com/tianjifou/CoreSQLite3.git;
轉載于:https://www.cnblogs.com/hjltonyios/p/8878947.html
總結
以上是生活随笔為你收集整理的ios swift版 sqlite3详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 外媒评出中国最美20个景点
- 下一篇: UVA11019 Martix Matc