美文网首页
【iOS开发】数据存储——SQLite3框架FMDB的使用

【iOS开发】数据存储——SQLite3框架FMDB的使用

作者: Lebron_James | 来源:发表于2017-02-16 22:34 被阅读72次

    【iOS开发】数据存储——SQLite3框架FMDB的使用

    在学习FMDB框架之前,我们首先来大概了解下SQL(Structed Query Language)语句:

    • 特点:1)不区分大小写;2)每条语句都必须以分号结尾
    • 常用的关键字:selectinsertupdatedeletefromcreatewheredescorderbygrouptablealterviewindex等等
    • 数据库中不可以使用关键字来命名字段
    • 字段类型:1)integer:整型值;2)real:浮点值;3)text:文本字符串;4)blob:二进制数据
    • 语句的种类
      • 数据定义语句(DDL:Data Definition Language):包括createdrop等操作,新建表:create table,删除表:drop table
      • 数据操作语句(DML: Data Manipulation Language): 包括insert/update/delete等
      • 数据查询语句(DQL: Data Query Language): select是DQL(也是所有SQL)用得最多的语句,其他常用的关键字有whereorder bygroup byhaving

    FMDB框架

    FMDB有三个主要的类
    • FMDatabase: 代表一个SQLite数据库,用于执行SQL语句
    • FMResultSet: 代表查询结果
    • FMDatabaseQueue: 在多线程中执行查询或者更新操作时使用
    数据库存储路径

    我们需要使用一个数据库的存储路径来创建一个FMDatabase实例,这个路径可以时以下三种情况之一:

    • 一个文件路径,这个路径可以时目前不存在的。如果不存在,它会自动创建。
    • 一个空字符""。使用空字符,它会在一个临时的地方创建一个空数据库,在FMDatabase连接关闭时,创建的数据库会被删除。
    • nil。会创建一个内存中临时的数据库,当FMDatabase连接关闭时,数据会被销毁

    简单约束

    建表时,我们可以给字段设置一些约束条件,而且我们建议给字段设定严格的约束,以保证数据的规范性

    • not null: 规定字段的值不能为null
    • unique: 规定字段的值必须是唯一的
    • default: 指定字段的默认值

    主键约束

    良好的数据库编程规范应该保持每条记录的唯一性,因此,增加主键约束来保证记录的唯一性。

    什么是主键:Primary Key,简称PK,用来唯一标示某一条记录。主键可以是一个或多个字段。

    设计原则:1)主键应当是对用户没有意义的;2)永远也不要更新主键;3)主键不应该包含动态变化的数据;4)主键应当由计算机生成。

    主键字段:1)只要声明为primary key,就说明是一个主键字段;2)主键字段默认包含了not nullunique连个约束;3)如果想让主键自动增长,那么字段的类型必须是integer类型,并且增加autoincrement关键字。

    外键约束

    利用外键约束可以建立表与表之间的联系。外键一般是:一张表的某个字段引用着另外一张表的主键字段。

    代码演示

    数据准备

    这个例子演示用到了两个类:studentclass

    class StudentModel: NSObject {
    
        var name: String
        var age: Int
        var score: Double
        var classModel: ClassModel
        
        init(name: String, age: Int, score: Double, classModel: ClassModel) {
            self.name = name
            self.age = age
            self.score = score
            self.classModel = classModel
        }
        
    }
    
    
    class ClassModel: NSObject {
    
        var name: String
        
        init(name: String) {
            self.name = name
        }
        
    }
    

    下面其他方便使用的函数:

    // 获取全部班级
    private func getClasses() -> [ClassModel] {
        let ios = ClassModel(name: ClassName.ios.rawValue)
        let android = ClassModel(name: ClassName.android.rawValue)
        let html5 = ClassModel(name: ClassName.html5.rawValue)
        let java = ClassModel(name: ClassName.java.rawValue)
        
        return [ios, android, html5, java]
    }
    
    // 获取全部学生
    private func getStudents() -> [StudentModel] {
        // 班级
        var ios, android, html5, java:ClassModel!
        
        for aClass in getClasses() {
            switch aClass.name {
            case ClassName.ios.rawValue:
                ios = aClass
                
            case ClassName.android.rawValue:
                android = aClass
                
            case ClassName.html5.rawValue:
                html5 = aClass
                
            case ClassName.java.rawValue:
                java = aClass
                
            default:
                break
            }
        }
        
        // android班
        let zhangsan = StudentModel(name: "张三", age: 18, score: 59, classModel: android)
        let lisi = StudentModel(name: "李四", age: 19, score: 65, classModel: android)
        let wangwu = StudentModel(name: "王五", age: 17, score: 80, classModel: android)
        let zhaoliu = StudentModel(name: "赵六", age: 16, score: 95, classModel: android)
        
        // HTML5班
        let xiaoming = StudentModel(name: "小明", age: 20, score: 75, classModel: html5)
        let xiaofang = StudentModel(name: "小芳", age: 21, score: 79, classModel: html5)
        
        // Java班
        let xiaohong = StudentModel(name: "小红", age: 25, score: 85, classModel: java)
        let xiaolong = StudentModel(name: "小龙", age: 26, score: 80, classModel: java)
        
        // iOS班
        let james = StudentModel(name: "詹姆斯", age: 32, score: 95, classModel: ios)
        let irving = StudentModel(name: "欧文", age: 24, score: 89, classModel: ios)
        
        return [zhangsan, lisi, wangwu, zhaoliu, xiaoming, xiaofang, xiaohong, xiaolong, james, irving]
    }
    
    // 获取班级ID
    private func getClassID(withName name: ClassName) -> Int {
        switch name {
        case .ios:
            return ClassID.ios.rawValue
            
        case .android:
            return ClassID.android.rawValue
            
        case .html5:
            return ClassID.html5.rawValue
            
        case .java:
            return ClassID.java.rawValue
        }
    }
    
    // 用于保存班级的名称
    private enum ClassName: String {
        case ios, android, html5, java
    }
    
    // 用于保存班级的id
    private enum ClassID: Int {
        case ios, android, html5, java
    }
    
    创建数据库
    // 获取路径
    let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    let schoolPath = fileURL!.path + "/school.db"
    print(schoolPath)
    
    // 创建表格
    if let db = FMDatabase(path: schoolPath) {
        self.db = db
    }
    else {
        print("无法创建数据库")
        return
    }
    
    // 打开表格
    guard db.open() else {
        print("无法打开数据库")
        return
    }
    
    创建表格
    private func createTable() {
        // 创表学生表:主键名称为integer类型的id、自动增长;文本类型的name、不为空;integer类型的age、不为空;real类型的score、不为空;
        // integer类型的class_id、不为空;fk_student_class外键,student表格的class_id字段引用class表格的id字段。
        let createStudentTable = "create table student (id integer primary key autoincrement, name text not null, age integer not null, score real not null, class_id integer not null, constraint fk_student_class foreign key (class_id) references class (id));"
        
        // 创建班级表:主键名称为integer类型的id、自动增长;文本类型的name、不为空
        let createClassTable = "create table class (id integer primary key autoincrement, name text not null);"
        
        do {
            try db.executeUpdate(createStudentTable, values: nil)
            try db.executeUpdate(createClassTable, values: nil)
            print("表格创建成功")
        }
        catch {
            print("创建表格错误: \(error.localizedDescription)")
        }
    }
    
    删除表格
    private func dropTable() {
        do {
            try db.executeUpdate("drop table student", values: nil)
            try db.executeUpdate("drop table class", values: nil)
            print("表格删除成功")
        }
        catch {
            print("删表错误: \(error.localizedDescription)")
        }
    }
    
    插入数据
    private func insert(students: [StudentModel], andClasses classes: [ClassModel]) {
        
        for aClass in classes {
            do {
                try db .executeUpdate("insert into class (name) values (?);", values: [aClass.name])
                print("\(aClass.name)插入成功")
            }
            catch {
                print("数据插入错误: \(error.localizedDescription)")
            }
        }
        
        for student in students {
            do {
                try db.executeUpdate("insert into student (name, age, score, class_id) values (?, ?, ?, ?);", values: [student.name, student.age, student.score, getClassID(withName: ViewController.ClassName(rawValue: student.classModel.name)!)])
                print("\(student.name)插入成功")
            }
            catch {
                print("failed: \(error.localizedDescription)")
            }
        }
    }
    
    更新数据
    private func updateData() {
        do {
            // 把表格中所有年龄大于等于30的记录,年龄改为29
            try db.executeUpdate("update student set age = 29 where age >= 30;", values: nil)
            print("数据更新成功")
        }
        catch {
            print("数据更新错误:\(error.localizedDescription)")
        }
    }
    
    查询数据
    private func queryData() {
        do {
            // 从student表格中查询年龄大于等于18的记录
            let rs = try db.executeQuery("select * from student where age >= 18;", values: nil)
            
            while rs.next() {
                let name = rs.string(forColumn: "name")
                let age = rs.int(forColumn: "age")
                let score = rs.double(forColumn: "score")
                print("姓名: \(name),年龄:\(age), 分数:\(score)")
            }
        }
        catch {
            print("数据更新错误:\(error.localizedDescription)")
        }
    }
    
    计算记录的数量
    private func computeCountOfAllRecords() {
        do {
            let rs = try db.executeQuery("select count(age) from student where age >= 18;", values: nil)
            while rs.next() {
                let count = rs.int(forColumnIndex: 0)
                print("记录总数为:\(count)")
            }
        }
        catch {
            print("计算总数量错误:\(error.localizedDescription)")
        }
    }
    
    排序
    private func orderByAge() {
        do {
            // asc:升序。 desc:降序
            let rs = try db.executeQuery("select * from student order by age asc;", values: nil)
            
            while rs.next() {
                let name = rs.string(forColumn: "name")
                let age = rs.int(forColumn: "age")
                let score = rs.double(forColumn: "score")
                print("姓名: \(name),年龄:\(age), 分数:\(score)")
            }
        }
        catch {
            print("排序错误:\(error.localizedDescription)")
        }
    }
    
    分页查询
    private func selectDataWithLimitation() {
        do {
            let rs = try db.executeQuery("select * from student order by score desc limit 5;", values: nil)
            
            while rs.next() {
                let name = rs.string(forColumn: "name")
                let age = rs.int(forColumn: "age")
                let score = rs.double(forColumn: "score")
                print("姓名: \(name),年龄:\(age), 分数:\(score)")
            }
        }
        catch {
            print("限制数量错误:\(error.localizedDescription)")
        }
    }
    

    Demo地址 >>

    如果文中有错误,请指出!我们共同学习,共同进步。谢谢!

    相关文章

      网友评论

          本文标题:【iOS开发】数据存储——SQLite3框架FMDB的使用

          本文链接:https://www.haomeiwen.com/subject/ybqxwttx.html