1. FMDB github 地址: FMDB
2. 使用 CocoaPods 导入框架 pod init
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'FMDB' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for FMDB
pod 'FMDB'
end
3. 命令行指令 pod install --no-repo-update
4. 创建封装类 SQLiteManager.swift
import Foundation
import FMDB
//数据库名称
private let dbName = "my.db"
class SQLiteManager{
//单例
static let shareManager = SQLiteManager()
let queue: FMDatabaseQueue?
init(){
//0.数据库路径 - 全路径 (可读可写)
var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last!
path = (path as NSString).appendingPathComponent(dbName)
print(path)
//1.打开数据库队列
//如果数据不存在,会建立数据库,然后,再创建队列
//如果数据库存在,会直接创建队列并且打开数据库
queue = FMDatabaseQueue(path: path)
createTable()
}
//创建数据表
private func createTable(){
//1.准备 SQL (只读,创建应用程序时,准备的素材)
let path = Bundle.main.path(forResource: "db.sql", ofType: nil)!
let sql = try! String(contentsOfFile: path)
//2.执行 SQL
queue?.inDatabase({ (db) in
//executeStatements 建立多个表 executeUpdate 建立单个表
if db.executeStatements(sql) {
print("创表成功")
}else{
print("创表失败")
}
})
}
/// 执行 SQL 返回字典数组
/// - Parameter sql: sql
/// - Returns: 字典数组
func execRecordSet(sql: String)-> [[String: Any]]? {
//定义结果[字典数组]
var result = [[String: Any]]()
//2. 执行 SQL
//执行数据库操作 '同步'执行数据库查询 - FMDB 默认情况下,都是在主线程上执行的
SQLiteManager.shareManager.queue?.inDatabase({ db in
print(Thread.current)
guard let rs = db.executeQuery(sql, withArgumentsIn: []) else{
print("没有结果")
return
}
while rs.next(){
//1.列数
let colCount = rs.columnCount
//创建字典
var dict = [String: Any]()
//2.遍历每一列
for col in 0..<colCount{
//1.列名
let name = rs.columnName(for: col)!
//2.值
let value = rs.object(forColumn: name)
//print("name \(name) -- value \(value)")
//设置字典
dict[name] = value
}
//print(dict)
result.append(dict)
}
//print(result)
})
//返回结果
return result
}
}
5. 创建指令文件 db.sql
-- 创建个人表 --
CREATE TABLE IF NOT EXISTS "T_Person" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"age" INTEGER,
"height" NUMBER
);
-- 创建图书表 --
CREATE TABLE IF NOT EXISTS "T_Book" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"bookName" TEXT
);
6. 测试方法
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//使用 executeStatements 出现问题测试
//demoInsert2(name: "王小', 0 , 0); DELETE FROM T_Person; -- ")
//demoInsert4(dict: ["name":"李四","age":18,"height":1.9])
//demoUpdate(dict: ["id": 10, "name": "老九", "age": 19, "height": 1.9])
// demoDelete(id: 6)
//person2()
// person3()
manyPerson()
}
//MARK: - 事务处理
func manyPerson(){
//1.准备 SQL
let sql = "INSERT INTO T_Person (name, age, height) VALUES (:name, :age, :height);"
let start = CACurrentMediaTime()
//2.执行 SQL
// OC 写法 *stop = YES
//1.Swift 写法 rollback.pointee = true
//2.FMDB的执行都是同步的!绝大多数开发时,不需要使用 block 回调
//3.所有大数据的插入,都应该使用事务!
SQLiteManager.shareManager.queue?.inTransaction({ db, rollback in
for i in 0..<20000 {
let name = "张三\(i)"
let age = 20 + Int.random(in: 0...10)
let height = String(1.5 + Float(Int.random(in: 0...4))/10)
let dict:[String: Any] = ["name":name, "age":age, "height":height]
if !db.executeUpdate(sql, withParameterDictionary: dict){
//回滚
rollback.pointee = true
break
}
//模拟失败
if i == 1000 {
rollback.pointee = true
break
}
}
})
print("完成 \(CACurrentMediaTime() - start)")
}
//MARK: 数据查询
func person3(){
//使用 FMDB 千万不要嵌套使用,否则一定挂掉
DispatchQueue.global().async {
//1.准备 SQL
let sql = "SELECT name, age, height FROM T_Person;"
let array = SQLiteManager.shareManager.execRecordSet(sql: sql)
print(array)
}
}
func person2(){
//1.准备 SQL
let sql = "SELECT name, age, height FROM T_Person;"
//2. 执行 SQL
SQLiteManager.shareManager.queue?.inDatabase({ db in
guard let rs = db.executeQuery(sql, withArgumentsIn: []) else{
print("没有结果")
return
}
var result = [[String: Any]]()
while rs.next(){
//1.列数
let colCount = rs.columnCount
//创建字典
var dict = [String: Any]()
//2.遍历每一列
for col in 0..<colCount{
//1.列名
let name = rs.columnName(for: col)!
//2.值
let value = rs.object(forColumn: name)
//print("name \(name) -- value \(value)")
//设置字典
dict[name] = value
}
//print(dict)
result.append(dict)
}
print(result)
})
}
func person1(){
//1.准备 SQL
let sql = "SELECT id, name, age, height FROM T_Person;"
//2.执行 SQL
SQLiteManager.shareManager.queue?.inDatabase({ db in
guard let rs = try? db.executeQuery(sql, values: nil) else{
print("没有结果")
return
}
//逐行便利所有的数据结果 next 表示还有下一行
while rs.next(){
//使用的函数名称 取决于,需要的“返回值”
let id = rs.int(forColumn: "id")
let name = rs.string(forColumn: "name")
let age = rs.int(forColumn: "age")
let height = rs.double(forColumn: "height")
print("\(id) _ \(name) _ \(age) _ \(height)")
}
})
}
//MARK: - 基本数操作
//删除操作
func demoDelete(id: Int){
//1.准备 SQL
let sql = "DELETE FROM T_Person WHERE id=:id;"
//2.执行 SQL
SQLiteManager.shareManager.queue?.inDatabase({ db in
// do{
// try db.executeUpdate(sql, values: [id])
// print("删除成功")
// } catch{
// print("删除失败")
// }
if db.executeUpdate(sql, withArgumentsIn: [id]){
if db.changes > 0 {
print("删除成功")
}else{
print("删除失败")
}
}else{
print("执行失败")
}
//db.executeUpdate(sql, withArgumentsIn: [id])
})
print("OK")
}
//更新操作
func demoUpdate(dict:[String: Any]){
//1.准备 SQL
let sql = "UPDATE T_Person set name = :name, age = :age, height = :height \n" +
" WHERE id = :id;"
SQLiteManager.shareManager.queue?.inDatabase({ db in
if db.executeUpdate(sql, withParameterDictionary: dict){
if db.changes > 0{
print("修改成功")
}else{
print("修改失败")
}
}else{
print("执行失败")
}
})
print("OK")
}
func demoInsert4(dict: [String: Any]){
/**
FMDB 的特殊语法
:key -> 与字典中的 key 相对应
*/
let sql = "INSERT INTO T_Person (name, age, height) VALUES (:name, :age, :height);"
SQLiteManager.shareManager.queue?.inDatabase({ db in
if db.executeUpdate(sql, withParameterDictionary: dict) {
print("执行 成功 自增长id - \(db.lastInsertRowId)")
}else{
print("插入失败")
}
})
print("OK")
}
//由于注入的原因,现在开发中,数据操作时,通常会使用绑定参数的形式
func demoInsert3(){
//1.SQL
/**
? 表示占位符号
- SQLite 首先编译 SQL,再执行的时候,动态绑定数据,同样可以避免 “注入”
- 使用占位符操作,不需要单引号
*/
let sql = "INSERT INTO T_Person (name, age, height) VALUES (?, ?, ?);"
SQLiteManager.shareManager.queue?.inDatabase({ db in
do{
try db.executeUpdate(sql, values: ["王小', 0 , 0); DELETE FROM T_Person; --", 28, 66])
print("执行 成功 自增长id - \(db.lastInsertRowId)")
} catch{
print("插入失败")
}
})
print("OK")
}
//SQL "注入" 演示
func demoInsert2(name: String){
//1.SQL
let sql = "INSERT INTO T_Person (name, age, height) VALUES ('\(name)', 18, 1.8);"
//2.执行SQL
SQLiteManager.shareManager.queue?.inDatabase({ db in
//在程序执行中,最好不要使用 executeStatements
//使用 executeUpdate 会更安全
// db.executeUpdate(sql, withArgumentsIn: [])
try? db.executeUpdate(sql, values: nil)
})
print("OK")
}
func demoInsert1(){
//1.SQL
let sql = "INSERT INTO T_Person (name, age, height) VALUES ('张三', 18, 1.8);"
//2.执行SQL
SQLiteManager.shareManager.queue?.inDatabase({ db in
//在程序执行中,最好不要使用 executeStatements
db.executeStatements(sql)
})
}
}