使用 Swift 3.0 操做 MySQL 數據庫

做者:Joe,原文連接,原文日期:2016-09-24
譯者:shanks;校對:walkingway;定稿:CMBnode

若是你閱讀過本主其餘的 Swift 文章,你會發現咱們是 Swift 服務器端開發的忠實擁護者。
今天咱們將繼續研究這個主題,使用 Vapor 封裝的 MySQL wrapper 來操做 MySQL 數據庫。mysql

說明:這並非一篇介紹 MySQL 或 SQL 的文章,若是你對數據庫還不熟悉,網上有大量的教程可供學習。本篇咱們將焦聚在 Linux 上使用 Swift 3.0 來操做 MySQL 數據庫。git

開始

在這篇教程中,咱們採用 Ubuntu 16.04 系統和 MySQL 5.7。MySQL 5.7 引入了一系列的新特性。其中一個就是提供了更加高效的存儲 JSON 數據的能力,同時提供了查詢 JSON 數據內部的能力。稍後若是 MySQL 5.7 成爲了 Ubuntu 16.04 上默認的 MySQL 版本之後,咱們將使用 Ubuntu 16.04 做爲咱們的操做系統。github

若是你尚未安裝 Swift, 你可使用 apt-get 方式來安裝。參見這篇文章的說明安裝。2016 年 9 月底,蘋果也開始在 Ubuntu16.04 上編譯 Swift 的鏡像。請查看 Swift.org 獲取更多的信息。sql

建立數據庫

咱們把數據庫命名爲 swift_test, 分配的用戶是 swift, 密碼是 swiftpass,若是你熟悉 MySQL,你應該知道須要執行 GRANT ALL ON swift_test.* 進行受權。
下面是這部分的命令:數據庫

# 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
Bye

建立 Swift 包

如今開始正式進行編碼,首先建立一個包:ubuntu

# mkdir swift_mysql
# swift package init --type executable

編寫 Package.swift 文件:swift

import PackageDescription

let package = Package(
    name: "swift_mysql",
    dependencies:[
      .Package(url:"https://github.com/vapor/mysql", majorVersion:1)
    ]
)

第二步,咱們使用一些輔助的工具代碼來生成一些隨機的數據,填充到數據庫中。
在 Sources 目錄下添加 utils.swift 文件並在裏面添加如下內容:服務器

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)
}

Vapor MySQL

接下來是真正的代碼,咱們的 main.swift 文件使用了 Vapor MySQL 模塊。數據結構

鏈接數據庫

添加如下代碼到 Sources/main.swift 中:

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。構造器 Database(host:String, user:String, password:String, database:String) 一目瞭然。
語句 try mysql.execute("SELECT @@version」) 是用來測試保證咱們鏈接正確,併成功鏈接到了數據庫。若是 do 代碼塊運行無錯誤,接下來就能夠開始操做數據庫了!

整型和字符串

全部對 MySQL 的調用都將經過 execute(_:String) 方法。須要注意的是該方法和一些抽象 API 的方法不一樣,好比 .create(table:String, ...) 或者 .insert(table:String, …execute 獲取原始的 SQL 語句並傳給 MySQL 鏈接器。

do {
  try mysql.execute("DROP TABLE IF EXISTS foo")
  try mysql.execute("CREATE TABLE foo (bar INT(4), baz VARCHAR(16))")

  for i in 1...10 {
    let int    = randomInt()
    let string = randomString(ofLength:16)
    try mysql.execute("INSERT INTO foo VALUES (\(int), '\(string)')")
  }

  // Query
  let results = try mysql.execute("SELECT * FROM foo")
  for result in results {
    if let bar = result["bar"]?.int,
       let baz = result["baz"]?.string {
      print("\(bar)\t\(baz)")
    }
  }
} catch {
  print("Error:  \(error)")
  exit(-1)
}

查詢結果也是使用的 execute(_:String) 方法。可是返回的結果是一個 [String:Node] 字典。字典的 key 對應着數據庫的列名。

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 進行編譯,運行可執行代碼,不要忘了你還須要擁有一個數據庫,用戶名而且受權經過。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索