1 创建一个基类
(1)只要我们让实体类继承 SQLModel,那么它就会自行将实体类跟数据库表作关联映射。之后只要操作实体类对象就可以直接进行数据的读取,修改,新增操作。
(2)SQLModel 其内部实现使用到了 Swift 的反射,通过遍历对象内的所有属性,从而自动拼接成相应的 SQL 语句来执行。
(3)SQLModel 代码如下 使用的时候直接粘
//
// SQLModel.swift
// FMDBtest
//
// Created by 刁宏瑞 on 2019/12/11.
// Copyright © 2019 刁宏瑞. All rights reserved.
//
import Foundation
protocol SQLModelProtocol {}
// 数据库模型(一张表对应一个模型)
@objcMembers
class SQLModel: NSObject, SQLModelProtocol {
// 模型对应的表名(直接使用对应模型类名字)
internal var table = ""
// 记录每个模式对应的数据表是否已经创建完毕了
private static var verified = [String:Bool]()
// 初始化方法
required override init() {
super.init()
// 自动初始化表名
self.table = type(of: self).table
// 自动建对应的数据库表
let verified = SQLModel.verified[self.table]
if verified == nil || !verified! {
let db = SQLiteManager.shareManger().db
var sql = "CREATE TABLE IF NOT EXISTS \(table) ("
// Columns
let cols = values()
var first = true
for col in cols {
if first {
first = false
sql += getColumnSQL(column:col)
} else {
sql += ", " + getColumnSQL(column: col)
}
}
// Close query
sql += ")"
if db.open() {
if db.executeUpdate(sql, withArgumentsIn:[]) {
SQLModel.verified[table] = true
print("\(table) 表自动创建成功")
}
}
}
}
// 返回主键字段名(如果模型主键不是id,则需要覆盖这个方法)
func primaryKey() -> String {
return "id"
}
// 忽略的属性(模型中不需要与数据库表进行映射的字段可以在这里发返回)
func ignoredKeys() -> [String] {
return []
}
// 静态方法返回表名
static var table:String {
// 直接返回类名字
return "\(classForCoder())"
}
// 删除指定数据(可附带条件)
@discardableResult
class func remove(filter: String = "") -> Bool {
let db = SQLiteManager.shareManger().db
var sql = "DELETE FROM \(table)"
if !filter.isEmpty {
// 添加删除条件
sql += " WHERE \(filter)"
}
if db.open() {
return db.executeUpdate(sql, withArgumentsIn:[])
} else {
return false
}
}
// 获取数量(可附带条件)
class func count(filter: String = "") -> Int {
let db = SQLiteManager.shareManger().db
var sql = "SELECT COUNT(*) AS count FROM \(table)"
if !filter.isEmpty {
// 添加查询条件
sql += " WHERE \(filter)"
}
if let res = db.executeQuery(sql, withArgumentsIn: []) {
if res.next() {
return Int(res.int(forColumn: "count"))
} else {
return 0
}
}
return 0
}
// 保存当前对象数据
// * 如果模型主键为空或者使用该主键查不到数据则新增
// * 否则的话则更新
@discardableResult
func save() -> Bool{
let key = primaryKey()
let data = values()
var insert = true
let db = SQLiteManager.shareManger().db
if let rid = data[key] {
var val = "\(rid)"
if rid is String {
val = "'\(rid)'"
}
let sql = "SELECT COUNT(*) AS count FROM \(table) "
+ "WHERE \(primaryKey())=\(val)"
if db.open() {
if let res = db.executeQuery(sql, withArgumentsIn: []) {
if res.next() {
insert = res.int(forColumn: "count") == 0
}
}
}
}
let (sql, params) = getSQL(data:data, forInsert:insert)
// 执行SQL语句
if db.open() {
return db.executeUpdate(sql, withArgumentsIn: params ?? [])
} else {
return false
}
}
// 删除当天对象数据
@discardableResult
func delete() -> Bool{
let key = primaryKey()
let data = values()
let db = SQLiteManager.shareManger().db
if let rid = data[key] {
if db.open() {
let sql = "DELETE FROM \(table) WHERE \(primaryKey())=\(rid)"
return db.executeUpdate(sql, withArgumentsIn: [])
}
}
return false
}
// 通过反射获取对象所有有的属性和属性值
internal func values() -> [String:Any] {
var res = [String:Any]()
let obj = Mirror(reflecting:self)
processMirror(obj: obj, results: &res)
getValues(obj: obj.superclassMirror, results: &res)
return res
}
// 供上方方法(获取对象所有有的属性和属性值)调用
private func getValues(obj: Mirror?, results: inout [String:Any]) {
guard let obj = obj else { return }
processMirror(obj: obj, results: &results)
getValues(obj: obj.superclassMirror, results: &results)
}
// 供上方方法(获取对象所有有的属性和属性值)调用
private func processMirror(obj: Mirror, results: inout [String: Any]) {
for (_, attr) in obj.children.enumerated() {
if let name = attr.label {
// 忽略 table 和 db 这两个属性
if name == "table" || name == "db" {
continue
}
// 忽略人为指定的属性
if ignoredKeys().contains(name) ||
name.hasSuffix(".storage") {
continue
}
results[name] = unwrap(attr.value)
}
}
}
//将可选类型(Optional)拆包
func unwrap(_ any:Any) -> Any {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .optional {
return any
}
if mi.children.count == 0 { return any }
let (_, some) = mi.children.first!
return some
}
// 返回新增或者修改的SQL语句
private func getSQL(data:[String:Any], forInsert:Bool = true)
-> (String, [Any]?) {
var sql = ""
var params:[Any]? = nil
if forInsert {
sql = "INSERT INTO \(table)("
} else {
sql = "UPDATE \(table) SET "
}
let pkey = primaryKey()
var wsql = ""
var rid:Any?
var first = true
for (key, val) in data {
// 处理主键
if pkey == key {
if forInsert {
if val is Int && (val as! Int) == -1 {
continue
}
} else {
wsql += " WHERE " + key + " = ?"
rid = val
continue
}
}
// 设置参数
if first && params == nil {
params = [AnyObject]()
}
if forInsert {
sql += first ? "\(key)" : ", \(key)"
wsql += first ? " VALUES (?" : ", ?"
params!.append(val)
} else {
sql += first ? "\(key) = ?" : ", \(key) = ?"
params!.append(val)
}
first = false
}
// 生成最终的SQL
if forInsert {
sql += ")" + wsql + ")"
} else if params != nil && !wsql.isEmpty {
sql += wsql
params!.append(rid!)
}
return (sql, params)
}
// 返回建表时每个字段的sql语句
private func getColumnSQL(column:(key: String, value: Any)) -> String {
let key = column.key
let val = column.value
var sql = "'\(key)' "
if val is Int {
// 如果是Int型
sql += "INTEGER"
if key == primaryKey() {
sql += " PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE"
} else {
sql += " DEFAULT \(val)"
}
} else {
// 如果是其它类型
if val is Float || val is Double {
sql += "REAL DEFAULT \(val)"
} else if val is Bool {
sql += "BOOLEAN DEFAULT " + ((val as! Bool) ? "1" : "0")
} else if val is Date {
sql += "DATE"
} else if val is NSData {
sql += "BLOB"
} else {
// Default to text
sql += "TEXT"
}
if key == primaryKey() {
sql += " PRIMARY KEY NOT NULL UNIQUE"
}
}
return sql
}
}
extension SQLModelProtocol where Self: SQLModel {
// 根据完成的sql返回数据结果
static func rowsFor(sql: String = "") -> [Self] {
var result = [Self]()
let tmp = self.init()
let data = tmp.values()
let db = SQLiteManager.shareManger().db
let fsql = sql.isEmpty ? "SELECT * FROM \(table)" : sql
if let res = db.executeQuery(fsql, withArgumentsIn: []){
// 遍历输出结果
while res.next() {
let t = self.init()
for (key, _) in data {
if let val = res.object(forColumn: key) {
t.setValue(val, forKey:key)
}
}
result.append(t)
}
}else{
print("查询失败")
}
return result
}
// 根据指定条件和排序算法返回数据结果
static func rows(filter: String = "", order: String = "",
limit: Int = 0) -> [Self] {
var sql = "SELECT * FROM \(table)"
if !filter.isEmpty {
sql += " WHERE \(filter)"
}
if !order.isEmpty {
sql += " ORDER BY \(order)"
}
if limit > 0 {
sql += " LIMIT 0, \(limit)"
}
return self.rowsFor(sql:sql)
}
}
2 创建具体的实体类
//
// User.swift
// FMDBtest
//
// Created by 刁宏瑞 on 2019/12/11.
// Copyright © 2019 刁宏瑞. All rights reserved.
//
import Foundation
class User: SQLModel {
var userID: Int = -1
var name: String = ""
var sex: String = ""
var phone: String = ""
var age: Int = -1
/// 默认情况下,模型里的 id 属性则作为数据库表里的主键。如果想要其它属性作为主键,则需要重写覆盖 primaryKey() 方法:
override func primaryKey() -> String {
return "userID"
}
/// 默认情况下,模型里面所有属性都会创建对应的数据库表字段,如果想要某些属性不与数据库表进行映射,则需要重写覆盖 ignoredKeys() 方法:
override func ignoredKeys() -> [String] {
return ["age"]
}
}
3 增加数据样列(调用save保存对象 修改对象)
注意:数据库以及数据表我们事先不需要手动创建,模型初始化时会自动判断是否存在对应的表,没有的话会自动创建。
for i in 1..<30 {
let user = User()
user.name = "测试用户\(i)"
user.age = 100 + i
user.save()
}
如果模型对象主键为空或者该主键在表中查不到对应的记录,则为新增。
否则的话则为修改。
let user = User()
user.name = "夏天无泪"
user.age = 100
user.userID = 1
user.save()
4 删除数据
1)调用模型对象的 delete() 方法即可将该对象对应的数据删除(内部根据主键来判断)
let user = User()
user.userID = 2
user.delete()
2)也可以通过类方法 remove() 来批量删除数据:
// 删除User表所有的数据
User.remove()
// 根据指定条件删除User表的数据
User.remove(filter: "userID > 1 and age < 10")
5 获取记录数
1)通过类方法 count() 可以获取数据表中的所有记录数
let count = User.count()
print("记录数:\(count)")
2)我们也可根据指定条件来获取记录数:
let count = User.count(filter: "id > 5 and age < 107")
print("记录数:\(count)")
6 查询数据
1)通过类方法 rows() 可以获取数据表中的所有数据(会自动转换成对应的模型数组):
// 获取所有数据
let users = User.rows()
// 遍历输出结果
for user in users {
print(user.userID, user.name, user.age, user.sex)
}
2)rows() 方法还可指定查询条件、排序、以及限制数量。
// 根据条件获取数据
let users = User.rows(filter: "userID > 1 and age < 10", order: "age desc", limit: 3)
// 遍历输出结果
for user in users {
print(user.userID, user.name, user.age, user.sex)
}
3)我们还可以使用 rowsFor() 方法来通过完整的 sql 查询数据(同样也会自动转换成对应的模型数组):
// 通过sql获取数据 查询5个用户信息
let users = User.rowsFor(sql: "select * from User limit 5")
// 遍历输出结果
for user in users {
print(user.userID, user.name, user.age,user.sex)
}