資料庫

要連接一個關聯式資料庫,我們需要根據資料庫的種類,挑選特定的 shard 來使用。crystal-lang/crystal-db 提供了統一的 API 來連接各種的資料庫驅動程式。

以下為幾個符合 crystal-db API 實作的套件:

這份文件會介紹 crystal-db 的 API,我們之所以會需要 crystal-db 是因為不同的資料庫(如:postgres、mysql 和 sqlite)之間的 SQL 語法細節不盡相同需要轉換。

並且某些的資料庫驅動提供了一些比較特別的功能,如 postgres 中的 LISTENNOTIFY

安裝 shard

從上述的列表選擇合適的驅動程式,將它加入到 shard.yml 檔案中,就像你在新增其他的 shard 時做的一樣。

在使用這些 shard 時我們不需要明確的在 shard.yml 宣告使用 crystal-lang/crystal-db

而在這份指南中我們會使用 crystal-lang/crystal-mysql

dependencies:
  mysql:
    github: crystal-lang/crystal-mysql

開啟資料庫

DB.open 指令讓我們能夠簡單地藉由資料庫 uri 來連上資料庫。Uri 的 schema 會決定預期要使用的驅動。下面是一個用 root 帳號及空白密碼連線至本機的 mysql 資料庫的範例。

require "db"
require "mysql"

DB.open "mysql://root@localhost/test" do |db|
  # ... 使用 db 執行查詢
end

其他的連線 uri

  • sqlite3:///path/to/data.db
  • mysql://user:password@server:port/database
  • postgres://server:port/database

或者是我們可以使用不帶程式區塊的方式呼叫 DB.open,只要我們記得在最後呼叫 Database#close 即可。

require "db"
require "mysql"

db = DB.open "mysql://root@localhost/test"
begin
  # ... 使用 db 執行查詢
ensure
  db.close
end

執行 SQL

我們可以使用 Database#exec 執行 sql 語句

db.exec "create table contacts (name varchar(30), age int)"

為了避免 SQL 注入攻擊 我們可以用參數化的方式來傳遞我們想傳遞的值。參數化的語法取決於你所使用的資料庫驅動,因為這些驅動會原封不動的將語句傳遞給資料庫。在 MySQL 中使用 ? 當作 參數展開並且根據引數的順序賦值。PostgreSQL 使用 $n,其中 n 是根據引數數量從 1 開始的一連串數字。

# MySQL
db.exec "insert into contacts values (?, ?)", "John", 30
# Postgres
db.exec "insert into contacts values ($1, $2)", "Sarah", 33

查詢

我們可以操作 Database#query 方法執行查詢並且拿回結果,而我們也能夠使用相同的參數來操作 Database#exec 方法。

如同 Database#open 方法,Database#query 會回傳一組需要被關閉的 ResultSet,同樣的若是藉由程式區塊呼叫方法,則 Crystal 會自動地幫我們關閉 ResultSet

db.query "select name, age from contacts order by age desc" do |rs|
  rs.each do
    # ... 利用 ResultSet 對每一行進行操作
  end
end

當從資料庫讀取資料時,Crystal 在編譯時並不曉得資料的型別資訊,所以我們會需要使用 rs.read(T) 來特定型別 T 作為我們預期從資料庫拿到的結果。

db.query "select name, age from contacts order by age desc" do |rs|
  rs.each do
    name = rs.read(String)
    age = rs.read(Int32)
    puts "#{name} (#{age})"
    # => Sarah (33)
    # => John Doe (30)
  end
end

Crystal 有需多方便的查詢方法建立在 #query 之上。

我們可以一次讀取多行:

name, age = rs.read(String, Int32)

或是一次讀取一行:

name, age = db.query_one "select name, age from contacts order by age desc limit 1", as: {String, Int32}

我們也可以不需要通過 ResultSet 來獲得一個單純的值:

max_age = db.scalar "select max(age) from contacts"

而在 DB::QueryMethods 定義了所有能用來執行資料庫語句的方法。

results matching ""

    No results matching ""