欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用

程序员文章站 2022-04-11 16:12:09
...

0. 本篇重点

本篇主要整理一下 Swift 中如何使用 SQLite 这一轻量级数据库。

  • SQLite 接口的引入
  • SQLite 的基本操作(开启关闭和CRUD)

1. SQLite 介绍

百度百科:https://baike.baidu.com/item/SQLite

官网:https://www.sqlite.org/index.html

2. 引入 SQLite 库

2.1 新建 Single View App
2.2 新建一个纯代码文件 SQLiteManager.swift
2.3 新建一个纯代码文件 Test.swift
2.4 添加 SQLite 库

Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用
选择最新版
Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用

2.5 通过建立一个Objective-C 文件来获得桥接头文件

我们要做的就是调用引入的 libsqlite3.tbd 里面的接口。

因为它是用 C 语言写的,所以我们需要一个桥接头文件(bridging header)。

Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用

Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用

2.6 删除 Objective-C 文件
2.7 在桥接文件中引入 SQLite 的头文件

Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用

这样我们就可以调用引用进来的 libsqlite3.tbd 里面的接口了。

3. 编写 SQLiteManger.swift

//
//  SQLiteManager.swift
//  S0902
//
//  Created by Hedon - on 2020/4/15.
//  Copyright © 2020 Hedon -. All rights reserved.
//

import Foundation

class SQLiteManager:NSObject
{
    
    private var dbPath:String!    //数据库所存储的文件所在路径
    private var database:OpaquePointer?=nil   //数据库,是一个指针,指向一个结构体
    
    //共享一个实例化对象 —————— 单例变量
    static var shareInstance:SQLiteManager{
        return SQLiteManager()
    }
    
    
    override init() {
        let dirpath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        dbPath =  dirpath.appendingPathComponent("app.sqlite").path
    }
    
    
    //打开数据库
    func openDB() -> Bool {
        let result = sqlite3_open(dbPath, &database)
        if result != SQLITE_OK{
            print("打开数据库失败")
            return false
        }
        return true
    }
    
    
    //关闭数据库
    func closeDB(){
        sqlite3_close(database)
    }
    
    
    /**
            执行数据库语句
            1. 创建 create
            2. 插入 insert
            3. 更新 update
            4. 删除 delete
     */
    func execNoneQuerySQL(sql:String)->Bool{
        
        var errMsg:UnsafeMutablePointer<Int8>? = nil   //错误信息的指针(不安全、可修改)
        let cSql = sql.cString(using: String.Encoding.utf8)!  //给sql语句进行编码
        
        /**
            参数说明:
                1. 已打开的数据库句柄
                2. 执行的sql语句
                3. 回调函数
                4. 自定义指针,会传递到回调函数内
                5. 错误信息指针
         */
        //执行成功
        if sqlite3_exec(database, cSql, nil, nil, &errMsg) == SQLITE_OK {
            return true
        }
        
        //执行不成功
        let msg = String.init(cString: errMsg!)
        print(msg)
        return false
        
    }
    
    /**
            执行数据库语句
                1. 查询
     */
    func execQuerySQL(sql:String)->[[String:AnyObject]]?{
        
        let cSql = sql.cString(using: String.Encoding.utf8)!  //对sql语句进行utf8编码
        var statement:OpaquePointer? = nil  //语句的指针
        
        /**
                参数说明:
                    1. 已打开的数据库句柄
                    2. 执行的sql语句
                    3. 以字节为单位的sql语句长度,-1表示自动计算
                    4. 语句句柄,据此获取查询结果,需要调用 sqlite3_finalize 释放
                    5. 未使用的指针地址,通常传入nil
         */
        //执行不成功
        if sqlite3_prepare_v2(database, cSql, -1, &statement, nil) != SQLITE_OK{
            
            sqlite3_finalize(statement)
            
            print("执行 \(sql) 错误")
            let errmsg = sqlite3_errmsg(database)
            if errmsg != nil{
                print(errmsg!)
            }
            
            return nil
        }
        
        //成功:需要从 statement 里面取数据,定义 rows 变量来接收数据
        var rows = [[String:AnyObject]]()
        
        //每次取 1 行
        while sqlite3_step(statement) == SQLITE_ROW{
            rows.append(record(stmt: statement!))
        }
        
        //最后释放空间
        sqlite3_finalize(statement)
        
        //返回结果
        return rows
        
    }
    
    //读一行记录一行
    private func record(stmt:OpaquePointer)->[String:AnyObject]{
        var row = [String:AnyObject]()
        
        //遍历所有列,获取每一列的信息
        for col in 0..<sqlite3_column_count(stmt){
            
            let cName = sqlite3_column_name(stmt, col) //获取列名
            let name = String(cString: cName!,encoding: String.Encoding.utf8)
            
            var value:AnyObject?
            
            switch sqlite3_column_type(stmt, col) {
            case SQLITE_FLOAT:
                value = sqlite3_column_double(stmt, col) as AnyObject
            case SQLITE_INTEGER:
                value = Int(sqlite3_column_int(stmt, col)) as AnyObject
            case SQLITE_TEXT:
                let cText = sqlite3_column_text(stmt, col)
                value = String.init(cString: cText!) as AnyObject
            case SQLITE_NULL:
                value = NSNull()
            default:
                print("不支持的数据类型")
            }
            row[name!] = value ?? NSNull()
        }
        
        return row
    }
}

4. 编写 Test.swift

//
//  Test.swift
//  S0902
//
//  Created by Hedon - on 2020/4/15.
//  Copyright © 2020 Hedon -. All rights reserved.
//

import Foundation

class Test
{
    static func initDB()
    {
        //引用SQLiteManager的单例对象
        let sqlite = SQLiteManager.shareInstance
        
        //没打开则返回
        if !sqlite.openDB()
        {
            return
        }
        
        //创建表
        let createSql = "CREATE TABLE IF NOT EXISTS student('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + "'name' TEXT, 'phone' TEXT);"
        if !sqlite.execNoneQuerySQL(sql: createSql)   //如果不成功,则关闭数据库并返回。
        {
            sqlite.closeDB()
            return
        }
        
        //清除表
        let cleanAllStu = "DELETE FROM student;"
        if !sqlite.execNoneQuerySQL(sql: cleanAllStu)
        {
            sqlite.closeDB()
            return
        }
        
        //重置表
        let resetStu = "DELETE FROM sqlite_sequence WHERE name = 'student';"
        if !sqlite.execNoneQuerySQL(sql: resetStu)
        {
            sqlite.closeDB()
            return
        }
        
        //插入数据
        let stu0 = "INSERT INTO student(name,phone) VALUES('张三','aaa@qq.com');"
        let stu1 = "INSERT INTO student(name,phone) VALUES('李四','aaa@qq.com');"
        if !sqlite.execNoneQuerySQL(sql: stu0)
        {
            sqlite.closeDB()
            return
        }
        if !sqlite.execNoneQuerySQL(sql: stu1)
        {
            sqlite.closeDB()
            return
        }
        
        
        //关闭数据库
        sqlite.closeDB()
        
    }
    
    
    static func GetStudent()
    {
        //获取SQLiteManager的单例对象
        let sqlite = SQLiteManager.shareInstance
        
        //打开数据库
        if !sqlite.openDB(){return}
        
        //查询所有
        let queryResult = sqlite.execQuerySQL(sql: "SELECT * FROM student;")
        
        print(queryResult!)
        
        for row in queryResult!
        {
            print(row["name"]!)
        }
        
        //关闭数据库
        sqlite.closeDB()
    }
    
    static func GetID1()->[AnyObject]
    {
        var info:[AnyObject] = []
        //获取SQLiteManager的单例对象
        let sqlite = SQLiteManager.shareInstance
        
        //打开数据库
        if !sqlite.openDB(){return info}
        
        //查询所有
        let queryResult = sqlite.execQuerySQL(sql: "SELECT * FROM student WHERE id=1;")
        
        print(queryResult!)
        
        for row in queryResult!
        {
            info.append(row["id"]!)
            info.append(row["name"]!)
            info.append(row["phone"]!)
            
        }
        
        //关闭数据库
        sqlite.closeDB()
        
        return info
    }
    
}

5. 设计 Main.storyboard 界面

Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用

6. 效果

  • 界面(显示 id = 1 的所有信息)
    Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用
  • 点击按钮,控制台信息
    Swift — UIKit 之(10)—— 持久层|SQLite 的基本使用