2017-10-20 36 views
4

首先,这些都是我的功能:斯威夫特sqlite3的语法和绑定

  1. 插入功能

    func insert(book : Book) throws -> Bool { 
        var insertPointer: OpaquePointer? = nil 
        let query = "INSERT INTO BOOK (bookName, bookAuthor, bookDesc, bookDate, bookImg, createdBy) VALUES (?, ?, ?, ?, ?, ?)" 
    
        defer{ 
         sqlite3_finalize(insertPointer) 
        } 
    
        if sqlite3_prepare_v2(db, query, -1, &insertPointer, nil) == SQLITE_OK { 
         sqlite3_bind_text(insertPointer, 1, book.bookTitle, -1, nil) 
         sqlite3_bind_text(insertPointer, 2, book.bookAuthor, -1, nil) 
         sqlite3_bind_text(insertPointer, 3, book.bookDesc, -1, nil) 
         //sqlite3_bind_date(insertPointer, 4, book.bookDate,nil) 
         //sqlite3_bind_image(insertPointer, 5, book.bookImg, -1, nil) 
         sqlite3_bind_text(insertPointer, 6, book.createdBy, -1, nil) 
    
         guard sqlite3_step(insertPointer) == SQLITE_DONE else { 
          throw SQLiteError.Step(message: errorMessage) 
         } 
        } else { 
         throw SQLiteError.Prepare(message: errorMessage) 
        } 
    
        return true 
    } 
    
  2. 更新功能

    func update(book : Book) throws -> Bool { 
        var updatePointer: OpaquePointer? = nil 
        var query = "UPDATE Book SET bookName = ?, bookAuthor = ?, bookDesc = ?, bookDate = ?, bookImg = ?, createdBy = ?, WHERE bookId = ?" 
    
        defer{ 
         sqlite3_finalize(updatePointer) 
        } 
    
        if sqlite3_prepare_v2(db, query, -1, &updatePointer, nil) == SQLITE_OK { 
    
         sqlite3_bind_text(updatePointer, 2, book.bookAuthor, -1, nil) 
         sqlite3_bind_text(updatePointer, 3, book.bookDesc, -1, nil) 
         //sqlite3_bind_date(updatePointer, 4, book.bookDate,nil) 
         //sqlite3_bind_image(updatePointer, 5, book.bookImg, -1, nil) 
         sqlite3_bind_text(updatePointer, 6, book.createdBy, -1, nil) 
         sqlite3_bind_text(updatePointer, 7, book.bookId, -1, nil) 
         guard sqlite3_step(updatePointer) == SQLITE_DONE else { 
          throw SQLiteError.Step(message: errorMessage) 
         } 
        } else { 
         throw SQLiteError.Prepare(message: errorMessage) 
        } 
    
        return true 
    } 
    
  3. 删除功能

    func delete(book : Book) throws -> Bool { 
        var deletePointer: OpaquePointer? = nil 
        var query = "DELETE FROM Book WHERE bookId = ?" 
    
        defer{ 
         sqlite3_finalize(deletePointer) 
        } 
    
        if sqlite3_prepare_v2(db, query, -1, &deletePointer, nil) == SQLITE_OK { 
         sqlite3_bind_text(updatePointer, 1, book.bookId, -1, nil) 
         guard sqlite3_step(deletePointer) == SQLITE_DONE else { 
          throw SQLiteError.Step(message: errorMessage) 
         } 
        } else { 
         throw SQLiteError.Prepare(message: errorMessage) 
        } 
    
        return true 
    } 
    

而且我有一个Book类是这样的:

class Book{ 
    var bookId : Int 
    var bookImg : Data 
    var bookTitle : String 
    var bookAuthor : String 
    var bookDesc : String 
    var bookDate : Date 
    var createdBy : String 

    init(bookId : Int, bookImg : Data, bookTitle : String, bookAuthor : String, bookDesc : String, bookDate : Date, createdBy : String){ 
     self.bookId = bookId 
     self.bookImg = bookImg 
     self.bookTitle = bookTitle 
     self.bookAuthor = bookAuthor 
     self.bookDesc = bookDesc 
     self.bookDate = bookDate 
     self.createdBy = createdBy 
    } 
} 

我是新来的斯威夫特和SQLite。我的问题是:

  1. 我是否正确地使用参数绑定?

  2. 如何将DataDate类型绑定到SQLite查询中? (上面代码中的注释行)

任何帮助将不胜感激!

+0

SQLiteError类型来自哪里? errorMessage定义在哪里? –

+0

SQLiteError是枚举在同一个文件中定义的。 errorMessage与类中的函数(不是Book类)一起定义。 – Jeff

回答

5

你问:

  1. 我说得对不对与参数绑定做什么呢?

重要。

  • 当绑定字符串,它可能是谨慎使用SQLITE_TRANSIENT作为最后一个参数来sqlite3_bind_textsqlite3_bind_blob,这里定义的:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self) 
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self) 
    
  • 当绑定bookId,要使用sqlite3_bind_int64

  • delete你指的是updatePointer。将其更改为deletePointer

  • 您应该正在检查这些sqlite3_bind_xxx返回码,并且如果它们不是SQLITE_OK也会发出错误。

你接着问:

  • 如何绑定数据和日期型到SQLite的查询?(在上面的代码中的注释行)
  • 重新日期型,SQLite不具有天然日期类型(见http://sqlite.org/datatype3.html)。或者:

    • 如果你不需要毫秒,使用ISODateFormatter建立一个字符串,并绑定串;
    • 如果您需要毫秒,使用DateFormatteryyyy-MM-dd'T'HH:mm:ss.SSSXdateFormat,一个localeLocale(identifier: "en_US_POSIX"),以及TimeZone(secondsFromGMT: 0)一个timeZone,再存储和检索日期为字符串,并将其转换;或
    • 使用timeIntervalSince1970Date,并将其插入为sqlite3_bind_double

    以前的字符串替代方法最容易使用,并且在第三方工具中直观地检查数据库时很容易使用。 timeIntervalSince1970可以说是更有效一些,但它意味着如果查看第三方SQLite工具中的列,则需要使用unixepoch将double转换为可理解的日期,这可能有点麻烦。这是效率与可用性的平衡。

    重新编号Data,插入使用sqlite3_bind_blob


    一对夫妇最终未成年人意见:

    • sqlite3_prepare_v2之前,您都推迟sqlite3_finalize。你应该defersqlite3_prepare_v2。如果准备成功,您应该只是最后确定,而不是失败。

    • 使用WHERE子句进行更新时,可能需要检查sqlite3_changes以查看是否更改了任何记录。对于标识符更新,如果没有更新/删除,我更改了函数以抛出错误。

    • 这些函数中的几个定义为抛出错误以及返回布尔值。对于没有意义的更新/删除函数(因为我们使用错误来知道它是否成功,使布尔返回值成为冗余)。所以我删除了Bool返回类型。对于其他函数(例如SELECT例程),返回值显然有意义,但不适用于这些通过/失败更新例程。

    • 对于Book属性,我删除了book前缀。在SQL中有这个前缀是有意义的(它使得连接查询更容易编写),但是它在Swift类型中是多余的。您通常仅在需要消除歧义时才使用此类前缀(例如,bookDescription,以避免与CustomStringConvertible财产description混淆)。


    不管怎么说,拉一起,你喜欢的东西:

    var dateFormatter: DateFormatter = { 
        let _formatter = DateFormatter() 
        _formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX" 
        _formatter.locale = Locale(identifier: "en_US_POSIX") 
        _formatter.timeZone = TimeZone(secondsFromGMT: 0) 
        return _formatter 
    }() 
    
    var errorMessage: String { return String(cString: sqlite3_errmsg(db)) } 
    
    func insert(book: Book) throws { 
        var statement: OpaquePointer? = nil 
        let query = "INSERT INTO book (bookName, bookAuthor, bookDesc, bookDate, bookImg, createdBy) VALUES (?, ?, ?, ?, ?, ?)" 
    
        guard sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK else { 
         throw SQLiteError.prepare(message: errorMessage) 
        } 
    
        defer { sqlite3_finalize(statement) } 
    
        guard sqlite3_bind_text(statement, 1, book.title, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 2, book.author, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 3, book.bookDescription, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 4, dateFormatter.string(from: book.createDate), -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard book.image.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> Int32 in 
         sqlite3_bind_blob(statement, 5, bytes, Int32(book.image.count), SQLITE_TRANSIENT) 
        }) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 6, book.createdBy, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_step(statement) == SQLITE_DONE else { 
         throw SQLiteError.step(message: errorMessage) 
        } 
    } 
    
    func update(book: Book) throws { 
        var statement: OpaquePointer? = nil 
        let query = "UPDATE Book SET bookName = ?, bookAuthor = ?, bookDesc = ?, bookDate = ?, bookImg = ?, createdBy = ?, WHERE bookId = ?" 
    
        guard sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK else { 
         throw SQLiteError.prepare(message: errorMessage) 
        } 
    
        defer { sqlite3_finalize(statement) } 
    
        guard sqlite3_bind_text(statement, 2, book.author, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 3, book.bookDescription, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 4, dateFormatter.string(from: book.createDate), -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard book.image.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> Int32 in 
         sqlite3_bind_blob(statement, 5, bytes, Int32(book.image.count), SQLITE_TRANSIENT) 
        }) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_text(statement, 6, book.createdBy, -1, SQLITE_TRANSIENT) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_bind_int64(statement, 7, Int64(book.id)) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_step(statement) == SQLITE_DONE else { 
         throw SQLiteError.step(message: errorMessage) 
        } 
    
        guard sqlite3_changes(db) > 0 else { 
         throw SQLiteError.noDataChanged 
        } 
    } 
    
    func delete(book: Book) throws { 
        var statement: OpaquePointer? = nil 
        let query = "DELETE FROM Book WHERE bookId = ?" 
    
        guard sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK else { 
         throw SQLiteError.prepare(message: errorMessage) 
        } 
    
        defer { sqlite3_finalize(statement) } 
    
        guard sqlite3_bind_int64(statement, 1, Int64(book.id)) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_step(statement) == SQLITE_DONE else { 
         throw SQLiteError.step(message: errorMessage) 
        } 
    
        guard sqlite3_changes(db) > 0 else { 
         throw SQLiteError.noDataChanged 
        } 
    } 
    
    func select(bookId: Int) throws -> Book { 
        var statement: OpaquePointer? = nil 
        let query = "SELECT bookId, bookName, bookAuthor, bookDesc, bookDate, bookImg, createdBy FROM Book WHERE bookId = ?" 
    
        guard sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK else { 
         throw SQLiteError.prepare(message: errorMessage) 
        } 
    
        defer { sqlite3_finalize(statement) } 
    
        guard sqlite3_bind_int64(statement, 1, Int64(bookId)) == SQLITE_OK else { 
         throw SQLiteError.bind(message: errorMessage) 
        } 
    
        guard sqlite3_step(statement) == SQLITE_ROW else { 
         throw SQLiteError.step(message: errorMessage) 
        } 
    
        return try book(for: statement) 
    } 
    
    func selectAll() throws -> [Book] { 
        var statement: OpaquePointer? = nil 
        let query = "SELECT bookId, bookName, bookAuthor, bookDesc, bookDate, bookImg, createdBy FROM Book" 
    
        guard sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK else { 
         throw SQLiteError.prepare(message: errorMessage) 
        } 
    
        defer { sqlite3_finalize(statement) } 
    
        var books = [Book]() 
    
        var rc: Int32 
        repeat { 
         rc = sqlite3_step(statement) 
         guard rc == SQLITE_ROW else { break } 
         books.append(try book(for: statement)) 
        } while rc == SQLITE_ROW 
    
        guard rc == SQLITE_DONE else { 
         throw SQLiteError.step(message: errorMessage) 
        } 
    
        return books 
    } 
    
    func book(for statement: OpaquePointer?) throws -> Book { 
        let bookId = Int(sqlite3_column_int64(statement, 0)) 
    
        guard let bookNameCString = sqlite3_column_text(statement, 1) else { 
         throw SQLiteError.column(message: errorMessage) 
        } 
        let bookName = String(cString: bookNameCString) 
    
        guard let bookAuthorCString = sqlite3_column_text(statement, 2) else { 
         throw SQLiteError.column(message: errorMessage) 
        } 
        let bookAuthor = String(cString: bookAuthorCString) 
    
        guard let bookDescCString = sqlite3_column_text(statement, 3) else { 
         throw SQLiteError.column(message: errorMessage) 
        } 
        let bookDesc = String(cString: bookDescCString) 
    
        guard let bookDateCString = sqlite3_column_text(statement, 4) else { 
         throw SQLiteError.column(message: errorMessage) 
        } 
        guard let bookDate = dateFormatter.date(from: String(cString: bookDateCString)) else { 
         throw SQLiteError.invalidDate 
        } 
    
        let bookImgCount = Int(sqlite3_column_bytes(statement, 5)) 
        guard bookImgCount > 0 else { 
         throw SQLiteError.missingData 
        } 
        guard let bookImgBlog = sqlite3_column_blob(statement, 5) else { 
         throw SQLiteError.column(message: errorMessage) 
        } 
        let bookImg = Data(bytes: bookImgBlog, count: bookImgCount) 
    
        guard let createdByCString = sqlite3_column_text(statement, 6) else { 
         throw SQLiteError.column(message: errorMessage) 
        } 
        let createdBy = String(cString: createdByCString) 
    
        return Book(id: bookId, image: bookImg, title: bookName, author: bookAuthor, bookDescription: bookDesc, createDate: bookDate, createdBy: createdBy) 
    } 
    

    有了这些定义:

    struct Book { 
        var id: Int 
        var image: Data 
        var title: String 
        var author: String 
        var bookDescription: String // this is the only one where I kept the `book` prefix, simply because `description` is a reserved name 
        var createDate: Date 
        var createdBy: String 
    } 
    
    enum SQLiteError: Error { 
        case open(result: Int32) 
        case exec(message: String) 
        case prepare(message: String) 
        case bind(message: String) 
        case step(message: String) 
        case column(message: String) 
        case invalidDate 
        case missingData 
        case noDataChanged 
    } 
    

    由于斯威夫特3,我更喜欢小写enum值。

    +0

    每个使用Swift的纯SQLite的人都应该阅读https://stackoverflow.com/a/28642293/1187415(现在也正式成为“伟大的答案”) –

    +0

    感谢Rob!我意识到我错过了一个问题。我如何在SELECT查询中解码它们?特别是当我的数据库有日期和Blob类型。如何将SQLite.Date转换为Swift.Date和SQLite.Blob到Swift.Data中? – Jeff

    +0

    如果使用'dateFormatter'将日期转换为字符串,如上所示,当检索它时,可以使用'dateFormatter.date(from:)'将从'sqlite3_column_text'检索到的字符串转换为日期。为了检索'Data',使用'sqlite3_column_bytes'来获取'Data'的'count',使用'sqlite3_column_blob'获取'bytes',并使用'Data(bytes:count)'来构建'Data对象。 – Rob