基於SQLite自身的諸多優點, 在IOS開發中它是不二的選擇; 除了會使用框架, 接下來我們來看一下原生SQLite是如何操作的.
- 添加動態庫
注意: xcode7 默認添加 libsqlite3.tbd, 這種tbd的方式只能用在IOS9.0的系統上,如果想用在7.0/8.0系統上, 這樣導入的動態庫是用不了的.接下來怎麼辦呢?
添加動態庫選擇add other–>(shift+command+g) /user/lib/libsqlite3.dylib –>open 這樣動態庫就可以向下兼容了. - 創建橋接文件 SQLite-Header.h, 導入頭文件
- 配置橋接文件 Objective-C Bridging Header 中—-項目名/橋接文件名
接下來就進入數據庫了:
數據庫是獨立的文件, 每個APP都有獨立的沙盒, 數據庫就存在APP的沙盒中. 應用程序訪問時直接訪問這個唯一的數據庫文件, 因此在實際開發中我們操作這個數據庫文件時採用單例;單例的好處: 一個APP有多個頁面, 可能都會涉及到數據庫訪問, 爲了方便管理, 因此採用單例.
- 建立單例 SQLiteManager
// 單例
static let sharedManager = SQLiteManager()
- 打開數據庫
// 全局的數據庫訪問句柄
// C語言中沒有對象, 如果想要實現類似面向對象的功能, 是通過結構體實現的
// db爲指向結構體的指針
var db: COpaquePointer = nil
// 打開數據庫
// 目的: 數據庫是一個獨立的文件,在程序啓動時,打開一次,提供存儲數據的空間!
// 參數: 數據庫名字
func openDB(dbName: String) {
var path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
// 數據庫在沙盒中的路徑
path = (path as NSString).stringByAppendingPathComponent(dbName)
// 參數1:(filename: UnsafePointer<Int8>) 文件名 Int8 -> uint8 -> Char -> Byte 純 C 語言格式的路徑
// 但是,從 Xcode 7.0 beta 5 之後,可以使用 String (Swift)
// 參數2: (ppDb: UnsafeMutablePointer<COpaquePointer>)`全局`數據庫訪問`句柄`(指針) -> 後續所有數據庫的操作,都基於這個句柄
// 這個函數執行完了來改變db的地址, 因此上面定義用var
// 返回值: SQLite 的函數,絕大多數成功都是用 SQLITE_OK 判斷
// 細節: 如果數據庫不存在,會新建再打開,如果已經存在,會直接打開!
if sqlite3_open(path, &db) != SQLITE_OK {
print("數據庫打開失敗")
return
}
print("數據庫打開成功")
// 打開成功後, 創建數據表, 函數在後面
createTable1()
}
- 打開數據庫函數我們寫好了, 去哪裏調用呢? – APPDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// `持久化`連接
// 在開發本地數據庫的時候,通常只會打開數據庫,不會關閉數據庫
// 一旦打開之後,後續只需要做讀寫操作即可,效率更高!
// 分享: 對應的網絡有長連接, 但是做網絡開發,數據庫訪問結束後,是必須關閉,並且斷開連接(切斷的目的: 把服務器的鏈接資源釋放, 保證一臺服務器爲更多地用戶響應)!
SQLiteManager.sharedManager.openDB("my.db")
return true
}
- 數據庫成功打開, 但目前還是空的; 想要存儲數據, 首先要創建數據表–> 創建數據表分爲兩部分: 準備SQL + 執行SQL
- 執行SQL
func execSQL(sql: String) -> Bool {
// 參數1: 全局數據庫句柄
// 參數2: sql 語句的 C 語言的字符串,swift 可以使用 String
// 參數3: callback 回調-執行 SQL 完成之後,調用的 C 語言的函數指針,通常傳入 nil
// 參數4: 參數3的參數地址,通常傳入 nil
// 參數5: 錯誤信息的地址,通常傳入 nil,有其他方式獲取錯誤提示
// 返回值: SQLITE_OK
*/
return sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK
}
- 2.創建數據表
- 2.1 方式1
private func createTable1() -> Bool {
// 1. 準備 SQL
let sql = "CREATE TABLE \n" +
"IF NOT EXISTS 'T_Person' ( \n" +
"'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \n" +
"'name' TEXT,\n" +
"'age' INTEGER,\n" +
"'height' REAL, \n" +
"'title' TEXT\n" +
");"
print(sql)
// 2. 執行 SQL
return execSQL(sql)
}
數據庫開發技巧分享:
// 將sql語句拼接好打印出來利用 navicat 做語法檢查
// 拼接 sql 的時候,可以在末尾添加 ‘\n’,讓 SQL 更好閱讀!
- 2.2 方式2
創建一個空白文件db.sql(可以創建多個數據表)
-- 創建個人數據表
CREATE TABLE IF NOT EXISTS "T_Person" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"age" INTEGER,
"height" REAL
);
-- 創建圖書數據表
CREATE TABLE IF NOT EXISTS "T_Book" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"bookName" TEXT
);
讀取db.sql,創建數據表
private func createTable2() -> Bool {
// 1. 準備 sql - 讀取文件
let path = NSBundle.mainBundle().pathForResource("db.sql", ofType: nil)!
let sql = try! String(contentsOfFile: path)
// 2. 執行sql
return execSQL(sql)
執行結果: 在數據庫中兩個表都創建成功(個人/圖書)
注意: 每一次應用程序啓動, path是不一樣的
- 3.在打開數據庫函數中對創建數據表加一個判斷告訴外界是否創建成功
if createTable1() {
print("創表成功")
} else {
print("創表失敗")
}
- 數據表有了, 接下來就是創建要存儲的內容(person對象)
// Person身上的屬性
var id: Int64 = 0
var name: String?
var age = 0
var height: Double = 0
// person的構造方法
init(dict: [String: AnyObject]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
// 重寫description方法
override var description: String {
let keys = ["id", "name", "age", "height"]
// 下面兩種方式等價
return "\(dictionaryWithValuesForKeys(keys))"
// return dictionaryWithValuesForKeys(keys).description
}
// MARK: - 數據庫操作
// 注: 除了查詢語句, 幾乎所有的數據庫操作就是幹兩件事情–準備SQL/執行SQL
目標1: 插入數據
- 首先在Manager中新建一個函數, 執行sql語句並返回id
/// 執行 SQL 插入數據,返回 自動增長的 id
func execInsert(sql: String) -> Int64 {
// 1. 執行 SQL,如果失敗,返回 -1
if !execSQL(sql) {
return -1
}
// 2. 如果成功,返回最後插入記錄的 id,自動增長的 id
return Int64(sqlite3_last_insert_rowid(db))
}
- person類中開始進行數據庫操作
/// 將當前對象插入到數據庫
func insertPerson() -> Bool {
// 0. 判斷 name 是否爲nil
guard let name = name else {
print("name 不能爲 nil")
return false
}
// 1. 準備 SQL
let sql = "INSERT INTO T_Person (name, age, height) VALUES \n" +
"('\(name)', \(age), \(height));"
// 2. 執行 SQL
id = SQLiteManager.sharedManager.execInsert(sql)
// id > 0 表示數據插入成功!
return id > 0
}
- viewController中創建person並插入到數據庫
private func demoInsert() {
let p = Person(dict: ["name": "zhang", "age": 19, "height": 1.6])
if p.insertPerson() {
print("插入成功 \(p)")
} else {
print("插入失敗")
}
}
目標2: 更新數據
- 首先在Manager中新建一個函數, 返回id
/// 執行 SQL 更新 / 刪除 數據,返回更新的數據行數
func execUpdate(sql: String) -> Int {
// 1. 執行 SQL,如果失敗,返回 -1
if !execSQL(sql) {
return -1
}
// 2. 如果 更新 / 刪除 成功,返回最後`修改/刪除`的數據行數
return Int(sqlite3_changes(db))
}
- person類中開始進行數據庫操作
/// 使用當前對象的信息更新數據庫
func updatePerson() -> Bool {
// 0. 判斷 name 是否爲nil
guard let name = name else {
print("name 不能爲 nil")
return false
}
if id <= 0 {
print("id 不正確,person和數據庫沒有關係")
return false
}
// 1. 準備 SQL
let sql = "UPDATE T_Person set name = '\(name)', age = \(age), height = \(height) \n" +
"WHERE id = \(id);"
// 2. 執行 SQL - 如果修改的行數 > 0 ,表示修改成功
return SQLiteManager.sharedManager.execUpdate(sql) > 0
}
- ViewController中執行
private func demoUpdate() {
let p = Person(dict: ["id": 1, "name": "張三", "age": 190, "height": 16])
if p.updatePerson() {
print("更新成功 \(p)")
} else {
print("更新失敗")
}
目標3: 刪除數據
- person類中開始進行數據庫操作
// 刪除當前對象在數據庫中的記錄
func deletePerson() -> Bool {
if id <= 0 {
print("id 不正確,person和數據庫沒有關係")
return false
}
// 1. 準備 sql
let sql = "DELETE FROM T_Person WHERE id = \(id);"
// 2. 執行 sql
return SQLiteManager.sharedManager.execUpdate(sql) > 0
}
- ViewController中執行
private func demoDelete() {
let p = Person(dict: ["id": 2, "name": "張三", "age": 190, "height": 16])
if p.deletePerson() {
print("刪除成功")
} else {
print("刪除失敗")
}
}