iOS中数据库应用基础

iOS 数据库入门

一.数据库简介

1.什么是数据库?

  • 数据库(Database) 是按照数据结构来组织,存储和管理数据的仓库

  • 数据库可以分为2大种类

    • 关系型数据库(主流)
      • PC端
        • Oracle
        • MySQL
        • SQL Server
        • Access
        • DB2
        • Sybase
      • 嵌入式/移动客户端
        • SQLite
    • 对象型数据库
      • 以对象的形式进行存储,使用方便,维护简单,但目前还不成熟

2.iOS中数据存储的方式

  • Plist (NSArray / NSDictionary)
    • 特点: 只能存储系统自带的数据类型,如NSDictionary,NSArray等等,自定义的对象无法存储
  • Preference (偏好设置 / NSUserDefaults)
    • 特点: 本质就是一个Plist文件
  • NSCoding (NSKeyedArchiver / NSKeyedUnarchiver)
    • 特点: 可以存储自己定义的数据类型,但是都是一次性的全数据操作
  • SQLite3
    • 特点: 存储一些大批量的数据,排序,统计等操作
  • Core Data
    • 特点: 对SQLite3的一层面向对象的包装,本质还是要转换成对应的SQL语句去执行
  • 钥匙串
    • 特点: keychain是一个安全的加密的容器,用来为多个应用程序存储密码和提供安全服务
    • 在mac中,用户只需要输入一个主密码就可以访问到keychain里的所有内容
    • 在iOS中,不需要输入密码,一个App永远可以访问他自己的keychain items,但是不能访问到别的app的items
    • 对keychain操作的工具类github搜索: lockbox

3.什么是SQLite

  • SQLite是一款轻型的嵌入式数据库
  • 占用的资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了
  • 处理速度比MySQL,PostgreSQL这两款著名的数据库都还快

4.如何存储数据到数据库

  • 数据库的存储结构类似Excel,以表(table)为单位
  • 存储数据的步骤
    • 新建数据库文件
    • 新建一张表(table)
    • 添加多个字段(column, 列, 属性)
    • 添加多行记录(row, 每行存放多个字段对应的值)

二.Navicat安装

  • 软件简介
    • Navicat是一套适用于MySQL.SQLite等多个数据库系统的图形化数据库管理,报告以及监控的工具.具有高性能的,商业智能的,强大的备份功能
  • 使用目的
    • 通过该软件来演练创建/删除表, 查询/删除/修改记录操作的SQL语句
  • 打开软件出现文件已经损坏的解决方式
    • 打开系统偏好设置,找到安全性与隐私
    • 设置为任何来源

三.Navicat使用基础

  • 表格组成
    • 列(属性) : 标识这列应该存什么
    • 行(记录) : 存放的具体内容
  • 属性类型
    • blob : 二进制类型
    • integer : 整形
    • real : 浮点型
    • text : 文本类型
    • null : 空
  • 主键
    • 主键(Primary Key,简称PK) 用来唯一的标识某一条记录
    • 可以是一个字段或多个字段
    • 主键的设计原则
      • 应当是对用户没有意义的
      • 永远也不要更新主键
      • 主键不应该包含动态变化的数据
      • 主键应当由计算机自动生成

四.SQL语言简介

1.什么是SQL?

  • SQL(structured query language) : 结构化查询语言
  • SQL是一种对关系型数据库中的数据进行定义和操作的语言
  • SQL语言简洁,语法简单,好学好用

2.什么是SQL语句?

  • 使用SQL语言编写出来的句子/代码,就是SQL语句
  • 程序运行过程中,要想操作(增删改查,CRUD)数据库中的数据,必须使用SQL语句
  • CRUD : Create, Retrive, Update, Delete

3.SQL语句的特点?

  • 不区分大小写(如数据库认为user和UsEr是一样的)
  • 每条语句都必须以分号结尾

4.SQL常用的关键字

  • select / insert / update / from / create / where / desc / order / by / group / table / alter / view / index 等等
  • 数据库中不可以使用关键字来命名表,字段

5.SQL语句的种类

  • DDL (Data Definition Language) 数据定义语句
    • 包括creat和drop,Alert等操作
    • 在数据库中创建新表或删除表(create table 或 drop table)
  • DML (Data Manipulation Language) 数据操作语句
    • 包括insert, delete, update等操作
    • 上面的3种操作分别用于添加,删除,修改表中的数据
  • DQL (Data Query Language) 数据查询语句
    • 可以用于查询获得表中的数据
    • 关键字select是DQL(也是所有SQL)用的最多的操作
    • 其他DQL常用的关键字: where , order by, group by 和having

五.DDL语句

  • 创表

    // create table 表名(字段名1 字段类型1, 字段名2 字段类型2, ...);
    create table t_stu2 (id integer, name text, age integer, score real)
    
    • 实际上SQLite是无类型的

      • 就算声明为integer类型,还是能存储字符串文本(主键除外)
      • 建表时声明啥类型或者不声明类型都可以,也就意味着创表语句可以这么写: create table t_stu2 (id,name,age,score);
      • 但为了保持良好的编程规范,方便程序员之间的交流,编写建表语句时最好加上每个字段的具体类型
    • 语句优化

      • 创建表格时,最好加个表格是否已经存在的判断,这个防止语句多次执行时发生错误
      create table if not exists 表名 (字段名1 字段类型1, 字段名2 字段类型2, ...);
      
  • 删表

    // 格式: drop table 表名
    // 格式优化: drop table if exists 表名
    drop table if exists t_stu;
    
    • 格式优化: 最好加个判断,防止语句多次执行时发生错误
  • 修改表

    // 修改表名
    Alter table t_stu2 RENAME TO t_stu4;
    // 新增属性
    Alter table t_stu3 ADD COLUMN nana integer;
    
    • 注意: sqlite里面只能实现Alter Table的部分功能, 不能删除一列及修改一个已经存在的列名

六.约束

1.简单约束

  • 不能为空
not null: 规定字段的值不能为null
  • 不能重复
unique: 规定字段的值必须唯一
  • 默认值
default: 指定字段的默认值
  • 示例
// name字段不能为null,并且唯一
// age字段不能为null,并且默认为1
create table if not exists t_stu5(id integer, name text not null unique, age integer not null default 1);

2.主键约束

  • 添加主键约束的原因?

    • 区分数据, 防止造成数据库的记录不唯一,否则不方便管理数据
    • 良好的数据库编程规范应该要保证每条记录的唯一性,为此,增加了主键约束
    • 也就是说,每张表都必须有一个主键,用来标识记录的唯一性
  • 什么是主键?

    • 主键 (Primary Key,简称PK) 用来唯一地标识某一条记录
    • 可以是一个字段或多个字段
  • 主键的声明?

    • 创表的时候用primary key声明一个主键
    create table t_stu6(id integer primary key,name text, age integer);
    
    • 主键字段
      • 只要声明为primary key,就说明是一个主键字段
      • 默认包含了not null 和 unique两个约束
    • 如果想让主键自动增加,必须是integer类型,应该增加autoincrement
    create table t_stu7(id integer primary key autoincrement, name text, age integer);
    

七.DML语句

  • 插入数据 (insert)

    // 注意: 数据库中的字符串内容应该用单引号'括住
    insert into t_stu3 (name, age) values ('lisi', 20);
    
  • 更新数据 (update)

    // 注意: 会将t_stu3表中所有记录的name,age都更改
    update t_stu3 set name = 'wangwu', age = 100;
    
  • 删除数据 (delete)

    // 注意: 会将t_stu3表中所有记录都删掉
    delete from t_stu3;
    

八.条件语句

  • 作用
    • 如果只想更新 或者 删除某些固定的记录, 那就必须在DML语句后加上一些条件
  • 条件语句的常见格式
    • where 字段 = 某个值; (注意不能用 == )
    • where 字段 is 某个值; (is相当于 = )
    • where 字段 != 某个值;
    • where 字段 is not 某个值; (is not相当于 !=)
    • where 字段 > 某个值;
    • where 字段1 = 某个值 and 字段2 > 某个值; (and相当于 &&)
    • where 字段1 = 某个值 or 字段2 = 某个值; (or 相当于 ||)

九.DQL语句(查询)

  • 格式
    • select 字段1,字段2,... from 表名;
    • select * from 表名; // 查询所有

十.查询相关语句

  • 统计

    • count(X)
    • avg(X)
    • sum(X)
    • max(X)
    • min(X)
  • 排序

    • 查询出来的结果可以用order by进行排序
    select * from t_stu3 order by age;
    
    • 默认是按照升序排序,也可以变为降序
    // 降序
    select * from t_stu3 order by desc;
    // 升序
    select * from t_stu3 order by esc;
    
    • 也可以用多个字段进行排序
    // 先按照age排序(升序), 年龄相等就按照height排序(降序)
    select * from t_stu3 order by age asc, height desc;
    
  • limit分页

    • 使用limit可以精确的控制查询结果的数值 ,如每次只查询10条数据

    • 格式

      • select * from 表名 limit 数值1, 数值2;
    • 分页

      • limit常用来做分页查询, 如每页固定显示5条数据
      第1页 : limit 0,5
      第2页 : limit 5,5
      第3页 : limit 10,5
      第n页 : limit (n - 1) * 5, 5
      
    • 特殊案例

    // 相当于select * from t_stu3 limit 0,7
    // 表示取最前面的7条记录
    select * from t_stu3 limit 7;
    

十一.多表查询

  • 多表查询
select 字段1,字段2,... from 表名1, 表名2;
  • 别名
select
别名1.字段1 as 字段别名1,
别名2.字段2 as 字段别名2,
...
from
表名1 as 别名1,
表名2 as 别名2;

// 可以给表或者字段单独起别名, as可以省略
  • 表连接查询
select 字段1,字段2,...from 表名1,表名2 where 表名1.id = 表名2.id;
  • 外键

    • 如果表A的主关键字是表B中的字段,则该字段称为表B的外键
    • 保持数据一致性,完整性,主要目的是控制存储在外键表中的数据.使两张表形成关联,外键只能引用表中的列的值或使用空值

十二.代码实现 SQLite - DDL

  • 用swift创建一个工程
  • 添加系统框架 sqlite3.0.tbd
  • 建立桥接文件,导入头文件 #import "sqlite3.h"
    • 随便创建一个OC文件,点击桥接文件,然后把OC文件删除
  • 代码实现

1.创建数据库

// 参数1: 数据库文件位置
// 参数2: 指向数据库的指针(后期操作sql语句都要使用这个对象)
// 该方法功能: 创建并打开一个数据库,如果该数据库文件不存在则自动创建
// 数据库文件的后缀随便写,建议 db, db3, sqlite 规范
sqlite3_open(<#T##filename: UnsafePointer<Int8>!##UnsafePointer<Int8>!#>, <#T##ppDb: UnsafeMutablePointer<OpaquePointer?>!##UnsafeMutablePointer<OpaquePointer?>!#>)

2.另外两种创建数据库的方法,都不会自动创建数据库文件,只读(都不常用)
sqlite3_open16(<#T##filename: UnsafeRawPointer!##UnsafeRawPointer!#>, <#T##ppDb: UnsafeMutablePointer<OpaquePointer?>!##UnsafeMutablePointer<OpaquePointer?>!#>)
sqlite3_open_v2(<#T##filename: UnsafePointer<Int8>!##UnsafePointer<Int8>!#>, <#T##ppDb: UnsafeMutablePointer<OpaquePointer?>!##UnsafeMutablePointer<OpaquePointer?>!#>, <#T##flags: Int32##Int32#>, <#T##zVfs: UnsafePointer<Int8>!##UnsafePointer<Int8>!#>)

2.判断数据库是否打开成功

// 文件全路径
let path = "/Users/Maty/Desktop/demo/demo.sqlite"
// 指向数据库的指针
var db: OpaquePointer? = nil
if sqlite3_open(path, &db) != SQLITE_OK {
            print("打开数据库失败")
        } else {
            print("打开数据库成功")
        }

3.创表

  • 执行sql语句的函数
// 参数1: 一个已经打开的数据库对象
// 参数2: sql语句
// 参数3: 回调函数nil
// 参数4: 参数3中回调函数的参数1
// 参数5: 错误信息
sqlite3_exec(<#T##OpaquePointer!#>,
             <#T##sql: UnsafePointer<Int8>!##UnsafePointer<Int8>!#>, <#T##callback: ((UnsafeMutableRawPointer?, Int32, UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Int32)!##((UnsafeMutableRawPointer?, Int32, UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Int32)!##(UnsafeMutableRawPointer?, Int32, UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Int32#>, <#T##UnsafeMutableRawPointer!#>, <#T##errmsg: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!##UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!#>)
func creatTable() -> () {
        
        let sql = "create table if not exists t_stu (id integer primary key autoincrement, name text not null, age integer, score real default 60)"
        
        if sqlite3_exec(db, sql, nil, nil, nil) != SQLITE_OK {
            print("创表失败")
        }else {
            print("创表成功")
        }
    }

4.删表

func dropTable() -> () {
        
        let sql = "drop table if exists t_stu"
        if sqlite3_exec(db, sql, nil, nil, nil) != SQLITE_OK {
            print("删表失败")
        }else {
            print("删表成功")
        }

    }

5.封装SQLiteTool工具类

import UIKit

class SQLiteTool: NSObject {
    
    // 数据库对象
    var db: OpaquePointer? = nil
    // 单例
    static let shareInstance = SQLiteTool()
    
    override init() {
        
        super.init()
        
        let fullPath = "/Users/Maty/Desktop/demo/stu.sqlite"
        
        if sqlite3_open(fullPath, &db) != SQLITE_OK {
            print("打开数据库失败!")
        }else {
            print("打开数据库成功!")
        }
    }
    
    // 执行sql语句
    // 返回 创表成功还是失败
    func execute(sql: String) -> Bool {
        // 参数1:一个已经打开的数据库对象
        // 参数2:sql语句
        // 参数3:回调函数nil
        // 参数4:参数3中回调函数的参数1
        // 参数5:错误信息
        return sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK
    }
    
    // 创表
    func createTable() -> () {
        
        let sql = "create table if not exists t_stu(id integer primary key autoincrement, name text not null, age integer, score real default 60)"
        if execute(sql: sql) {
            print("创表成功!")
        }else {
            print("创表失败!")
        }
        
    }
    
    // 删表
    func dropTable() -> () {
        
        let sql = "drop table if exists t_stu"
        if execute(sql: sql) {
            print("删表成功!")
        }else {
            print("删表失败!")
        }
    }
    
}
  • 外界调用
 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 创表
//        SQLiteTool.shareInstance.createTable()
        // 删表
        SQLiteTool.shareInstance.dropTable()
    }

十三.代码实现SQLite - DML

1.创建一个Student类

  • 属性
  • 构造方法

2.创建数据库操作方法

  • 数据库中,对Student对象的操作封装

代码实现

import UIKit

class Student: NSObject {

    // 属性
    var name: String = ""
    var age: Int = 0
    var score: Double = 0.0
    
    // 构造方法
    init(name: String, age: Int, score: Double) {
        self.name = name
        self.age = age
        self.score = score
    }
    
    // 增
    func insert() -> () {
        // 写sql语句
        let sql = "insert into t_stu(name, age, score) values ('\(name)','\(age)','\(score)')"
        // 执行sql语句
        if SQLiteTool.shareInstance.execute(sql: sql) {
            print("插入数据成功!")
        } else {
            print("插入数据失败!")
        }
    }
    
    // 删
    func delete() -> () {
        let sql = "delete from t_stu where name = '\(name)'"
        if SQLiteTool.shareInstance.execute(sql: sql) {
            print("删除数据成功!")
        }else {
            print("删除数据失败!")
        }
    }
    
    // 改
    func update(newStu: Student) -> () {
        let sql = "update t_stu set name = '\(newStu.name)', age = '\(newStu.age)', score = '\(newStu.score)' where name = '\(name)'"
        if SQLiteTool.shareInstance.execute(sql: sql) {
            print("修改数据成功!")
        }else {
            print("修改数据失败!")
        }
    }
}

外界调用

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 创表
//        SQLiteTool.shareInstance.createTable()
        // 删表
//        SQLiteTool.shareInstance.dropTable()
        
        let stu = Student(name: "zhangsan", age: 18, score: 60)
        
        let newStu = Student(name: "lisi", age: 20, score: 70)
        
//        stu.insert()
        stu.update(newStu: newStu)
        
    }

3.Insert绑定参数

  • 准备语句

    • 准备语句(prepared statement)对象, 是一个代表一个简单SQL语句对象的实例,这个对象通常被称为"准备语句" 或者"编译好的SQL语句" 或者就直接称为"语句"
  • 代码

func insertBind() -> () {
        let sql = "insert into t_stu(name, age, score) values (?, ?, ?)"
        
        // 1.创建"准备语句"
        
        // 参数1:已经打开的数据库对象
        // 参数2:sql语句
        // 参数3:取出参数2的长度 -1 自动计算
        // 参数4:指向准备语句的指针
        // 参数5:取出参数2的参数3的长度的字符串剩余的字符串
        let db = SQLiteTool.shareInstance.db
        var stmt: OpaquePointer? = nil
        if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) != SQLITE_OK {
            print("创建准备语句失败!")
            return
        }
        
        // 2.绑定数据
        
        // 不同的类型绑定数据的函数也不同
        // 绑定name - 字符串
        // 参数1:准备语句
        // 参数2:索引,从1开始
        // 参数3:插入的值"字符串"
        // 参数4:取出参数3的长度的字符串插入到数据库中 -1代表自动计算
        // 参数5:对参数的处理方式
        
        // #define SQLITE_STATIC  处理方式:不会对参数做任何处理,认为该参数是一个static变量,不会释放
        // #define SQLITE_TRANSIENT  处理方式:会对参数做一次强引用,在合适的地方释放(一般使用这个)
        // 不安全的按位转换,注意:必须明确的知道这个类型能够转换的类型,否则会报错
        let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
        
        sqlite3_bind_text(stmt, 1, "lisi--2", -1, SQLITE_TRANSIENT)
        
        // age - int
        sqlite3_bind_int(stmt, 2, 18)
        
        // score - double
        sqlite3_bind_double(stmt, 3, 80)
        
        // 3.执行"准备语句"
        if sqlite3_step(stmt) == SQLITE_DONE {
            print("执行成功")
        }else {
            print("执行失败")
        }
        
        // 4.重置"准备语句"
        sqlite3_reset(stmt)
        
        // 5.销毁准备语句
        sqlite3_finalize(stmt)
    }

4.Insert插入数据优化

  • 测试方式

    • 第1种: 使用sqlite3_execu方案插入数据,循环插入10000条数据,查看耗时
    let beginTime = CFAbsoluteTimeGetCurrent()
        for _ in 0..<10000 {
            stu.insert()
        }
        let endTime = CFAbsoluteTimeGetCurrent()
        print(endTime - beginTime)
        
        // 结果耗时:6.65072101354599s
    
    • 第2种: 使用sqlite3_step准备语句 分解版的方案插入数据,循环插入10000条,查看耗时
    let beginTime = CFAbsoluteTimeGetCurrent()
        
        for _ in 0..<10000 {
            
            // 2.绑定数据
            let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
            sqlite3_bind_text(stmt, 1, "lisi--2", -1, SQLITE_TRANSIENT)
            
            // age - int
            sqlite3_bind_int(stmt, 2, 18)
            
            // score - double
            sqlite3_bind_double(stmt, 3, 80)
            
            // 3.执行"准备语句"
            if sqlite3_step(stmt) == SQLITE_DONE {
    

// print("执行成功")
}else {
print("执行失败")
}

        // 4.重置"准备语句"
        sqlite3_reset(stmt)
    }
    
    let endTime = CFAbsoluteTimeGetCurrent()
    print(endTime - beginTime)

```

- 查看耗时


```swift
let beginTime = CFAbsoluteTimeGetCurrent()
    for _ in 0..<10000 {

// stu.insert()
stu.insertBind()
}
let endTime = CFAbsoluteTimeGetCurrent()
print(endTime - beginTime)

    // 结果耗时:6.56768196821213
```
  • 如何插入数据

    • 方案: sqlite3_execu : 执行sql语句
    • 方案: sqlite3_step : 准备语句
  • 插入大批量数据的时候如何优化

    • 方案1: sqlite3_step : 准备语句 分解版

    • 方案2: 首先,sqlite3_execu 和sqlite3_step 函数内部都会开启一个事务,执行完毕之后会提交事务,开启/提交非常耗费时间

    • 所以,只要我们手动开启事务,执行完毕之后手动提交事务,函数内部就不会自动开启/提交事务

    • SQLiteTool工具类定义方法

    // 开启事务
    func beginTransaction() -> () {
        let sql = "begin transaction"
        execute(sql: sql)
    }
    
    // 提交事务
    func commitTransaction() -> () {
        let sql = "commit transcation"
        execute(sql: sql)
    }
    
    • 在Student类中开启和提交任务,开启任务一定要在插入数据之前
    // 开启事务
        SQLiteTool.shareInstance.beginTransaction()
    // 提交事务
        SQLiteTool.shareInstance.commitTransaction()
    
    • 结果耗时: 0.0229350328445435

5.事务

  • 事务(Transaction)是并发控制的单位,是用户定义的一个操作序列,这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务,可以将逻辑相关的一组操作绑定在一起,保持数据的完整性
  • 事务通常是以BEGIN TRANSACTION开始,以COMMIT TRANSACTION 或ROLLBACKTRANSACTION结束.
    • COMMIT 提交事务的所有操作,具体就是将事务中所有对数据库的更新写回到磁盘上的物理数据库中去,事务正常结束
    • ROLLBACK表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有yi
SQLiteTool.shareInstance.beginTransaction()
        let result1 = Student.updateMoney(sql: "update t_stu set score = score - 50 where name = 'zhangsan'")
        let result2 = Student.updateMoney(sql: "update t_stu set score = score + 50 where name = 'lisi'")
        
        if result1 && result2 {
            // 提交事务
            SQLiteTool.shareInstance.commitTransaction()
        }else {
            // 回滚事务
            SQLiteTool.shareInstance.rollBackTransaction()
        }

十四.代码实现SQLite - DQL

  • 方式1: sqlite3_exec

    • 作用: 可以通过回调来获取结果, 步骤相对来说简单,结果数据类型没有特定类型(id)
    // DQL查询语句
    class func queryAll() {
        let sql = "select * from t_stu"
        
        // 执行sql语句
        let db = SQLiteTool.shareInstance.db
        sqlite3_exec(db, sql, {(param, columnCount, values, columnNames) -> Int32 in
            
            // 遍历列
            let count = Int(columnCount)
            for i in 0..<count {
                // 获取列的名称
                let columnName = columnNames?[i]
                let columnNameStr = String(cString: columnName!)
                
                // 获取值
                let value = values?[i]
                let valueStr = String(cString: value!)
                print(columnNameStr, valueStr)
            }
            return 0
        },nil,nil)
        
    }
    
    • 外界直接调用方法查询
  • 方式2: 通过"准备语句"

    • 作用: 可以处理不同特定类型,步骤相对来说复杂
    class func queryALLBind() {
        
        // 写sql语句
        let sql = "select * from t_stu"
        
        // 1.创建"准备语句"
        // 参数1:一个已经打开的数据库
        // 参数2:sql语句
        // 参数3:取出参数3长度的参数2的字符串
        // 参数4:一个指向准备语句的指针
        // 参数5:取出参数3长度的参数2的字符串剩余的字符串
        let db = SQLiteTool.shareInstance.db
        var stmt : OpaquePointer? = nil
        if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) != SQLITE_OK {
            print("创建准备语句失败")
            return
        }
        
        // 2.绑定数据(可以忽略)
        
        // 3.执行"准备语句"
        // SQLITE_DONE 只适用于 删除 插入 和更新
        // 查询适用SQLITE_ROW
        // 每调用一次这个函数,会有一个指针指向该记录,当该记录有值则返回SQLITE_ROW,并把查询的数据放到stmt(准备语句)中
        while sqlite3_step(stmt) == SQLITE_ROW {
            
            // 1.获取总共有多少列
            let count = sqlite3_column_count(stmt)
            
            // 2.遍历列
            for i in 0..<count {
                // 3.取出列名
                let columnName = sqlite3_column_name(stmt, i)
                let columnNameStr = String(cString: columnName!)
                print(columnNameStr)
                
                // 4.取出值
                // 不同类型的值需要用不同的函数去获取不同的值
                let type = sqlite3_column_type(stmt, i)
                if type == SQLITE_TEXT {
                    let value = sqlite3_column_text(stmt, i)
                    let valueStr = String(cString: value!)
                    print(valueStr)
                }
                
                if type == SQLITE_INTEGER {
                    let value = sqlite3_column_int(stmt, i)
                    print(value)
                }
                
                if type == SQLITE_FLOAT {
                    let value = sqlite3_column_double(stmt, i)
                    print(value)
                }
                
            }
        }
        
        // 4.重置"准备语句"(可以省略)
        
        // 5.释放"准备语句"
        sqlite3_finalize(stmt)
        
    }
    

十五.SQLiteTool最终封装(添加事务的开启,提交,回滚方法)

import UIKit

class SQLiteTool: NSObject {
    
    // 指向数据库的指针
    var db: OpaquePointer? = nil
    // 单例
    static let shareInstance = SQLiteTool()
    
    override init() {
        
        super.init()
        
        let fullPath = "/Users/Maty/Desktop/demo/stu.sqlite"
        
        if sqlite3_open(fullPath, &db) != SQLITE_OK {
            print("打开数据库失败!")
        }else {
            print("打开数据库成功!")
        }
    }
    
    // 执行sql语句
    // 返回 创表成功还是失败
    func execute(sql: String) -> Bool {
        // 参数1:一个已经打开的数据库对象
        // 参数2:sql语句
        // 参数3:回调函数nil
        // 参数4:参数3中回调函数的参数1
        // 参数5:错误信息
        return sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK
        
        
    }
    
    // 创表
    func createTable() -> () {
        
        let sql = "create table if not exists t_stu(id integer primary key autoincrement, name text not null, age integer, score real default 60)"
        if execute(sql: sql) {
//            print("创表成功!")
        }else {
            print("创表失败!")
        }
        
    }
    
    // 删表
    func dropTable() -> () {
        
        let sql = "drop table if exists t_stu"
        if execute(sql: sql) {
//            print("删表成功!")
        }else {
            print("删表失败!")
        }
    }
    
    // 开启事务
    func beginTransaction() -> () {
        let sql = "begin transaction"
        execute(sql: sql)
    }
    
    // 提交事务
    func commitTransaction() -> () {
        let sql = "commit transcation"
        execute(sql: sql)
    }
    
    // 回滚事务
    func rollBackTransaction() -> () {
        let sql = "rollback transaction"
        execute(sql: sql)
    }
    
}

原文地址:https://www.cnblogs.com/mtystar/p/6052907.html