swift定义iOS数据库框架1——创建数据库

虞安康
2023-12-01

数据库原始SQL操作

创建数据库

    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)")

数据库框架

数据库框架构建无非是,将原本SQL语句和对应操作的数据对象进行绑定管理,封装成增、删、改、查的操作接口便于用户调用。

  • 比较成熟的OC数据库框架有, pod ‘FMDB’。
  • 比较成熟的OC数据库框架有, pod ‘SQLite.swift’

自己构建过程中,为了验证操作数据我们需要一个查看数据库的工具。

查看工具

DB Browser for SQLite

自己构建

第一步:新建一个Connection类管理我们数据库

功能:打开数据库,关闭数据库,执行SQL语句,数据库基本属性使用

第二步:定义数据库位置方式(类型)

三种数据库存储位置不同
三种类型:内存数据库、临时数据库、URI方式(地址)
定义枚举=>Location

    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)
    }

第五步:检测数据库结果

定义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)
    }
}

定义检测方法check()

    //检测是否成功,还是失败
    //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: [])

定义处理队列

	//定义并行队列参数queue,queueKey
    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)
        }
    }

并在第一个构造函数里检测完调用(第四部)

	//第六步:定义队列->并行队列
    //主队列:串行队列
    //设置队列值(缓存当前Connaction所在的队列)
    queue.setSpecific(key: queueKey, value: queueContext)	

第七步:执行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))
    }
}

SQliteFramework面向对象编程,创建表SQL

	let users = Table("users")
	let name = Expression<String>("name")
	let email = Expression<String>("email")
	let sql = users.create { (t) in
     	t.column(name).column(email)
	}
 类似资料: