一个纯 Swift 客户端,实现了 MySQL 协议。它不依赖于 libmysql。
let package = Package(
name: "MyApp",
dependencies: [
.package(url: "https://github.com/noppoMan/SwiftMysql.git", .upToNextMajor(from: "0.1.0"))
],
)
let url = URL(string: "mysql://:3306")
let con = try Connection(url: url!, user: "root", password: "password", database: "swift_mysql")
let result = try con.query("selct * from users")
if let rows = result.asRows() {
for row in rows {
print(row) // ["id": 1, "name": "Luke", "email": "test@example.com"]
}
}
您可以轻松地使用预处理语句,如下所示。
let url = URL(string: "mysql://:3306")
let con = try Connection(url: url!, user: "root", password: "password", database: "swift_mysql")
let result = try con.query("selct * from books where published_at > ? and category_id = ?", [2017, 3])
if let rows = result.asRows() {
for row in rs {
print(row)
}
}
这个模块还提供了内置的连接池,使用 ConnectionPool(url:user:database:minPoolSize:maxPoolSize)
,而不是逐个创建和管理连接。
let pool = try ConnectionPool(
url: URL(string: "mysql://:3306")!,
user: "root",
database: "swift_mysql",
minPoolSize: 3,
maxPoolSize: 10
)
try pool.query("select * from users") // the connection is released after finishing query.
当内部使用的连接数达到 maxPoolSize
,并且调用 query
时,将抛出 failedToGetConnectionFromPool
错误。但这错误是可恢复的,所有开发者都可以像下面这样重试执行 query
。
do {
try pool.query("select * from users")
} catch ConnectionPoolError.failedToGetConnectionFromPool {
// may need to wait a moment...
// try again.
try pool.query("select * from users")
}
在连接级别提供了简单的事务支持
如果事务块中的程序在没有抛出错误的情况下完成,事务应该自动提交。
try con.transaction {
$0.query("insert into users (name, email), value (\"Foo\", \"foo@example.com\")")
}
如果事务块中抛出错误,则应执行 rollback
。
try con.transaction {
throw FooError
}
有时您可能想要选择大量行,并在接收到每一行时对其进行处理。可以这样做:
let result = try con.query("selct * from large_tables")
if let resultFetcher = result.asResultSet() {
print(resultFetcher.columns) // [String]
// resultFetcher.rows is a infinity Sequence
// you can loop it until the Mysql sends EOF packet.
for row in resultFetcher.rows {
print(row)
}
}
一旦调用 close
方法,MySQL 连接将被安全终止。
try con.close()
SwiftMysql 通过 AsyncConnection
支持非阻塞查询。非阻塞意味着使用操作系统原生异步系统调用(epoll/kqueue)的事件驱动非阻塞 I/O。它不通过工作线程进行并发执行。
目前,所有非阻塞特性都是线程不安全的。因此,您应该在单线程上使用它们。
您可以使用 AsyncConnection(url:user:password:database:queue)
异步连接到 MySQL。
一旦调用 AsyncConnection
的初始化器,连接将自动在指定的线程(队列)上打开。然后,您的所有操作(查询)都将按顺序排队和处理。该线程将在自己的线程上创建事件循环,以观察连接的文件描述符。
import Foundation
import SwiftMysql
let url = URL(string: "mysql://:3306")!
let con = try SwiftMysql.AsyncConnection(
url: url,
user: "root",
password: nil,
database: "swift_mysql"
)
con.onConnect {
print("connected to \(url)")
}
con.onError { error in
print("Error: \(error)")
}
con.query("select * from users where id = 1") { result in
if let error = result.asError() {
print(error)
return
}
result.asRows {
print($0) // [["id": 1, "name": "Jack....]]
}
}
con.query("select * from users where id = 2") { result in
if let error = result.asError() {
print(error)
return
}
result.asRows {
print($0) // [["id": 2, "name": "Tonny....]]
}
}
RunLoop.main.run()
如果您不关心运行事件循环的线程,它会在内部由 DispatchQueue(attributes: .serial)
自动确定。
或者,您可以像下面这样通过初始化器的 queue
标签来提供它。
let url = URL(string: "mysql://:3306")!
let con = try SwiftMysql.AsyncConnection(
url: url,
user: "root",
password: nil,
database: "swift_mysql",
queue: DispatchQueue.main
)
con.connect {
print(Thread.current == Thread.main) // true
}
con.query("...") { _ in
print(Thread.current == Thread.main) // true
}
您可以使用 ResultSetEvent
来提高获取记录的内存效率。
ResultSetEvent
提供了两种方法来流式获取字段和行。
onFields
:当接收到所有字段数据包时调用 onFields。onRow
:当接收到每个行数据包时调用 onRow。con.query("select * from users limit ?", bindParams: [100]) { result in
let rs = result.asResultSet() // get ResultSetEvent
rs.onFields { fields in
print(fields) // ["id", "name", "age"...]
}
event.onRow { row in
print(row) // [[1, "Jack", 35...]]
}
}
您还可以将连接池用于使用 AsyncConnectionPool
的非阻塞查询。用法与同步版本大致相同。当同时使用的连接数达到 maxPoolSize
时,下一个查询队列应等待连接可用。
let pool = try AsyncConnectionPool(
url: url,
user: "root",
database: "swift_mysql",
minPoolSize: 2,
maxPoolSize: 10
)
pool.onReady {
print("The initial connections are ready")
}
pool.onNewConnectionIsReady {
print("new Connection is ready")
}
// Uses a existing connection
pool.query("select * from users where id = ?", bindParams: [1]) { result in
result.asRows()
// connection will be released automatically,
// when the all of packets of this query are received.
}
// Uses a existing connection
pool.query("select * from users where id = ?", bindParams: [2]) { result in
result.asRows()
}
// May create a new connection asynchronously.
// Depends on the timing of first query finished.
pool.query("select * from users where id = ?", bindParams: [3]) { result in
result.asRows()
}
pool.transaction { error, con in
con?.query("insert into ....") { result in
if let error = result.asError() {
con?.rollback { _ in
done(error)
}
return
}
con?.query("update users set name = ....") { result in
if let error = result.asError() {
con?.rollback { _ in
done(error)
}
return
}
con?.commit { _ in
done(nil)
}
}
}
}
SwiftMysql 在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE。