静态构建表-指手动创建数据库,自己写sql代码
第一步:新建一个Connection类?
作用:管理我们数据库
功能:
打开数据库
关闭数据库
执行SQL语句
数据库基本属性使用(后面会用到)->铺垫
第二步:定义数据库位置方式(类型)
三种类型:内存数据库、临时数据库、URI方式(地址)
枚举->Location
三种数据库存储位置不同
网址:https://www.sqlite.org/inmemorydb.html#sharedmemdb
第三步:定义数据库SQL操作类(操作表)
种类:
插入数据-insert
更新数据-update
删除数据-delete
枚举->定义表操作
第四步:打开数据库
通过构造方法实现
第五步:检测数据库结果
第六步:创建数据库执行队列
数据库所有表操作->什么样队列中执行
串行队列->一个个排队执行(线下买票)
并行队列->同时执行(网上购票)
问题:什么时候串行队列、什么时候并行队列?
挖个坑?后面我们在讨论?
第七步:执行SQL语句
第八步:定义数据库基本操作属性
1、关闭数据库
2、数据库状态->是否是可读性数据库
3、最后插入的一条数据所返回的行id->rowid
4、数据库受影响行数->changes
5、数据库自从打开到目前为止数据库受影响行数
insert、delete、update总共操作几次
insert->10次(插入10条数据)
delete->删除5条->干坏事5次
update->更新2次->干坏事2次
总共:到目前为止17次
6、中断任何长时间运行的查询操作(客户端少见)
服务器开发->我开发服务器->100万数量级->耗费时间长
7、数据库超时
第九步:测试
具体代码
import UIKit
import SQLite3
//太监类
//第一步:新建一个Connection类?
public final class Connection {
// 第二步:定义数据库位置方式(类型)->几种位置方式->指定存储位置
// 三种类型:内存数据库、临时数据库、URI方式(地址)
// 枚举->Location
//网址:https://www.sqlite.org/inmemorydb.html#sharedmemdb
public enum Location {
//内存数据库->相当于->uri(":memory")
case inMemory
//临时数据库->相当于->uri("")
case temporary
//URI方式(地址)->相当于->uri("/user/test.db")
case uri(String)
}
// 第三步:定义数据库SQL操作类(操作表)
// 种类:
// 插入数据-insert
// 更新数据-update
// 删除数据-delete
// 枚举->定义表操作
public enum Operation {
// 插入数据-insert
case insert
// 更新数据-update
case update
// 删除数据-delete
case delete
//数据库类型转换
fileprivate init(rawValue:Int32){
switch rawValue {
case SQLITE_INSERT:
self = .insert
case SQLITE_UPDATE:
self = .update
case SQLITE_DELETE:
self = .delete
default:
fatalError("没有这个操作类型...")
}
}
}
// 第四步:打开数据库
// 通过构造方法实现
//第一个默认构造方法
fileprivate var _handle: OpaquePointer? = nil
public init(_ location: Location = .inMemory, readonly: Bool = false) throws {
//打开数据库
//参数一:数据位置
//参数二:数据库指针(用于操作数据库)
//参数三:打开数据库方式
//SQLITE_OPEN_READONLY:只读数据库
//SQLITE_OPEN_CREATE:创建数据库(没有就创建)
//SQLITE_OPEN_READWRITE:可读可写数据库
//SQLITE_OPEN_FULLMUTEX:设置数据库链接运行队列模式->串行队列、并行队列
//支持多线程操作
let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
let result = sqlite3_open_v2(location.description, &_handle, flags | SQLITE_OPEN_FULLMUTEX, nil)
//判定数据库是否成功状态(面向形式设计)
//将result->变为对象->枚举->关心可选项
//关心 = 1,0,-1->枚举->对象->代码阅读可读性高
//检测数据库返回值
try check(result)
//第六步:定义队列->并行队列
//主队列:串行队列
//设置队列值(缓存当前Connaction所在的队列)
queue.setSpecific(key: queueKey, value: queueContext)
}
//第二个默认构造方法
//第二个构造方法调用第一个构造方法
//普及一个语法->遍历构造器
public convenience init(_ filename: String, readonly: Bool = false) throws {
try self.init(.uri(filename), readonly: readonly)
}
//第五步:检测数据库结果
//检测是否成功,还是失败
//resultCode->数据库返回结果
//throws->向外抛异常->客户端处理异常
//大家都是用代码,很少自己写(成长很少)
//第一次接触,蒙蔽很正常
//相当于异常传递->事件传递
@discardableResult func check(_ resultCode: Int32) throws -> Int32 {
guard let error = Result(errorCode: resultCode, connection: self) else {
return resultCode
}
//失败了->数据库打开失败了
//抛异常(我不处理异常)
throw error
}
// 第六步:创建数据库执行队列
// 数据库所有表操作->什么样队列中执行
// 串行队列->一个个排队执行(线下买票)
// 并行队列->同时执行(网上购票)
// 问题:什么时候串行队列、什么时候并行队列?
// 挖个坑?后面我们在讨论?
//课后记得复习
//注意:这个队列执行一个操作->方法->闭包->执行闭包(核心:还是方法)->代码块
//同步代码块->主线程执行的
//方法泛型
//代码块动态
//串行队列定义:DispatchQueue(label: "database")
//并行队列定义(加了一个attributes):DispatchQueue(label: "database", attributes: [])
//百度搜索查看并行队列参数
fileprivate var queue = DispatchQueue(label: "database", attributes: [])
//根据key获取值
fileprivate var queueKey = DispatchSpecificKey<Int>()
//当前Connection指针->将Connection类对象引用->转为Int类型指针(引用)->强制类型转换
//根据这个指针判定当前是串行队列,还是并行队列
fileprivate lazy var queueContext: Int = unsafeBitCast(self, to: Int.self)
func sync<T>(_ block: () throws -> T) rethrows -> T {
//定义队列
if DispatchQueue.getSpecific(key: queueKey) == queueContext {
//串行队列->直接执行->主队列->排队执行
return try block()
} else {
//并行队列->子队列->指通过queue管理执行,同时执行
return try queue.sync(execute: block)
}
}
//第七步:执行SQL语句
func execute(_ SQL: String) throws {
//同步代码块
//"_"表示返回变量名接受"_"默认->变量通配符->没有类型
_ = try sync {
try self.check(sqlite3_exec(self._handle, SQL, nil, nil, nil))
}
}
// 第八步:定义数据库基本操作属性
// 1、关闭数据库
deinit {
//析构函数
sqlite3_close(_handle)
}
// 2、数据库状态->是否是可读性数据库
public var readonly: Bool {
return sqlite3_db_readonly(_handle, nil) == 1
}
// 3、最后插入的一条数据所返回的行id->rowid
public var lastInsertRowId: Int64 {
return sqlite3_last_insert_rowid(_handle)
}
// 4、数据库受影响行数->changes
public var changes: Int {
return Int(sqlite3_changes(_handle))
}
// 5、数据库自从打开到目前为止数据库受影响行数
// insert、delete、update总共操作几次
// insert->10次(插入10条数据)
// delete->删除5条->干坏事5次
// update->更新2次->干坏事2次
// 总共:到目前为止17次
public var totalChanges: Int {
return Int(sqlite3_total_changes(_handle))
}
// 6、中断任何长时间运行的查询操作(客户端少见)
// 服务器开发->我开发服务器->100万数量级->耗费时间长
public func interrupt(){
sqlite3_interrupt(_handle)
}
// 7、数据库超时
public var busyTimeout: Double = 0 {
didSet{
sqlite3_busy_timeout(_handle, Int32(busyTimeout * 1_000))
}
}
}
extension Connection.Location : CustomStringConvertible {
//数据库位置
public var description: String {
switch self {
case .inMemory:
return ":memory"
case .temporary:
return ""
case .uri(let URI):
return URI
}
}
}
//定义Result枚举类
//普及:枚举泛型
public enum Result : Error {
//SQLITE_OK->执行成功
//SQLITE_ROW->执行下一行(成功)
//SQLITE_DONE->执行完成
fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]
case error(message: String, code: Int32)
//errorCode->数据库返回结果
init?(errorCode: Int32, connection: Connection){
guard !Result.successCodes.contains(errorCode) else {
return nil
}
//获取错误信息(封装数据库错误信息)
let message = String(cString: sqlite3_errmsg(connection._handle))
//初始化error信息
self = .error(message: message, code: errorCode)
}
}
调用
let path = Bundle.main.path(forResource: "test", ofType: ".db")
print(path!)
let db = try! Connection(path!)
try! db.execute("create table t_teacher(name text, email text)")
网友评论