시작
이 튜토리얼에서는 Ubuntu 16.04 시스템과 MySQL 5.7을 사용합니다. MySQL 5.7에는 일련의 새로운 기능이 도입되었습니다. 그 중 하나는 JSON 데이터를 보다 효율적으로 저장하는 기능을 제공하는 동시에 JSON 데이터 내부를 쿼리하는 기능을 제공하는 것입니다. 나중에 MySQL 5.7이 Ubuntu 16.04의 기본 MySQL 버전이 되면 Ubuntu 16.04를 운영 체제로 사용하게 됩니다.
아직 Swift를 설치하지 않았다면 apt-get을 사용하여 설치할 수 있습니다. 설치 지침은 이 문서를 참조하세요. 2016년 9월 말, Apple은 Ubuntu 16.04에서도 Swift 이미지 컴파일을 시작했습니다. 자세한 내용은 Swift.org를 확인하세요. 이 부분에 대한 명령은 다음과 같습니다.
Swift 패키지 만들기
# sudo mysql ... mysql> create user swift; Query OK, 0 rows affected (0.00 sec) mysql> create database swift_test; Query OK, 1 row affected (0.00 sec) mysql> grant all on swift_test.* to 'swift'@'localhost' identified by 'swiftpass'; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> flush privileges; Query OK, 0 rows affected (0.00 sec) mysql> quit ByePackage .swift 파일 작성:
# mkdir swift_mysql # swift package init --type executable두 번째 단계에서는 보조 도구 코드를 사용하여 임의의 데이터를 생성하고 데이터베이스에 채웁니다. Sources 디렉터리에 utils.swift 파일을 추가하고 그 안에 다음 내용을 추가하세요:
import PackageDescription let package = Package( name: "swift_mysql", dependencies:[ .Package(url:"https://github.com/vapor/mysql", majorVersion:1) ] )Vapor MySQL다음은 실제 코드입니다. main.swift 파일은 Vapor MySQL 모듈을 사용합니다. .
import Glibc class Random { static let initialize:Void = { srandom(UInt32(time(nil))) return () }() } func randomString(ofLength length:Int) -> String { Random.initialize let charactersString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" let charactersArray:[Character] = Array(charactersString.characters) var string = "" for _ in 0..<length { string.append(charactersArray[Int(random()) % charactersArray.count]) } return string } func randomInt() -> Int { Random.initialize return Int(random() % 10000) }
데이터베이스에 연결
Sources/main.swift에 다음 코드를 추가하세요.
위 코드는 데이터베이스를 설정하고 mysql을 처리합니다. 생성자 Database(호스트:문자열, 사용자:문자열, 비밀번호:문자열, 데이터베이스:문자열)는 설명이 필요하지 않습니다.try mysql.execute("SELECT @@version") 문은 데이터베이스에 올바르게 연결되고 성공적으로 연결되었는지 테스트하는 데 사용됩니다. do 코드 블록이 오류 없이 실행되면 데이터베이스 운영을 시작할 수 있습니다!
import Glibc import MySQL var mysql:Database do { mysql = try Database(host:"localhost", user:"swift", password:"swiftpass", database:"swift_test") try mysql.execute("SELECT @@version") } catch { print("Unable to connect to MySQL: \(error)") exit(-1) }정수 및 문자열
MySQL에 대한 모든 호출은 실행(_:String) 메서드를 통해 이루어집니다. 이 메소드는 .create(table:String, ...) 또는 .insert(table:String, ....execute)와 같은 일부 추상 API 메소드와 다르다는 점에 유의해야 합니다.
Node 类型是 Vapor 中的数据结构,用于转化为不同的类型。你可以从这里获取更多的信息。使用 Node 类型来表达 MySQL 可以方便的转换成对应的 Swift 类型。比如:let bar = result["bar"]?.int 给我们一个整型。
继续
接着我们来看一些更复杂的例子,比如创建一个表,包含了 MySQL 的 DATE, POINT 和 JSON 数据类型。我们的表名叫 samples。
do { try mysql.execute("DROP TABLE IF EXISTS samples") try mysql.execute("CREATE TABLE samples (id INT PRIMARY KEY AUTO_INCREMENT, created_at DATETIME, location POINT, reading JSON)") // ... Date // ... Point // ... Sample // ... Insert // ... Query } catch { print("Error: \(error)") exit(-1) }
要插入一个日期到数据库中,需要正确的 SQL 语句:
// ... Date let now = Date() let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" // MySQL will accept this format let created_at = formatter.string(from:date)
接下来使用 Swift 元组来创建一个 POINT:
// ... Point let location = (37.20262, -112.98785) // latitude, longitude
最后,我们来处理 MySQL 5.7 中新的 JSON 数据类型,此外我们使用了 Jay 包来快速将一个 Swift 字典 [String:Any] 转换为 JSON 格式的字符串。
// ... Sample let sample:[String:Any] = [ "heading":90, "gps":[ "latitude":37.20262, "longitude":-112.98785 ], "speed":82, "temperature":200 ]
提示:你不需要显式在 Package.swift 中声明对 Jay 的依赖,因为在 MySQL 的包中已经包含了这个依赖。接下来我们把 JSON 数据转换为 String,用来拼凑 MySQL 语句。
let sampleData = try Jay(formatting:.minified).dataFromJson(any:sample) // [UInt8] let sampleJSON = String(data:Data(sampleData), encoding:.utf8)
这样我们就有了 date, point 和 JSON 字符串(sample) 了, 现在添加数据到 sample 表中:
// ... Insert let stmt = "INSERT INTO samples (created_at, location, sample) VALUES ('\(created_at)', POINT\(point), '\(sampleJSON)')" try mysql.execute(stmt)
请注意我们在处理 POINT 时候,使用了一些技巧。在对 (point) 展开为字符串 (37.20262, -112.98785) 后,完整的字符串是 POINT(37.20262, -112.98785),这是 MySQL 所需要的数据,整个语句的字符串如下:
INSERT INTO samples (created_at, location, sample) VALUES ('2016-09-21 22:28:44', POINT(37.202620000000003, -112.98784999999999), '{"gps":{"latitude":37.20262,"longitude":-112.98785},"heading":90,"speed":82,"temperature":200}')
获取结果
警告:在写这篇文章的时候(2016-09-22), Vapor MySQL 1.0.0 有一个 bug:在读取 POINT 数据类型时会 crash 掉,所以不得不在下面代码中加入 do 代码块,然后不使用 select 语句。我们在 Vapor MySQL 中记录了这个 issue,等这个 issue 修复以后,我们将更新文章。
在下面的例子中,我们将使用 MySQL 5.7 中引入对 JSON 数据内部的查询特性,使用 SELECT … WHERE 查询 JSON 数据。在这里查询的是 samples 表中 JSON 数据类型 sample中、speed 字段大于 80 的数据。
// ... 查询 let results = try mysql.execute("SELECT created_at,sample FROM samples where JSON_EXTRACT(sample, '$.speed') > 80") for result in results { if let sample = result["sample"]?.object, let speed = sample["speed"]?.int, let temperature = sample["temperature"]?.int, let created_at = result["created_at"]?.string { print("Time:\(created_at)\tSpeed:\(speed)\tTemperature:\(temperature)") } }
这里做一些说明。JSON_EXTRACT 函数是用来 返回从 JSON 文档中的数据,根据传入的路径参数选择文档中满足条件的数据。在本例中,我们解包了列 sample 中的 speed 值。
为了循环处理结果,我们使用了 for result in results 语句,接着使用 if let 语句验证结果数据。首先使用 let sample = result["sample"]?.object 获取一个字典,对应 MySQL 中的 JSON 文档,这是一句关键的代码!Vapor MySQL 库并没有返回一个 String,而 String 还需进行 JSON 的解析。这个解析工作库已经帮你做了,所以你可以直接使用 sample 字典啦。
剩下的 let 语句给了我们 speed,temperature 和 created_at。注意 created_at 在 MySQL 中是 DATETIME 类型,我们读取它为字符串。为了在 Swift 中转换成 Date 类型,需要使用 .date(from:String) 方法加一个 DateFormatter 来做类型转换。
获取代码
如果你想直接运行代码,请到 github 上下载我们的代码。
在任何地方使用 swift build 进行编译,运行可执行代码,不要忘了你还需要拥有一个数据库,用户名并且授权通过。