基于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()
}
func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// `持久化`连接
// 在开发本地数据库的时候,通常只会打开数据库,不会关闭数据库
// 一旦打开之后,后续只需要做读写操作即可,效率更高!
// 分享: 对应的网络有长连接,但是做网络开发,数据库访问结束后,是必须关闭,并且断开连接(切断的目的: 把服务器的链接资源释放,保证一台服务器为更多地用户响应)!
sqliteManager.sharedManager.openDB("my.db")
return true
}
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) == 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 更好阅读!
-- 创建个人数据表
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: 插入数据
/// 执行 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类中开始进行数据库操作
@H_289_403@/// 将当前对象插入到数据库
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类中开始进行数据库操作
@H_289_403@/// 使用当前对象的信息更新数据库
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,"height": 16])
if p.deletePerson() {
print("删除成功")
} else {
print("删除失败")
}
}