你问:
- 我说得对不对与参数绑定做什么呢?
重要。
当绑定字符串,它可能是谨慎使用SQLITE_TRANSIENT
作为最后一个参数来sqlite3_bind_text
和sqlite3_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
建立一个字符串,并绑定串;
- 如果您需要毫秒,使用
DateFormatter
与yyyy-MM-dd'T'HH:mm:ss.SSSX
dateFormat
,一个locale
的Locale(identifier: "en_US_POSIX")
,以及TimeZone(secondsFromGMT: 0)
一个timeZone
,再存储和检索日期为字符串,并将其转换;或
- 使用
timeIntervalSince1970
的Date
,并将其插入为sqlite3_bind_double
。
以前的字符串替代方法最容易使用,并且在第三方工具中直观地检查数据库时很容易使用。 timeIntervalSince1970
可以说是更有效一些,但它意味着如果查看第三方SQLite工具中的列,则需要使用unixepoch
将double转换为可理解的日期,这可能有点麻烦。这是效率与可用性的平衡。
重新编号Data
,插入使用sqlite3_bind_blob
。
一对夫妇最终未成年人意见:
你sqlite3_prepare_v2
之前,您都推迟sqlite3_finalize
。你应该defer
吧后sqlite3_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
值。
SQLiteError类型来自哪里? errorMessage定义在哪里? –
SQLiteError是枚举在同一个文件中定义的。 errorMessage与类中的函数(不是Book类)一起定义。 – Jeff