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
- 郵件: wumingapie@gmail.com
- Twitter: @wumingapie
- Facebook: wumingapie
- LinkedIn: Rafael
License
ActiveSQLite is available under the MIT license. See the LICENSE file for more info.