SwiftKnex

一个适用于 Mac 和 Linux 的 Mysql 原生客户端和查询构建器。
这个库由 Prorsum 驱动。

SwiftKnex 旨在用于我公司的生产服务。

特性

待办事项

贡献

欢迎并鼓励所有开发者为 SwiftKnex 做出贡献。

要为 SwiftKnex 贡献功能或想法,请提交一个 issue! 如果你发现错误,当然你可以创建 PR(s) 目录。

查询构建器参考

初始化库

let config = KnexConfig(
    host: "localhost",
    user: "user",
    database: "my_database",
    isShowSQLLog: true
)

let con = try KnexConnection(config: config)
let knex = con.knex()

查询构建器

Select (选择)

where equal (等于条件)

let results = try knex.table("users").where("id" == 1).fetch()
print(results)

where between and limit and offset (范围条件、限制和偏移)

你可以像 SQL 一样链式调用条件子句,这非常具有表现力。

let results = try knex
                  .table("users")
                  .where(between("id", 1, 1000))
                  .limit(10)
                  .offset(10)
                  .fetch()

print(result)

join (连接)

let results = try knex
                  .table("users")
                  .join("payments")
                  .on("users.id" == "payment.user_id")
                  .where("users.id" == 1)
                  .limit(10)
                  .offset(10)
                  .fetch()

print(results)

count (计数)

let results = try knex
                  .select(count("id").as("count"))
                  .table("users")
                  .fetch()

print(results)

基于优先级的 where 条件

let results = try knex
                    .table("users")
                    .where(("country" == "Japan" && "age" > 20) || "country" == "USA")

print(results)

子查询

从子查询结果中获取所有行。

let results = try knex
                    .table(
                        Table(
                            QueryBuilder()
                              .table("users")
                              .where("country" == "USA")
                            )
                        ).as("t1")
                    )
                    .where("t1.id" == 1)

print(results)

使用子查询获取的 id 作为 in 子句的参数。

let results = try knex
                    .table("users")
                    .where(SwiftKnex.in("id",
                        QueryBuilder()
                          .select(col("id"))
                          .table("t1")
                          .where("country" == "USA")
                    )).fetch()

print(results)

运算符、条件和子句

运算符

SwiftKnex 可以将比较公式作为函数求值。 如果你在 where 子句中输入 "id" == 1,它会被视为 function (不是 BOOL) 并转换为 SQL 比较字面量。

SwiftKnex Mysql
where("id" == 1) where id = 1
where("id" > 1) where id > 1
where("id" >= 1) where id >= 1
where("id" < 1) where id < 1
where("id" <= 1) where id <= 1
where("id" != 1) where id != 1

条件

SwiftKnex 提供条件作为函数。 以下是当前可用的条件列表。

子句

当然,它支持其他子句。 你可以使用 knex() 的方法链来使用它们。

使用 Entity 协议进行类型安全的查询

定义你的实体并确认 Entity 协议,并将行作为你指定的类型获取。

struct User: Entity, Serializable {
    let id: Int
    let name: String
    let email: String
    let age: Int
    let country: String?

    init(row: Row) throws {
        self.id = row["id"] as! Int
        self.name = row["name"] as! String
        self.email = row["email"] as! String
        self.age = row["age"] as! Int
        self.country = row["country"] as? String
    }

    func serialize() throws -> [String: Any] {
        return [
            "name": name,
            "email": email,
            "age": age,
            "country": country
        ]
    }
}

// fetch rows as User(s)
let users: [User] = try! con.knex()
                            .table("users")
                            .where("country" == "Japan")
                            .fetch()

print(users.first)

// Insert User(should confirm Serializable)
let result = try! con.knex().insert(into: "users", values: user)

print(result?.insertId)

Insert (插入)

let result = try knex().insert(into: "users", values: ["id": 1, "name": "bar"])

print(result.affectedRows)

Update (更新)

let result = try knex().table("users").where("id" == 1).update(sets: ["name": "foobar"])

print(result.affectedRows)

Delete (删除)

let result = try knex().table("users").where("id" == 1).delete()

print(result.affectedRows)

Transaction (事务)

do {
    try con.knex().transaction { trx in // BEGIN TRANSCTION
        try con.knex().table("users").where("id" == 1).update(sets: ["name": "foo"], trx: trx)
        try con.knex().table("users").where("id" == 2).update(sets: ["name": "bar"], trx: trx)
        try con.knex().table("users").where("id" == 3).update(sets: ["name": "foobar"], trx: trx)
    }
    // COMMIT
} catch {
    // ROLLBACK
}

DDL (数据定义语言)

Create (创建)

let create = Create(table: "users", fields: [
    Schema.integer("id").asPrimaryKey().asAutoIncrement(),
    Schema.string("name"),
    Schema.string("email").asUnique(),
    Schema.datetime("last_logined_at").asIndex()
])
.hasTimeStamps() // add created_at and updated_at

try knex().execRaw(sql: create.toDDL())

Schema.Field 参考

Schema 类型比较

SwiftKnex Mysql 类型
Schema.string VARCHAR
Schema.integer INT
Schema.bigInteger BIGINT
Schema.dateTime DATETIME
Schema.text TEXT
Schema.mediumText MEDIUMTEXT
Schema.float FLOAT
Schema.double DOUBLE
Schema.boolean TINYINT(1)
Schema.json JSON

用于添加字段属性的函数

Drop (删除)

let drop = Drop(table: "users")
try knex().execRaw(sql: drop.toDDL())

Raw (原始 SQL)

你可以使用 SwiftKnex 执行原始 SQL。

try knex().execRaw(sql: "SELECT * from users where id = ?", params: [1])

Migration (迁移)

SwiftKnex 支持数据库迁移和回滚功能。

流程

1. 将 SwiftKnex 安装到你的项目中。

Package.swift

import PackageDescription

let package = Package(
    name: "MyApp",
    dependencies: [
        .Package(url: "https://github.com/noppoMan/SwiftKnex.git", majorVersion: 0, minor: 1)
    ]
)

运行 swift build

$ swift build

然后,SwiftKnexMigration 可执行二进制文件将在 .build/debug 目录中创建。

2. 创建迁移文件

下一步是在你的 Sources/Migration 目录中使用 ./build/debug/Migration create {ResourceName} 创建迁移类文件。

这是一个创建 CreateUser 迁移文件的示例。

.build/debug/SwiftKnexMigration create CreateUser

#
# Created /YourProject/Sources/Migration/20170116015823_CreateUser.swift
#

3. 编辑你的迁移文件

创建迁移类文件后,像下面这样编辑它。

创建的迁移类具有以下方法。

up (向上迁移)

在 migrate:latest 上执行

down (向下迁移)

在 migrate:rollback 上执行

import SwiftKnex
import Foundation

class Migration_20170116015823_CreateUser: Migratable {

    var name: String {
        return "\(Mirror(reflecting: self).subjectType)"
    }

    func up(_ migrator: Migrator) throws {
       let create = Create(
           table: "users",
           fields: [
               Schema.integer("id").asPrimaryKey().asAutoIncrement(),
               Schema.string("name").asIndex().asNotNullable(),
               Schema.string("email").asUnique().asNotNullable()
           ])
           .hasTimestamps()
           .index(columns: ["name", "email"], unique: true)

       try migrator.run(create)
    }

    func down(_ migrator: Migrator) throws {
        try migrator.run(Drop(table: "users"))
    }
}

4. 在 {$PROJ}/Sources/Migration 中创建 main.swift

{$PROJ}/Sources/Migration 目录中创建 main.swift,该目录由前一节中的 Migrate create 创建。
然后将以下代码复制/粘贴到你的 {$PROJ}/Sources/Migration/main.swift 中,然后将 knexMigrations 数组中的类名替换为正确的名称,并根据你的环境更改数据库配置。

你需要在每次创建迁移资源时将新的类名添加到 knexMigrations 中。

main.swift 示例

import SwiftKnex

let knexMigrations: [Migratable] = [
    Migration_20170116015823_CreateUser()
]

let config = KnexConfig(
   host: "localhost",
   user: "root",
   database: "swift_knex_test"
)

try Migrator.run(config: config, arguments: CommandLine.arguments, knexMigrations: knexMigrations)

编辑 main.swift 后,运行 swift build

swift build

5. 执行迁移和回滚

之后,你只需要运行迁移即可。

当前支持的命令是

尝试执行迁移

.build/debug/Migration migrate:latest

尝试执行回滚

.build/debug/Migration migrate:rollback

Seed (种子数据)

待办事项

与 Prorsum 配合使用

使用 Prorsum 进行 Go 风格的异步查询执行和同步。

import Prorsum

let chan = Channel<ResultSet>.make()

go {
    let rows = try! knex().table("users").where("id" == 1).fetch()
    try chan.send(rows!)
}

go {
    let rows = try! knex().table("users").where("id" == 2).fetch()
    try chan.send(rows!)
}

print(try! chan.receive())
print(try! chan.receive())

Mysql 库

SwiftKnex 中使用的基本连接和查询库。

待办事项

许可证

Prorsum 在 MIT 许可证下发布。 有关详细信息,请参阅 LICENSE。