跳到內容

連線

當處理資料庫時,連線是關鍵部分之一。它代表了陳述從我們的應用程式傳遞到資料庫的通道

在 Crystal 中,我們有兩種建立此連線的方式。因此,接下來,我們將提供一些範例,並就何時使用每種方式提供一些建議。

DB 模組

給我一個立足點,我將移動地球。阿基米德

當在 Crystal 中使用資料庫時,DB 模組是我們的立足點。正如文件中所寫:是資料庫存取的統一介面

此模組中實作的方法之一是 DB#connect。使用此方法是建立連線的第一種方式。讓我們看看如何使用它。

DB#connect

當使用 DB#connect 時,我們實際上是開啟與資料庫的連線。該模組使用作為引數傳遞的 uri 來決定要使用的驅動程式(例如:mysql://postgres://sqlite:// 等),也就是說,我們不需要指定我們正在使用哪個資料庫。

此範例的 urimysql://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 方法,databaseconnection 物件是多型的。資料庫負責連線的使用。太棒了!

何時使用其中之一?

鑑於這些範例,我們可能會注意到連線的數量是相關的。如果我們正在程式設計一個僅有一位使用者啟動對資料庫請求的短期應用程式,那麼由我們管理的單一連線(即 DB::Connection 物件)就足夠了(想想一個接收參數的命令列應用程式,然後啟動對資料庫的請求,最後將結果顯示給使用者)另一方面,如果我們正在建立一個具有許多並行使用者和大量資料庫存取的系統,那麼我們應該使用 DB::Database 物件;它透過使用連線池,將會有許多連線已經準備好可以使用(沒有引導/初始化時間的懲罰)。或者想像一下,您正在建立一個長期運行的應用程式(如背景工作),那麼連線池將使您擺脫監控連線狀態的責任:它是存活的還是需要重新連線?

連線設定

當使用 uri 建立連線時,我們不僅可以指定使用者、密碼、主機、資料庫等,還可以指定一些連線池設定和每個驅動程式提供的一些自訂選項。請查看每個驅動程式的文件以了解更多資訊。

僅舉幾個例子

進階連線設定

在某些情況下,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 資料庫的範例。