01,数据库简介
什么是数据库
- 数据库:Database 是按照数据结构来组织,存储和管理数据的仓库
- 分为2大种类
- 关系型数据库(主流)
- pc端:
- 嵌入式/移动客户端 : SQLite
- 对象型数据库
iOS中数据的存储方式
- plist(NSArray/NSDictionary)
- 只能存储系统自带的数据类型,自定义的对象无法储存
- preference(偏好设置/NSUserDefaults)
- 本质就是一个plist文件
- NSCoding(NSKeyedArchiver/NSKeyedUnarchiver)
- 可以存储自己定义的数据类型,但是都是一次性的全数据操作
- SQLite3
- 存储一些大批量的数据,排序,统计等操作
- Core Data
- 对SQLite3的一层面向对象的包装,本质还是转化为SQL语句去执行
什么是SQLite
- SQLite是一款轻量级的嵌入式数据库
- 占用资源非常低,在嵌入设备中,只需要几百K就行了
- 处理速度快
如何存储数据到数据库
- 数据库的存储结构和excle很像,以表(table)为单位
- 数据库存储数据的步骤:
- 新建数据库文件
- 新建一张表:table
- 添加多个字段:column 列 属性
- 添加多行记录:row 每行存放多个字段对应的值
02.Navicat软件
- 是一套适用于MySQL,SQLite等多个数据库系统的图形化数据库管理,报告以及箭筒的工具
- 通过软件可以演练创建/删除表,查询/删除/修改记录操作的SQL
03.软件的基本使用
- 表里面应该有个"主键",能标识唯一的一行
- 解决方案:重新编辑表结构,设置主键(自增长)
- 理论基础:
- 1.表格组成:行(记录)和列(属性)"属性" 是用来标识这一列应该存放什么
"记录" 是用来存放一条数据 - 2.属性类型:
- blob:二进制类型
- integer:整型
- real:浮点型
- text:文本类型
- null:空类型
- 3.主键:
- 主键(primary key /PK)用来位移标识某一条记录
- 主键可以使一个字段或者多个字段:行和列
- 设计原则:1>应当对用户没有意义,2>永远也不要更新主键3>主键不应包含动态变化的数据4>主键应当有计算机自动生成
04.SQL语言简介
什么是SQL?
- SQL(structured query language):结构化查询语言
- SQL是一种对关系性数据苦衷的数据进行定义和操作的语言
SQL语句
- 使用SQL语言编写出来的句子/代码就是SQL语言
- 在程序中如果想要操作(增删改查 CRUD)数据库中的数据,就必须使用SQL语言
- Create ,Retrive,Updata,Delete
SQL语句的特点
- 不区分大小写
- 每条语句以;结尾
SQL中常用的关键字
SQL语句的种类
1.数据定义语句(DDL : Data Definition Language)
- 包括create,drop,Alert等操作
- 在数据库中创建新表或者删除表(create table 和 drop table)
2.数据操作语句(DML : Data Manipulation Language)
- 包括insert.,delete,updata等操作
- 上面的3种操作分别用于添加,修改,删除表中的数据
3.数据查询语句(DQL : Data Query Language)
- 可以用于查询获得表中的数据
- 关键字select是DQL用的最多的操作
- 其他DQL常用的关键字where,order by,group by 和 having
05.DDL语句
创建表
- 格式: create table 表名 (字段名1 字段类型1 ....);
- 示例: create table t_stu(id integer,name text,age integer,score real);
- 经验:
- 实际上SQLite是无类型的
- 就算申明为某种类型,但是还是可以存储其他类型的(主键除外)
- 但是为了保证编程规范,方便交流,最好加上类型
- 语句优化:
- 创建表格时,最好加上判断这个表格是否存在,防止语句多次执行发生错误
- create table if not exists 表名(字段名1 字段类型1 ....)
删除表
- 格式: drop table 表名; drop table if exists 表名;
- 示例: drop table t_stu;
- 语句优化:最好加个判断,防止语句多次执行发生错误
- drop table if exists 表名;
修改表
- 注意: SQLite里面只能实现Alter Table的部分功能,不能删除一列,修改一个已经存在的列名
- 修改表名:ALTER TABLE 旧表名 REMANE TO 新表名
- 新增属性:ALTER TABLE 表名 ADD COLUMN 列名 数据类型 限定符
06.约束
简单约束
- 不能为空: not null
- 不能重复:uinque
- 默认值:default
- 示例:create table t_stu (id integer ,name text not null unique,age integer not null default 1);
主键约束
- 添加主键约束的原因:
- 如果t_stu表中就name和age连个字段,而且有些记录的name和age字段的值一样,没办法区分这些数据,造成数据录的记录不唯一,不方便管理数据,应该保证每条记录的唯一性,所以增加了主键约束
- 每张表都必须有一个主键,用来标示记录的唯一性
- 主键:Primary Key :用来唯一的标示某一条记录
- 示例:creat table t_stu (id integer primary key autoincrement,name text);
07.DML语句
插入语句:insert
- 格式:insert into 表名(字段1,字段2...) value(字段1值,字段2值...);
- 示例:insert into t_stu(name,age) value('liuwei',19);
- 注意:数据库中的字符串内容应该用单引号括住
更新语句:update
- 格式:update 表名 set 字段1 = 字段1的值,字段2 = 字段2的值,...;
- 示例:update t_stu set name = 'wen',age = 20;
- 注意:示例会将t_stu中的所有的记录中的那么都改为wen,age都改为20
删除语句:delete
- 格式:delete from 表名;
- 示例:delete from t_stu;
- 注意:示例会将t_stu表中所有的记录都删除
08.条件语句
作用
- 如果只想更新或者删除某些固定的记录,那就必须在DML语句加上一些条件
常见格式
- where 字段 = 某个值;//不能使用两个==
- where 字段 is 某个值;//is 相当于 =
- where 字段 != 某个值 ;
- where 字段 is not 某个值 ; // is not 相当于 !=
- where 字段 > 某个值 ;
- where 字段1 = 某个值 and 字段2 > 某个值 ; // and相当于C语言中的 &&
- where 字段1 = 某个值 or 字段2 = 某个值 ; // or 相当于C语言中的 ||
小试牛刀
- 将t_student表中年龄大于10 并且 姓名不等于wex的记录,年龄都改为 5
- update t_student set age = 5 where age > 10 and name != 'wex';
- 删除t_student表中年龄小于等于10 或者 年龄大于30的记录
- delete from t_student where age <= 10 or age > 30;
- update t_student set score = age where name = ‘wex’ ;
- 将name = 'wex'的人的score的值改为age的值
09DQL
格式
- select 字段1,字段2,...from 表名;
- select * from 表名;//查询所有的字段
- 示例:
- select name, age from t_student ;
- select * from t_student ;
- select * from t_student where age > 10 ; // 条件查询
10查询相关语句
统计
- count(X)
- select count(*) from t_student //计算所有记录个数
- select count(age) from t_student //计算age有值的记录个数(Null不计算在内)
- avg(X)
- 计算某个字段的平均值
- sum(X)
- 计算某个字段的总和
- max(X)
- 计算某个字段的最大值
- min(X)
- 计算某个字段的最小值
排序
- 查询出来的结果可以用order by进行排序
- select 字段1, 字段2 from 表名 order by 字段 ;
- select * from t_student order by age ;
- 默认是按照升序排序(由小到大),也可以变为降序(由大到小)
- select * from t_student order by age desc ; //降序
- select * from t_student order by age asc ; // 升序(默认)
- 也可以用多个字段进行排序
- select * from t_student order by age asc, height desc ;
- 先按照年龄排序(升序),年龄相等就按照身高排序(降序)
limit分页
- 使用limit可以精确地控制查询结果的数量,比如每次只查询10条数据
- 格式:select * from 表名 limit 数值1, 数值2 ;
- 示例:select * from t_student limit 4, 8 ;//可以理解为:跳过最前面4条语句,然后取8条记录
- 分页:
- limit常用来做分页查询,比如每页固定显示5条数据,那么应该这样取数据
- 第1页:limit 0, 5 第2页:limit 5, 5 第3页:limit 10, 5 第n页:limit 5*(n-1), 5
- 特殊案例
- select * from t_student limit 7 ;
- 相当于select * from t_student limit 0, 7 ;
- 表示取最前面的7条记录
11多表查询
多表查询
- 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的外键
- 保持数据一致性,完整性,主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值或使用空值。
12-代码实现SQLite-DDL
- 导入框架 导入系统框架sqlite3.tbd(sqlite3.dylib)
- 建立桥接文件, 导入头文件sqlite3.h
- 代码实现:
-
- 打开数据库
-
- 使用打开的数据库, 执行DDL语句, 创建一个数据库表
-
- 将数据库操作封装成一个工具类
class SQLiteTool: NSObject {
static let shareInstance = SQLiteTool()
var db: COpaquePointer = nil
override init() {
super.init() // 1. 打开数据库 // 参数1: 数据库路径 // 参数2: 一个打开的数据库的指针(后期操作所有的sql语句, 都是使用这个对象) // 根据一个路径, 打开一个数据库(如果路径不存在, 则创建一个数据库, 如果存在, 则直接打开) // 关于sqlite数据库的后缀名, 没有一个明确的要求, 随便写 // db, db3, sqlite let path = "/Users/xiaomage/Desktop/db/demo.sqlite" if sqlite3_open(path, &db) != SQLITE_OK { print("打开数据库失败") }else { print("打开数据库成功") createTable() } } func dropTable() -> () { // 理论基础: 如果想要操作表或者操作记录, 统一都是分两个步骤 // 步骤1: 创建sql语句(因为我们操作数据库, 都必须使用sql语句才能操作) // 步骤2: 执行sql语句 let sql = "drop table if exists t_stu" if excuteSql(sql) { print("执行成功") } } func createTable() -> () { // 1. 写sql语句 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 excuteSql(sql) { print("执行成功") } } func excuteSql(sql: String) -> Bool { // 2. 执行sql语句 // 功能, 执行sql字符串 // 参数1: 一个已经打开的数据库(代表操作的是哪一个数据库) // 参数2: sql语句 // 参数3: 回调函数 nil // 参数4: 参数3: 回调函数里面的参数1 nil // 参数5: 错误信息 nil return sqlite3_exec(db, sql, nil , nil , nil) == SQLITE_OK }
13-代码实现DML语句-Insert
class Student: NSObject {
var name : String = ""
var age : Int = 0
var score : Double = 0.0
override init() {
super.init()
}
init (name : String , age : Int, score : Double){
self.name = name
self.age = age
self.score = score
}
func insertStu() -> () {
let sql = "insert into t_stu (name ,age ,score) values ('\(name)',\(age),\(score))" if SQLiteTool.shareInstance.excuteSql(sql) { print("插入成功") }else{ print("插入失败") } } class func deletStu(name : String) -> () { let sql = "delete from t_stu where name = '\(name)'" if SQLiteTool.shareInstance.excuteSql(sql) { print("删除成功") }else{ print("删除失败") } } func updateStu (newStu : Student) -> () { let sql = "update t_stu set name = '\(newStu.name)',age = \(newStu.age),score = \(newStu.score) where name = '\(name)'" if SQLiteTool.shareInstance.excuteSql(sql) { print("更改成功") }else{ print("更改失败") }
14-代码实现DML语句-Insert绑定参数
准备语句(prepared statement)对象
- 准备语句(prepared statement)对象一个代表一个简单SQL语句对象的实例,这个对象通常被称为“准备语句”或者“编译好的SQL语句”或者就直接称为“语句”。
操作历程
-
- 使用sqlite3_prepare_v2或相关的函数创建这个对象
-
- 使用sqlite3_bind_*()给宿主参数(host parameters)绑定值
-
- 通过调用sqlite3_step() 一次或多次来执行这个sql
-
- 使用sqlite3_reset()重置这个语句,然后回到第2步,这个过程做0次或多次
-
- 使用sqlite3_finalize()销毁这个对象, 防止内存泄露
func insertBind() -> () {
let sql = "insert into t_stu(name, age, score) values (?, ?, ?);"
// 1. 创建"准备语句"
// 参数1: 一个打开的数据库
// 参数2: sql字符串 // 参数3: 取出参数2 的长度 3 -1 代表自动计算 // 参数4: 指向"准备语句的指针" // 参数5: 通过参数3指定的长度, 取出参数2, 字符串之后, 剩余的字符串 nil let db = SQLiteTool.shareInstance.db var stmt: COpaquePointer = nil if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) != SQLITE_OK { print("准备语句创建失败") return } // 2. 绑定数据 // 不同的数据类型, 绑定的方法不一样 // 绑定姓名- 字符串 // 参数1: 准备语句 // 参数2: 绑定的索引 从1开始 // 参数3: 绑定的值 // 参数4: 代表, 值取出的长度 -1 自动计算 // 参数5: 指向函数的指针: 参数的处理方式 // #define SQLITE_STATIC 处理方式: 不对参数做任何操作(认为参数是一个static) // #define SQLITE_TRANSIENT 处理方式: 会对参数, 做一个强引用, 然后, 使用完毕之后, 在合适的时候释放 // 使用这个函数一定要注意: 需要明确的知道最终的确切类型是什么 let SQLITE_TRANSIENT = unsafeBitCast(-1, sqlite3_destructor_type.self) sqlite3_bind_text(stmt, 1, "lisi---3", -1, SQLITE_TRANSIENT) // 绑定年龄 Int sqlite3_bind_int(stmt, 2, 18) // 绑定分数 - Double sqlite3_bind_double(stmt, 3, 99) // 3. 执行"准备语句" if sqlite3_step(stmt) == SQLITE_DONE { print("执行成功") }else { print("执行失败") } // 4. 重置"准备语句" sqlite3_reset(stmt) // 5. 释放资源 sqlite3_finalize(stmt) } }
15-DML语句-Insert插入数据优化
-
测试结果分析
-
sqlite_exec 直接执行 和 未拆解"准备语句" 执行 执行时间平均差不多
-
拆解后的"准备语句" 执行, 效率明显高了一些
-
虽然按步骤使用"准备语句", 但是执行效率,依然非常低, 达到了5秒左右, 不可原谅
- 原因分析:每当SQL调用执行方法执行一个语句时, 都会开启一个叫做"事务"的东西, 执行完毕之后再提交"事务";
也就是说, 如果执行了10000次SQL语句, 就打开和提交了10000次"事务", 所以造成耗时严重 - 解决方案:只要在执行多个SQL语句之前, 手动开启事务, 在执行完毕之后, 手动提交事务, 这样 再调用SQL方法执行语句时, 就不会再自动开启和提交事务
- 原因分析:每当SQL调用执行方法执行一个语句时, 都会开启一个叫做"事务"的东西, 执行完毕之后再提交"事务";
-
结论
-
- 如果插入大量数据, 请务必手动开启/提交事务
-
- 根据不同情况, 选择使用sqlite3_exec 或者 "准备语句"
- 单条语句, 前者, 因为使用简单
- 大批量插入, 选择后者
// 优化方案:
// 如果大批量插入数据
// 方案: sqlite3_step() 准备语句
// 方案: sqlite3_exec() sql字符串 // 优化方案1: sqlite3_step() 准备语句 分解版 // 优化方案2: // sqlite3_step() sqlite3_exec() 这两个函数执行, 内部都会自动开启一个事务, 然后执行完毕之后, 提交事务 , 开启/提交, 整个过程非常耗费时间 // 只要我们手动的开启事务, 手动的提交事务, 那么在函数内部, 就不会自动开启和提交事务
- 工具类中,添加了两个方法,用来手动开启事务和提交事务
func beginTransaction() -> () {
let sql = "begin transaction"
excuteSql(sql)
}
func commitTransaction() -> () {
let sql = "commit transaction"
excuteSql(sql)
}
- 在执行绑定数据前手动开启事务
SQLiteTool.shareInstance.beginTransaction()
for _ in 0..<10000 {
- 在重置准备语句后手动关闭事务
// 4. 重置"准备语句"
sqlite3_reset(stmt)
}
SQLiteTool.shareInstance.commitTransaction()
16-代码实现-事务
概念
- 事务(Transaction)是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,可以将逻辑相关的一组操作绑定在一起,保持数据的完整性。
- 事务通常是以BEGIN TRANSACTION开始,以COMMIT TRANSACTION或ROLLBACK TRANSACTION结束。
小试牛刀
- 在工具类方法中添加一个rollbackTransaction的方法
func rollBackTransaction() -> () {
let sql = "rollback transaction"
excuteSql(sql)
}
- 在学生类中增加一个更新分数的方法
class func updateScore(sql : String) -> Bool { return SQLiteTool.shareInstance.excuteSql(sql) }
- 在点击屏幕时做操作,当两个操作都完成时才会commit,否则rollback
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
SQLiteTool.shareInstance.beginTransaction()
let result1 = Student.updateScore("update t_stu set score = score - 9 where name = 'liuwei'")
let result2 = Student.updateScore("update t_stu set score = score + 9 where name = 'wenxiaoli'") if result1 && result2 { SQLiteTool.shareInstance.commitTransaction() }else{ SQLiteTool.shareInstance.rollbackTransaction() } }
17-代码实现DQL语句
方式1: sqlite3_exec
- 作用: 可以通过回调来获取结果, 步骤相对来说简单, 结果数据类型没有特定类型(id)
class func queryAll() -> () { let sql = "select * from t_stu" // 参数1: 一个已经打开的数据库 // 参数2: sql字符串 // 参数3: 回调 (一般存放查询的结果集) // 参数1: 参数4的值 // 参数2: 列的个数 // 参数3: 值(字符串)的数组 // 参数4: 列名(字符串)组成的数组 // 返回值: 0 继续查询, 非零, 终止查询 // 参数4: 参数3的参数1 // 参数5: 错误信息 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 columName = columnNames[i] let columnNameStr = String(CString: columName, encoding: NSUTF8StringEncoding) let value = values[i] let valueStr = String(CString: value, encoding: NSUTF8StringEncoding) print(columnNameStr, valueStr) } return 0 }, nil, nil) }
方式2: 通过"准备语句"
- 作用: 可以处理不同特定类型, 步骤相对来说复杂
- 步骤:
-
- 预处理函数 获取"准备语句"
-
- 不断执行"准备语句", 直到无结果集
-
- 获取列的类型
-
- 根据每列的类型取出不同的值
-
- 释放资源
class func queryAllStmt() -> () { let sql = "select * from t_stu" // 准备语句操作流程 // 1. 创建"准备语句" let db = SQLiteTool.shareInstance.db var stmt: COpaquePointer = nil if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) != SQLITE_OK { print("预处理失败") return } // 2. 绑定数据(这一步可以省略) // 3. 执行SQLITE_DONE 适用于删除, 或者更新, 或者插入 // 查询不是使用这个标志 // 有一个指针, 指向当前的记录, 当没执行一次,这个方法, 都会查询下一条还有没有数据, 如果有, 返回值是SQLITE_ROW, 并且, 把查询的结果放到"准备语句里面", 所以, 如果想要获取一行里面的数据, 应该从"准备语句里面获取" while sqlite3_step(stmt) == SQLITE_ROW { // 遍历一行的每一列 // 1. 获取总共有多少列 let count = sqlite3_column_count(stmt) // 2. 遍历每一列 for i in 0..<count { // 2.1 获取列的名称 // 参数1: 准备语句 // 参数2: 索引 // UnsafePointer<Int8> let columnName = sqlite3_column_name(stmt, i) let columnNameStr = String(CString: columnName, encoding: NSUTF8StringEncoding) print(columnNameStr) // 2.2 获取列的数值 // 不同的数据类型, 应该使用不同的函数进行获取 // 2.2.1 获取列的类型 let type = sqlite3_column_type(stmt, i) // 2.2.2 根据不同的类型, 使用不同的函数, 进行获取响应的值 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) } if type == SQLITE_TEXT { // UnsafePointer<UInt8> let value = UnsafePointer<CChar>(sqlite3_column_text(stmt, i)) // UnsafePointer<CChar> let valueStr = String(CString: value, encoding: NSUTF8StringEncoding) print(valueStr) } } } // 4. 重置(这一步可以省略) // 5. 释放资源 }
18-FMDB基本使用
1. 什么是FMDB?
- FMDB是iOS平台的SQLite数据库框架
- FMDB以OC的方式封装了SQLite的C语言API
2. FMDB有什么优势?
- 使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
- 提供了多线程安全的数据库操作方法,有效地防止数据混乱
3. 安装方式
- Cocoapods
- 手动集成(swift)
-
- 导入FMDB文件
-
- 导入系统依赖库sqlite3.0.tbd
-
- 建立桥接文件, 并导入需要的头文件
核心类
-
FMDatabase
-
一个FMDatabase对象就代表一个单独的SQLite数据库
-
用来执行SQL语句
-
FMResultSet
-
使用FMDatabase执行查询后的结果集
-
FMDatabaseQueue
-
用于在多线程中执行多个查询或更新,它是线程安全的
使用步骤
-
- 打开数据库
- 通过指定SQLite数据库文件路径来创建FMDatabase对象
- 文件路径有三种情况
- 具体文件路径:如果不存在会自动创建
- 空字符串@"":会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除
- nil:会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁
- FMDatabase *db = [FMDatabase databaseWithPath:path];
- if ([db open]) { NSLog(@"打开成功!"); }
static let shareInstance = FMDBTool()
lazy var db: FMDatabase = {
// 1. 创建或者打开一个数据库 let path = "/Users/xiaomage/Desktop/fmdb/demo.sqlite" let db = FMDatabase(path: path) return db }() override init() { super.init() if db.open() { print("数据库打开成功") } }
-
- 执行更新
- 在FMDB中,除查询以外的所有操作,都称为“更新”:create、drop、insert、update、delete等
- 使用executeUpdate:方法执行更新:
-
- (BOOL)executeUpdate:(NSString*)sql, ...
-
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
-
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
-
- 示例:[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]
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 db.executeUpdate(sql, withArgumentsInArray: nil) {
print("创建表成功") }else { print("失败") } } func dropTable() -> () { let sql = "drop table if exists t_stu" if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } func insertStu() -> () { let sql = "insert into t_stu(name, age, score) values ('zhangsan', 18, 99)" if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } func deleteStu() -> () { let sql = "delete from t_stu where name = 'zhangsan'" if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } func updateStu() -> () { let sql = "update t_stu set age = 999 where name = 'zhangsan'" if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } }
-
- 执行查询
- 查询方法:
-
- (FMResultSet )executeQuery:(NSString)sql, ...
-
- (FMResultSet )executeQueryWithFormat:(NSString)format, ...
-
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
-
func queryAll() -> () {
let sql = "select * from t_stu"
let resultSet = db.executeQuery(sql, withArgumentsInArray: nil)
while resultSet.next() { let age = resultSet.intForColumn("age") let name = resultSet.stringForColumn("name") let score = resultSet.doubleForColumn("score") print(age, name, score) } }
-
- 关闭数据库
- database.close()
19-FMDabaseQueue
- FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题
- 为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类
- FMDatabaseQueue的创建:FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
static let shareInstance = FMDBTool()
lazy var queue: FMDatabaseQueue = {
let path = "/Users/xiaomage/Desktop/fmdb/demo.sqlite"
let queue = FMDatabaseQueue(path: path)
return queue }() // lazy var db: FMDatabase = { // // 1. 创建或者打开一个数据库 // let path = "/Users/xiaomage/Desktop/fmdb/demo.sqlite" // let db = FMDatabase(path: path) // return db // }() // override init() { super.init() // if db.open() { // print("数据库打开成功") // } } 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)" queue.inDatabase { (db: FMDatabase!) in if db.executeUpdate(sql, withArgumentsInArray: nil) { print("创建表成功") }else { print("失败") } } } func dropTable() -> () { let sql = "drop table if exists t_stu" queue.inDatabase { (db: FMDatabase!) in if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } } func insertStu() -> () { let sql = "insert into t_stu(name, age, score) values ('zhangsan', 18, 99)" queue.inDatabase { (db: FMDatabase!) in if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } } func deleteStu() -> () { let sql = "delete from t_stu where name = 'zhangsan'" queue.inDatabase { (db: FMDatabase!) in if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } } func updateStu() -> () { let sql = "update t_stu set age = 999 where name = 'zhangsan'" queue.inDatabase { (db: FMDatabase!) in if db.executeUpdate(sql, withArgumentsInArray: nil) { print("成功") }else { print("失败") } } } func queryAll() -> () { let sql = "select * from t_stu" queue.inDatabase { (db: FMDatabase!) in let resultSet = db.executeQuery(sql, withArgumentsInArray: nil) while resultSet.next() { let age = resultSet.intForColumn("age") let name = resultSet.stringForColumn("name") let score = resultSet.doubleForColumn("score") print(age, name, score) } } } func transactionTest() -> () { queue.inTransaction { (db: FMDatabase!, rollback) in let sql = "update t_stu set score = score + 10 where name = 'zhangsan'" let sql2 = "update t_stu set score2 = score - 10 where name = 'lisi'" let result1 = db.executeUpdate(sql, withArgumentsInArray: nil) let result2 = db.executeUpdate(sql2, withArgumentsInArray: nil) if result1 && result2 { }else { // 回滚 *rollback = rollback.memory rollback.memory = true } } } func stamentsTest() -> () { let sql = "insert into t_stu(name, age, score) values ('zhangsan3', 18, 99);insert into t_stu(name, age, score) values ('zhangsan2', 18, 99);" queue.inDatabase { (db) in db.executeStatements(sql) } }
作者:天润
链接:http://www.xingzuji.com/
來源:行足迹旅游
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。