連線¶
當處理資料庫時,連線是關鍵部分之一。它代表了陳述從我們的應用程式傳遞到資料庫的通道。
在 Crystal 中,我們有兩種建立此連線的方式。因此,接下來,我們將提供一些範例,並就何時使用每種方式提供一些建議。
DB 模組¶
給我一個立足點,我將移動地球。阿基米德
當在 Crystal 中使用資料庫時,DB 模組是我們的立足點。正如文件中所寫:是資料庫存取的統一介面。
此模組中實作的方法之一是 DB#connect
。使用此方法是建立連線的第一種方式。讓我們看看如何使用它。
DB#connect¶
當使用 DB#connect
時,我們實際上是開啟與資料庫的連線。該模組使用作為引數傳遞的 uri
來決定要使用的驅動程式(例如:mysql://
、postgres://
、sqlite://
等),也就是說,我們不需要指定我們正在使用哪個資料庫。
此範例的 uri
是 mysql://root:root@localhost/test
,因此該模組將使用 mysql 驅動程式
來連線到 MySQL 資料庫。
以下是範例
require "mysql"
cnn = DB.connect("mysql://root:root@localhost/test")
puts typeof(cnn) # => DB::Connection
cnn.close
值得一提的是,該方法會傳回 DB::Connection
物件。雖然更具體來說,它會傳回 MySql::Connection
物件,但這並不重要,因為所有類型的連線都應該是多型的。因此,以下我們將使用 DB::Connection
實例,協助我們從每個資料庫引擎的特定問題中抽離出來。
當手動建立連線時(就像我們在這裡所做的那樣),我們有責任管理此資源,因此我們必須在使用完畢後關閉連線。關於後者,這個小細節可能是造成巨大錯誤的原因!Crystal,作為人類的語言,為我們提供了一種更安全的手動建立連線的方式,即使用區塊,如下所示
require "mysql"
DB.connect "mysql://root:root@localhost/test" do |cnn|
puts typeof(cnn) # => DB::Connection
end # the connection will be closed here
好的,現在我們有連線了,讓我們使用它!
require "mysql"
DB.connect "mysql://root:root@localhost/test" do |cnn|
puts typeof(cnn) # => DB::Connection
puts "Connection closed: #{cnn.closed?}" # => false
result = cnn.exec("drop table if exists contacts")
puts result
result = cnn.exec("create table contacts (name varchar(30), age int)")
puts result
cnn.transaction do |tx|
cnn2 = tx.connection
puts "Yep, it is the same connection! #{cnn == cnn2}"
cnn2.exec("insert into contacts values ('Joe', 42)")
cnn2.exec("insert into contacts values (?, ?)", "Sarah", 43)
end
cnn.query_each "select * from contacts" do |rs|
puts "name: #{rs.read}, age: #{rs.read}"
end
end
首先,在此範例中,我們正在使用交易(請查看交易章節以了解有關此主題的更多資訊)其次,重要的是要注意,交易提供的連線與我們在交易開始之前使用的連線相同。也就是說,在我們的程式中,始終只有一個連線。最後,我們正在使用 #exec
和 #query
方法。您可以在資料庫章節中閱讀有關執行查詢的更多資訊。
現在我們對建立連線有了很好的了解,讓我們介紹建立連線的第二種方式:DB#open
DB#open¶
require "mysql"
db = DB.open("mysql://root:root@localhost/test")
puts typeof(db) # DB::Database
db.close
與連線一樣,一旦我們不再需要資料庫,就應該關閉資料庫。或者,我們可以改用區塊並讓 Crystal 為我們關閉資料庫!
但是,連線在哪裡?好吧,我們應該要求取得連線。當建立資料庫時,會建立一個連線池,其中包含已準備好可以使用之資料庫的連線!(您想閱讀有關連線池的更多資訊嗎?在連線池章節中,您可以閱讀有關這個有趣主題的所有資訊!)
我們如何使用 database
物件中的連線?為此,我們可以透過使用 Database#checkout
方法來要求資料庫提供連線。但是,這樣做需要使用 Connection#release
將連線明確地返回到池中。以下是一個範例
require "mysql"
DB.open "mysql://root:root@localhost/test" do |db|
cnn = db.checkout
puts typeof(cnn)
puts "Connection closed: #{cnn.closed?}" # => false
cnn.release
puts "Connection closed: #{cnn.closed?}" # => false
end
我們希望有一種安全的方式(即我們無需釋放連線)來請求和使用 database
中的連線,我們可以改用 Database#using_connection
require "mysql"
DB.open "mysql://root:root@localhost/test" do |db|
db.using_connection do |cnn|
puts typeof(cnn)
# use cnn
end
end
在下一個範例中,我們將讓 database
物件自行管理連線,如下所示
require "mysql"
DB.open "mysql://root:root@localhost/test" do |db|
db.exec("drop table if exists contacts")
db.exec("create table contacts (name varchar(30), age int)")
db.transaction do |tx|
cnn = tx.connection
cnn.exec("insert into contacts values ('Joe', 42)")
cnn.exec("insert into contacts values (?, ?)", "Sarah", 43)
end
db.query_each "select * from contacts" do |rs|
puts "name: #{rs.read}, age: #{rs.read}"
end
end
正如我們可能注意到的,關於 #exec
/ #query
/ #transaction
方法,database
與 connection
物件是多型的。資料庫負責連線的使用。太棒了!
何時使用其中之一?¶
鑑於這些範例,我們可能會注意到連線的數量是相關的。如果我們正在程式設計一個僅有一位使用者啟動對資料庫請求的短期應用程式,那麼由我們管理的單一連線(即 DB::Connection
物件)就足夠了(想想一個接收參數的命令列應用程式,然後啟動對資料庫的請求,最後將結果顯示給使用者)另一方面,如果我們正在建立一個具有許多並行使用者和大量資料庫存取的系統,那麼我們應該使用 DB::Database
物件;它透過使用連線池,將會有許多連線已經準備好可以使用(沒有引導/初始化時間的懲罰)。或者想像一下,您正在建立一個長期運行的應用程式(如背景工作),那麼連線池將使您擺脫監控連線狀態的責任:它是存活的還是需要重新連線?
連線設定¶
當使用 uri
建立連線時,我們不僅可以指定使用者、密碼、主機、資料庫等,還可以指定一些連線池設定和每個驅動程式提供的一些自訂選項。請查看每個驅動程式的文件以了解更多資訊。
僅舉幾個例子
- crystal-lang/crystal-sqlite3 允許指定
?journal_mode=WAL
將 journal_mode 設定為WAL
。 - crystal-lang/crystal-mysql 允許指定
?encoding=utf8mb4_unicode_ci
將排序規則和字元集設定為utf8mb4_unicode_ci
。 - will/crystal-pg 允許指定
?auth_methods=scram-sha-256
以僅允許scram-sha-256
驗證方法。
進階連線設定¶
在某些情況下,uri
的彈性可能不足。如果我們想要連線池,我們可以手動建立連線物件或資料庫物件。每個驅動程式都將提供一種執行此操作的方法,因為每個驅動程式可能有不同的選項。
# for a single connection
connection = TheDriver::Connection.new(crystal_db_connection_options, driver_connection_options)
# for a connection pool
db = DB::Database.new(crystal_db_connection_options, crystal_db_pool_options) do
TheDriver::Connection.new(crystal_db_connection_options, driver_connection_options)
end
在 crystal-db#181 中,我們可以透過手動建立連線將使用的底層 IO
,來看到使用 crystal-pg 透過 SSH 通道連線到 postgres 資料庫的範例。