資料庫¶
若要存取關聯式資料庫,您需要一個專為您想使用的資料庫伺服器設計的 shard。 crystal-lang/crystal-db 套件為不同驅動程式提供統一的 API。
以下套件與 crystal-db 相容
- crystal-lang/crystal-sqlite3 適用於 sqlite
- crystal-lang/crystal-mysql 適用於 mysql & mariadb
- will/crystal-pg 適用於 postgres
以及其他 更多。
本指南介紹 crystal-db 的 API,由於 postgres、mysql 和 sqlite 之間存在差異,因此 sql 命令可能需要針對具體驅動程式進行調整。
此外,某些驅動程式可能會提供額外功能,例如 postgres 的 LISTEN
/NOTIFY
。
安裝 shard¶
從上面的清單中選擇合適的驅動程式,並將其作為任何 shard 添加到您的應用程式的 shard.yml
中
無需顯式引入 crystal-lang/crystal-db
在本指南中,將使用 crystal-lang/crystal-mysql
。
dependencies:
mysql:
github: crystal-lang/crystal-mysql
開啟資料庫¶
DB.open
可讓您輕鬆使用連線 URI 連線到資料庫。URI 的 schema 會決定預期的驅動程式。以下範例連線到名為 test 的本機 mysql 資料庫,使用者為 root,密碼為空白。
require "db"
require "mysql"
DB.open "mysql://root@localhost/test" do |db|
# ... use db to perform queries
end
其他連線 URI 為
sqlite3:///path/to/data.db
mysql://user:password@server:port/database
postgres://user:password@server:port/database
或者,您可以使用不產生 yield 的 DB.open
方法,只要在結尾呼叫 Database#close
即可。
require "db"
require "mysql"
db = DB.open "mysql://root@localhost/test"
begin
# ... use db to perform queries
ensure
db.close
end
或者,您可以使用 DB.connect
方法來開啟到資料庫的單一連線,而不是使用連線池。
Exec¶
若要執行 sql 陳述式,您可以使用 Database#exec
db.exec "create table contacts (name varchar(30), age int)"
db.exec "insert into contacts (name, age) values ('abc', 30)"
值可以作為查詢參數提供,請參閱下文。
Query¶
若要執行查詢並取得結果集,請使用 Database#query
。
Database#query
會回傳一個需要關閉的 ResultSet
。如 Database#open
中所述,如果使用區塊呼叫,則 ResultSet
將會隱式關閉。
db.query "select name, age from contacts order by age desc" do |rs|
rs.each do
# ... perform for each row in the ResultSet
end
end
值可以作為查詢參數提供,請參閱下文。
查詢參數¶
為避免 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
# Queries:
db.query("select name from contacts where age = ?", 33) do |rs|
rs.each do
# ... perform for each row in the ResultSet
end
end
查詢參數在底層會使用預備陳述式(有時會快取),或在客戶端插入,具體取決於驅動程式,但始終會避免 SQL 注入。
如果您想手動使用預備陳述式,可以使用 build
方法
# MySQL
prepared_statement = db.build("select * from contacts where id=?") # Use "... where id=$1" for PostgreSQL
# Use prepared statement:
prepared_statement.query(3) do |rs|
# ... use rs
end
prepared_statement.query(4) do |rs|
# ... use rs
end
prepared_statement.close
讀取查詢結果¶
從資料庫讀取值時,沒有 crystal 可以在編譯時期使用的型別資訊。您需要使用您期望從資料庫取得的型別 T
呼叫 rs.read(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
在 #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
中。