经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Swift » 查看文章
【Swift 4.0】扩展 WCDB 支持 SQL 语句
来源:cnblogs  作者:农民伯伯  时间:2018/9/25 20:29:06  对本文有异议

 

 

前言

  入坑 wcdb 有两个月了,整体来说还是很不错的,具体优点可以参考文档说明,由于官方明确说明不支持 SQL 只好自己写一个扩展支持一下了 ??

 

声明
  欢迎转载,但请保留文章原始出处:)
  博客园:http://www.cnblogs.com
  农民伯伯: http://over140.cnblogs.com

 

正文

  一、功能实现

  fork 一份源码然后把下面代码加入源码(有些类限制了访问作用域)

  SelectSQL.swift

  1. import Foundation
  2.  
  3.     extension Database {public func prepareSelectSQL(on propertyConvertibleList: [PropertyConvertible], sql: String, values: [ColumnEncodableBase] = []) throws -> SelectSQL {return try SelectSQL(with: self, on: propertyConvertibleList, sql: sql, values: values)
  4.         }
  5.  
  6.     }public final class SelectSQL {private final var core: Core
  7.         final var optionalRecyclableHandleStatement: RecyclableHandleStatement?final var statement: StatementSelectSQLprivate let keys: [CodingTableKeyBase]private let values: [ColumnEncodableBase]private lazy var decoder = TableDecoder(keys, on: optionalRecyclableHandleStatement!)
  8.  
  9.         init(with core: Core, on propertyConvertibleList: [PropertyConvertible], sql: String, values: [ColumnEncodableBase]) throws {//TODO: Use generic to check all coding table keys conform to same root typekeys = propertyConvertibleList.asCodingTableKeys()
  10.             self.statement = StatementSelectSQL(sql: sql)
  11.             self.core = core
  12.             self.values = values
  13.         }private func bindValues() throws {
  14.             guard values.count > 0 else {return}
  15.             let handleStatement = try lazyHandleStatement()for idx in 0..<values.count {
  16.                 handleStatement.bind(values[idx].archivedFundamentalValue(), toIndex: idx + 1)
  17.             }
  18.         }
  19.  
  20.         deinit {try? finalize()
  21.         }/// Get all selected objects according to the `CodingTableKey`.////// - Returns: Table decodable objects according to the `CodingTableKey`/// - Throws: `Error`public func allObjects() throws -> [Any] {
  22.             let rootType = keys[0].rootType as? TableDecodableBase.Type
  23.             assert(rootType != nil, "\(keys[0].rootType) must conform to TableDecodable protocol.")
  24.             var objects: [Any] = []try bindValues()while try next() {
  25.                 objects.append(try rootType!.init(from: decoder))
  26.             }return objects
  27.         }/// Get all selected objects.////// - Parameter type: Type of table decodable object/// - Returns: Table decodable objects./// - Throws: `Error`public func allObjects<Object: TableDecodable>(of type: Object.Type = Object.self) throws -> [Object] {
  28.             assert(keys is [Object.CodingKeys], "Properties must belong to \(Object.self).CodingKeys.")
  29.             var objects: [Object] = []try bindValues()while try next() {
  30.                 objects.append(try Object.init(from: decoder))
  31.             }return objects
  32.         }
  33.  
  34.         final func finalize() throws {if let recyclableHandleStatement = optionalRecyclableHandleStatement {try recyclableHandleStatement.raw.finalize()
  35.                 optionalRecyclableHandleStatement = nil
  36.             }
  37.         }
  38.  
  39.         final func lazyHandleStatement() throws -> HandleStatement {if optionalRecyclableHandleStatement == nil {
  40.                 optionalRecyclableHandleStatement = try core.prepare(statement)
  41.             }return optionalRecyclableHandleStatement!.raw
  42.         }//Since `next()` may throw errors, it can't conform to `Sequence` protocol to fit a `for in` loop.        @discardableResultpublic final func next() throws -> Bool {do {return try lazyHandleStatement().step()
  43.             } catch let error {try? finalize()throw error
  44.             }
  45.         }
  46.  
  47.     }
  48.  
  49.     extension SelectSQL: CoreRepresentable {/// The tag of the related database.public final var tag: Tag? {return core.tag
  50.         }/// The path of the related database.public final var path: String {return core.path
  51.         }
  52.     }

 

  StatementSelectSQL.swift

  1.     import Foundationpublic final class StatementSelectSQL: Statement {public private(set) var description: String = ""public var statementType: StatementType {return .select
  2.         }public init(sql: String) {
  3.             self.description = sql
  4.         }
  5.     }

 

    UpdateSQL.swift

  1.     import Foundation
  2.  
  3.     extension Database {public func prepareUpdateSQL(sql: String) throws -> UpdateSQL {return try UpdateSQL(with: self, sql: sql)
  4.         }
  5.  
  6.     }/// The chain call for updatingpublic final class UpdateSQL {private var core: Coreprivate let statement: StatementUpdateSQL/// The number of changed rows in the most recent call./// It should be called after executing successfullypublic var changes: Int?init(with core: Core, sql: String) throws {
  7.             self.core = core
  8.             self.statement = StatementUpdateSQL(sql: sql)
  9.         }/// Execute the update chain call with row.////// - Parameter row: Column encodable row/// - Throws: `Error`public func execute(with row: [ColumnEncodableBase?] = []) throws {
  10.             let recyclableHandleStatement: RecyclableHandleStatement = try core.prepare(statement)
  11.             let handleStatement = recyclableHandleStatement.rawfor (index, value) in row.enumerated() {
  12.                 let bindingIndex = index + 1handleStatement.bind(value?.archivedFundamentalValue(), toIndex: bindingIndex)
  13.             }try handleStatement.step()
  14.             changes = handleStatement.changes
  15.         }
  16.     }
  17.  
  18.     extension UpdateSQL: CoreRepresentable {/// The tag of the related database.public var tag: Tag? {return core.tag
  19.         }/// The path of the related database.public var path: String {return core.path
  20.         }
  21.     }

 

    StatementUpdateSQL.swift  

  1.     import Foundationpublic final class StatementUpdateSQL: Statement {public private(set) var description: String = ""public var statementType: StatementType {return .update
  2.         }public init(sql: String) {
  3.             self.description = sql
  4.         }
  5.     }

 

  二、使用 SQL 查询或更新数据

    2.1  查询

  1.       database.prepareSelectSQL(User.Properties.Id, "SELECT id FROM users where id = ?", values: ["1"])

      需要特别注意的是如果返回 Codable 数据,SELECT 字段的顺序必须要和 CodingKeys 里的顺序一致,否则数据会填充乱,但是用 WINQ 不会有这个问题。

    2.2  更新

  1.                 let updateSQL = try database.prepareUpdateSQL(sql: "UPDATE conversations SET last_message_id = (select id from messages where conversation_id = ? order by created_at DESC limit 1) WHERE conversation_id = ?")
  2.                 try updateSQL.execute(with: [conversationId, conversationId])

  

 结束

  目前用了一段时间没有发现什么问题,除了前面那个注意顺序问题,WINQ 就是拼 SQL 语句搞不懂官方为啥不直接支持一个,就算能支持所有 SQL 改起来也很麻烦,而且代码量很多。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号