ActiveSQLite更簡單的Swift數(shù)據(jù)庫方案(SQLite.swift封裝)

ActiveSQLite 更簡單的Swift數(shù)據(jù)庫方案子房。項目地址:https://github.com/KevinZhouRafael/ActiveSQLite

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

在做swift數(shù)據(jù)庫的時候,選擇并不多恒削,有三種:CoreData池颈,SQLite,Realm钓丰。很多喜歡用SQL的開發(fā)者在Objective-c中使用FMDB躯砰,而在swift中,star最多的SQLite框架就是SQLite.swift携丁,其star數(shù)量也才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暖侨、在創(chuàng)建表餐禁,增刪查改表都會用到Expression表達式,一般我們通常將表達式作為類的靜態(tài)屬性,這樣統(tǒng)一管理户魏。
2媚媒、讀取stmt的數(shù)據(jù)只能顯式的通過Expression表達式或者索引榆纽,這樣在修改表的時候也是非常麻煩的蒿叠,也很容易弄錯。

為了讓SQLite.swift更加好用券册,我寫了一個框架ActiveSQLite频轿,是對SQLite.swift的封裝。主要特性:

1烁焙、自動創(chuàng)建表航邢。
2、增刪查改除了支持Expression表達式考阱,還支持String和key-value的字典翠忠。
3鞠苟、自動把查詢結(jié)果包裝為model乞榨。
4、更方便的事務和異步当娱,模型與數(shù)據(jù)庫表映射吃既,日志,簡便的數(shù)據(jù)庫升級跨细。

寫的過程參考了MagicalRecord鹦倚,ActiveRecord。易用性達到Realm的級別冀惭。

——————————————————————————————————————
具體介紹如下:

特性

  • 支持 SQLite.swift 的所有特性震叙。
  • 自動創(chuàng)建表. 自動創(chuàng)建 id , created_at 和 updated_at 列。
  • 自動把SQL查詢的數(shù)據(jù)賦值給數(shù)據(jù)庫模型DBModel的屬性散休。
  • 自定義表名和模型名之間的映射媒楼,列名和模型的屬性名之間的映射。
  • 支持事務和異步戚丸。
  • 提供可擴展划址,鏈式,延遲執(zhí)行的查詢接口。
  • 通過屬性名字符串夺颤,字典痢缎,或SQLite.swift的表達式Expression<T>查詢和修改數(shù)據(jù)。
  • 日志級別

例子

執(zhí)行 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

連接數(shù)據(jù)庫

DBConfigration.dbPath = "..."

如果你沒有設置dbPath世澜,那么默認的數(shù)據(jù)庫文件是在documents目錄下的ActiveSQLite.db独旷。

支持的數(shù)據(jù)類型

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抚恒。

創(chuàng)建表

ActiveSQLite自動創(chuàng)建表并且添加"id", "created_at"和 "updated_at"字段咱台。"id"字段是主鍵。 創(chuàng)建的代碼類似于下面這樣:

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俭驮。

映射

你可以自定義表的名字, 列的名字回溺,還可以設置瞬時屬性不存在數(shù)據(jù)庫中。

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. 瞬時屬性。

瞬時屬性不會被存在數(shù)據(jù)庫中逸嘀。

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

ActiveSQLite 僅僅保存三種屬性類型 (String,NSNumber,NSDate)到數(shù)據(jù)庫车要。 如果屬性不是這三種類型,那么不會被存入數(shù)據(jù)庫崭倘,它們被當做瞬時屬性看待翼岁。

表約束

如果你要自定義列, 你僅需要實現(xiàn)CreateColumnsProtocol協(xié)議的createColumns方法,那么ActiveSQLite就不會自動創(chuàng)建列司光。寫自己的建列語句琅坡,要注意列名和屬性名必須一致,否則不能自動從查詢sql封裝數(shù)據(jù)庫模型對象残家。


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 ;

保存方法。

如果數(shù)據(jù)庫模型對象的 id == nil回窘,那么執(zhí)行插入诺擅。如果id != nil那么執(zhí)行更新語句。

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. 通過改屬性值

首先修改屬性的值凌盯,然后執(zhí)行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"的是類方法二打,這種方法一次性查詢出結(jié)果县忌。

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]

不要忘記執(zhí)行run()继效。

更多復雜的查詢參考ActiveSQLite的源碼和例子症杏。和SQLite.swift的文檔Building Complex Queries

表達式Expression

SQLite.swift再更新update和查詢select操作中瑞信,使用表達式Expression轉(zhuǎn)換成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
                ......
            })

改變表結(jié)構(gòu)

重命名表和添加列

第1步. 用新的表名做映射匀谣,添加新的屬性照棋。

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

Step 2. 當數(shù)據(jù)庫版本改變時候,執(zhí)行修改表名和添加列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. 設置數(shù)據(jù)庫路徑
DBConfigration.dbPath = "..."

保證首先設置日志級別,后設置數(shù)據(jù)庫路徑驻售。

硬件需求

  • 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.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末露久,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子欺栗,更是在濱河造成了極大的恐慌毫痕,老刑警劉巖征峦,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異消请,居然都是意外死亡栏笆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門臊泰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛉加,“玉大人,你說我怎么就攤上這事缸逃≌爰ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵需频,是天一觀的道長丁眼。 經(jīng)常有香客問我,道長昭殉,這世上最難降的妖魔是什么户盯? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮饲化,結(jié)果婚禮上莽鸭,老公的妹妹穿的比我還像新娘。我一直安慰自己吃靠,他們只是感情好硫眨,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巢块,像睡著了一般礁阁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上族奢,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天姥闭,我揣著相機與錄音,去河邊找鬼越走。 笑死棚品,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的廊敌。 我是一名探鬼主播铜跑,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼骡澈!你這毒婦竟也來了锅纺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤肋殴,失蹤者是張志新(化名)和其女友劉穎囤锉,沒想到半個月后坦弟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡官地,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年酿傍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片区丑。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡拧粪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沧侥,到底是詐尸還是另有隱情可霎,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布宴杀,位于F島的核電站癣朗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旺罢。R本人自食惡果不足惜旷余,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扁达。 院中可真熱鬧正卧,春花似錦、人聲如沸跪解。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叉讥。三九已至窘行,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間图仓,已是汗流浹背罐盔。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留救崔,地道東北人惶看。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像帚豪,于是被迫代替她去往敵國和親碳竟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容