美文网首页首页投稿(暂停使用,暂停投稿)
ActiveSQLite更简单的Swift数据库方案(SQLit

ActiveSQLite更简单的Swift数据库方案(SQLit

作者: 听你的声音LORD | 来源:发表于2017-08-17 16:53 被阅读0次

    ActiveSQLite 更简单的Swift数据库方案。项目地址:https://github.com/KevinZhouRafael/ActiveSQLite

    ——————————————————————————————————

    在做swift数据库的时候,选择并不多,有三种:CoreData,SQLite,Realm。很多喜欢用SQL的开发者在Objective-c中使用FMDB,而在swift中,star最多的SQLite框架就是SQLite.swift,其star数量也才4k+,但是没有更好的选择了。SQLite.swift功能很强大,但是有几点非常不方便:

    1,要写Expression表达式。
    2,不能自动包装成对象。

    参考如下代码:

    let users = Table("users")
    let id = Expression("id")
    let email = Expression("email")
    
    for stmt in try! db.prepare(users.select(id, email)) {
        print("id: \(stmt[id]), email: \(stmt[email])")
    }
    

    1、在创建表,增删查改表都会用到Expression表达式,一般我们通常将表达式作为类的静态属性,这样统一管理。
    2、读取stmt的数据只能显式的通过Expression表达式或者索引,这样在修改表的时候也是非常麻烦的,也很容易弄错。

    为了让SQLite.swift更加好用,我写了一个框架ActiveSQLite,是对SQLite.swift的封装。主要特性:

    1、自动创建表。
    2、增删查改除了支持Expression表达式,还支持String和key-value的字典。
    3、自动把查询结果包装为model。
    4、更方便的事务和异步,模型与数据库表映射,日志,简便的数据库升级。

    写的过程参考了MagicalRecordActiveRecord。易用性达到Realm的级别。

    ——————————————————————————————————————
    具体介绍如下:

    特性

    • 支持 SQLite.swift 的所有特性。
    • 自动创建表. 自动创建 id , created_at 和 updated_at 列。
    • 自动把SQL查询的数据赋值给数据库模型DBModel的属性。
    • 自定义表名和模型名之间的映射,列名和模型的属性名之间的映射。
    • 支持事务和异步。
    • 提供可扩展,链式,延迟执行的查询接口。
    • 通过属性名字符串,字典,或SQLite.swift的表达式Expression<T>查询和修改数据。
    • 日志级别

    例子

    执行 ActiveSQLiteTests target.

    用法

    import ActiveSQLite
    
    //定义model和table
    class Product:DBModel{
    
        var name:String!
        var price:NSNumber!
        var desc:String?
        var publish_date:NSDate?
    
    }
    
    //保存
    let product = Product()
    product.name = "iPhone 7"
    product.price = NSNumber(value:599)
    try! product.save()
    
    //查询
    let p = Product.findFirst("name",value:"iPhone") as! Product
    
    //or 
    let name = Expression<String>("name")
    let p = Product.findAll(name == "iPhone")!.first as! Product                    
    //id = 1, name = iPhone 7, price = 599, desc = nil,  publish_date = nil, created_at = 1498616987587.237, updated_at = 1498616987587.237, 
    
    //更新
    p.name = "iPad"
    try! p.update()
    
    //删除
    p.delete()
    
    

    开始

    在你的工程的target使用ActiveSQLite, 需要首先导入 ActiveSQLite 模块.

    import ActiveSQLite
    

    连接数据库

    DBConfigration.dbPath = "..."
    

    如果你没有设置dbPath,那么默认的数据库文件是在documents目录下的ActiveSQLite.db。

    支持的数据类型

    ActiveSQLite<br />Swift Type SQLite.swift<br />Swift Type SQLite<br /> SQLite Type
    NSNumber Int64 INTEGER
    NSNumber Double REAL
    String String TEXT
    nil nil NULL
    SQLite.Blob BLOB
    NSDate Int64 INTEGER

    NSNumber类型对应SQLite.swift的两种类型(Int64和Double)。NSNumber默认的映射类型是Int64。重写DBModel的doubleTypes()方法能标记属性为Double类型。

    class Product:DBModel{
    
        var name:String!
        var price:NSNumber!
        var desc:String?
        var publish_date:NSDate?
    
      override func doubleTypes() -> [String]{
          return ["price"]
      }
      
    }
    
    

    ActiviteSQLite映射NSDate类型到SQLite.swift的Int64类型。 你可以通过查找SQLite.swift的文档Custom Types of Documentaion映射NSDate到String。

    创建表

    ActiveSQLite自动创建表并且添加"id", "created_at"和 "updated_at"字段。"id"字段是主键。 创建的代码类似于下面这样:

    try db.run(products.create { t in      
        t.column(id, primaryKey: true)
        t.column(Expression<NSDate>("created_at"), defaultValue: NSDate(timeIntervalSince1970: 0))  
        t.column(Expression<NSDate>("updated_at"), defaultValue: NSDate(timeIntervalSince1970: 0))  
        t.column(...)  
    
    })                             
    
    // CREATE TABLE "Products" (
    //      "id" INTEGER PRIMARY KEY NOT NULL,
    //      created_at INTEGER DEFAULT (0),
    //      created_at INTEGER DEFAULT (0),
    //     ...
    //  )
      
    

    "created_at"和"updated_at"字段的单位是毫秒ms。

    映射

    你可以自定义表的名字, 列的名字,还可以设置瞬时属性不存在数据库中。

    1. 映射表名

    默认的表名和类名相同。

    //设置表名为 "ProductTable"
    override class var nameOfTable: String{
        return "ProductTable"
    }
    

    2. 映射列名

    默认的列名和属性名相同。

    //设置"product"属性映射的列名为"product_name"
    //设置"price"属性映射的列名为"product_price"
    override class func mapper() -> [String:String]{
        return ["name":"product_name","price":"product_price"];
    }
    

    3. 瞬时属性。

    瞬时属性不会被存在数据库中。

    override class func transientTypess() -> [String]{
        return ["isSelected"]
    }
    
    

    ActiveSQLite 仅仅保存三种属性类型 (String,NSNumber,NSDate)到数据库。 如果属性不是这三种类型,那么不会被存入数据库,它们被当做瞬时属性看待。

    表约束

    如果你要自定义列, 你仅需要实现CreateColumnsProtocol协议的createColumns方法,那么ActiveSQLite就不会自动创建列。写自己的建列语句,要注意列名和属性名必须一致,否则不能自动从查询sql封装数据库模型对象。

    
    class Users:DBModel,CreateColumnsProtocol{
        var name:String!
        var email:String!
        var age:Int?
       
        func createColumns(t: TableBuilder) {
            t.column(Expression<NSNumber>("id"), primaryKey: true)
            t.column(Expression<String>("name"),defaultValue:"Anonymous")
            t.column(Expression<String>("email"), , check: email.like("%@%"))
        }
    }
    

    更多信息查考SQLite.swift的文档table constraints document

    插入记录

    有三个方法用来插入记录。

    插入一条。

    func insert()throws ;
    

    插入多条。

    class func insertBatch(models:[DBModel])throws ;
    
    

    保存方法。

    如果数据库模型对象的 id == nil,那么执行插入。如果id != nil那么执行更新语句。

    func save() throws;
    

    例如:

    let u = Users()
    u.name = "Kevin"
    try! u.save()
                    
    var products = [Product]()
    for i in 1 ..< 8 {
        let p = Product()
        p.name = "iPhone-\(i)"
        p.price = NSNumber(value:i)
        products.append(p)
    }
                    
    try! Product.insertBatch(models: products)
    
    

    更多信息可以看ActiveSQLite的源码和例子, 也可以查阅SQLite.swift的文档Inserting Rows document

    更新记录

    有三种更新策略。

    1. 通过改属性值

    首先修改属性的值,然后执行save() 或者 update() 或者 updateBatch()。

    p.name = "zhoukai"
    p.save()
    
    

    2. 通过属性名字符串和属性值

    //更新一条
    u.update("name",value:"3ds")
    u.update(["name":"3ds","price":NSNumber(value:199)])
    
    
    //更新多条
    Product.update(["name": "3ds","price":NSNumber(value:199)], where: ["id": NSNumber(1)])
    
    

    2. 通过SQLite.swift的Setter

    //更新一条记录
    p.update([Product.price <- NSNumber(value:199))
    
    //更新多条
    Product.update([Product.price <- NSNumber(value:199), where: Product.name == "3ds")
    

    了解更多请看ActiveSQLite的源码和例子, 查看SQLite.swift的文档Updating Rows document , Setters document

    查询记录

    使用findFirst方法查询一条记录,使用findAll方法查询多条记录。

    方法名前缀是"find"的是类方法,这种方法一次性查询出结果。

    1.通过属性名字符串和属性值查询

    let p = Product.findFirst("name",value:"iWatch") as! Product
    
    let ps = Product.findAll("name",value:"iWatch",orders:["price",false]) as! [Product]
    
    

    2.通过SQLite.swift的Expression查询

    let id = Expression<NSNumber>("id")
    let name = Expression<String>("name")
    
    let arr = Product.findAll(name == "iWatch") as! Array<Product>
    
    let ps = Product.findAll(id > NSNumber(value:100), orders: [Product.id.asc]) as! [Product]
    
    

    链式查询

    链式查询方法是属性方法。

    let products = Product().where(Expression<NSNumber>("code") > 3)
                                    .order(Product.code)
                                    .limit(5)
                                    .run() as! [Product]
    
    

    不要忘记执行run()。

    更多复杂的查询参考ActiveSQLite的源码和例子。和SQLite.swift的文档Building Complex Queries

    表达式Expression

    SQLite.swift再更新update和查询select操作中,使用表达式Expression转换成SQL的'where'判断,。更多复杂的表达式用法,参考文档filtering-rows

    删除记录

    //1. 删除一条
    try? product.delete()
    
    //2. 删除所有
    try? Product.deleteAll()
    
    //3. 通过表达式Expression链式删除。
    try? Product().where(Expression<NSNumber>("code") > 3)
                                    .runDelete()
    
    

    事务

    建议把所有的insert,update,delete操作和alter表的代码全部放在ActiveSQLite.save代码块中。一个块中的sql操作在同一个事务当中。

     ActiveSQLite.save({ 
    
                    var products = [Product]()
                    for i in 0 ..< 3 {
                        let p = Product()
                        p.name = "iPhone-\(i)"
                        p.price = NSNumber(value:i)
                        products.append(p)
                    }
                    try Product.insertBatch(models: products)
                    
    
                    let u = Users()
                    u.name = "Kevin"
                    try u.save()
                    
    
                }, completion: { (error) in
                    
                    if error != nil {
                        debugPrint("transtion fails \(error)")
                    }else{
                        debugPrint("transtion success")
                    }
    
                })
    
    

    异步

    ActiveSQLite.saveAsync是一个异步的操作,当然代码块中的sql也在同一个事务当中。

     ActiveSQLite.saveAsync({ 
                .......
    
                }, completion: { (error) in
                    ......
                })
    

    改变表结构

    重命名表和添加列

    第1步. 用新的表名做映射,添加新的属性。

    class Product{
        var name:String!
        
        var newColumn:String!
        override class var nameOfTable: String{
            return "newTableName"
        }
        
    }
    

    Step 2. 当数据库版本改变时候,执行修改表名和添加列sql,并放在同一个事务中。

    let db = DBConnection.sharedConnection.db
                if db.userVersion == 0 {
                    ActiveSQLite.saveAsync({
                        try Product.renameTable(oldName:"oldTableName",newName:"newTableName")
                        try Product.addColumn(["newColumn"])
                        
                    }, completion: { (error) in
                        if error == nil {
                        
                            db.userVersion = 1
                        }
                    })
                    
                }             
    
    

    更多SQLite.swift的修改表信息参看 Altering the Schema

    索引

        let name = Expression<String>("name")
        Product.createIndex(name)
        Product.dropIndex(name)
    

    更多信息查看 Indexes of SQLite.swift Document

    删除表

    Product.dropTable()
    

    日志

    有四种日志级别,分别是: debug,info,warn,error。
    默认的日志级别是info。像这样来设置日志级别:

    //1. 设置日志级别
    DBConfigration.logLevel = .debug
    
    //2. 设置数据库路径
    DBConfigration.dbPath = "..."
    

    保证首先设置日志级别,后设置数据库路径。

    硬件需求

    • iOS 8.0+
    • Xcode 8.3.2
    • Swift 3

    安装

    Cocoapods

    再Podfile文件中添加:

    pod "ActiveSQLite"
    

    作者

    Rafael Zhou

    License

    ActiveSQLite is available under the MIT license. See the LICENSE file for more info.

    相关文章

      网友评论

        本文标题:ActiveSQLite更简单的Swift数据库方案(SQLit

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