略過到內容 略過到搜尋
方法
A
B
C
D
E
H
I
N
R
S
T
U
W

類別公開方法

new()

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 6
def initialize
  super
  reset_transaction
end

實體公開方法

add_transaction_record(record, ensure_finalize = true)

用目前的交易註冊一筆紀錄,以便在它完成並回滾後呼叫 after_commit 和 after_rollback 回呼。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 405
def add_transaction_record(record, ensure_finalize = true)
  current_transaction.add_record(record, ensure_finalize)
end

begin_db_transaction()

開始交易(並關閉自動認可)。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 410
def begin_db_transaction()    end

begin_isolated_db_transaction(isolation)

開始具有設定的隔離等級的交易。預設會引發錯誤;支援設定隔離等級的適配器應實作這個方法。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 432
def begin_isolated_db_transaction(isolation)
  raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
end

commit_db_transaction()

認可交易(並開啟自動認可)。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 446
def commit_db_transaction()   end

建立(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)

別名的定義: 插入

預設序列名稱(table, column)

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 468
def default_sequence_name(table, column)
  nil
end

刪除(arel, name = nil, binds = [])

執行刪除陳述,並傳回受影響的列數。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 212
def delete(arel, name = nil, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
  exec_delete(sql, name, binds)
end

空白插入陳述值(primary_key = nil)

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 498
def empty_insert_statement_value(primary_key = nil)
  "DEFAULT VALUES"
end

exec_delete(sql, name = nil, binds = [])

在這個連線中執行刪除sql陳述,並使用binds作為繫結替換。name會和執行的sql陳述一起記錄。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 165
def exec_delete(sql, name = nil, binds = [])
  affected_rows(internal_execute(sql, name, binds))
end

exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)

在這個連線中執行插入sql陳述,並使用binds作為繫結替換。name會和執行的sql陳述一起記錄。有些轉接器支援『returning』關鍵字引數,它可以控制查詢的結果:nil是預設值且維持預設行為。如果傳遞了欄位名稱的陣列,結果將包含從已插入列傳回的指定欄位值。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 157
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
  sql, binds = sql_for_insert(sql, pk, binds, returning)
  internal_exec_query(sql, name, binds)
end

exec_query(sql, name = "SQL", binds = [], prepare: false)

在這個連線中執行sql陳述,並使用binds作為繫結替換。name會和執行的sql陳述一起記錄。

注意:假設查詢有副作用,而且查詢快取會清除。如果查詢是唯讀的,請考慮改用select_all

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 147
def exec_query(sql, name = "SQL", binds = [], prepare: false)
  internal_exec_query(sql, name, binds, prepare: prepare)
end

exec_update(sql, name = nil, binds = [])

於此連線的脈絡下執行更新 sql 陳述式,使用 binds 作為綁定替代。name 與已執行的 sql 陳述式一起記錄。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 172
def exec_update(sql, name = nil, binds = [])
  affected_rows(internal_execute(sql, name, binds))
end

execute(sql, name = nil, allow_retry: false)

在此連線的脈絡中執行 SQL 陳述式,並從連線適配器傳回原始結果。

allow_retry 設為 true 會使資料庫重新連線並在遇到與連線相關的例外情況時重試執行 SQL 陳述式。這個選項僅應啟用於已知的冪等查詢。

注意:假設查詢有副作用,而且查詢快取會清除。如果查詢是唯讀的,請考慮改用select_all

注意:根據您的資料庫連接器,此方法傳回的結果可能會手動記憶體管理。考慮使用 exec_query 封裝器。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 136
def execute(sql, name = nil, allow_retry: false)
  internal_execute(sql, name, allow_retry: allow_retry)
end

high_precision_current_timestamp()

傳回 Arel SQL 文字,表示 CURRENT_TIMESTAMP,可用於任意精度的日期/時間欄位。

支援精度日期時間的適配器應覆寫此功能,以提供可用的最高精度。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 536
def high_precision_current_timestamp
  HIGH_PRECISION_CURRENT_TIMESTAMP
end

insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)

執行 INSERT 查詢並傳回新記錄的 ID

id_value 將會傳回,除非這個值為 nil,對於這種情況資料庫將嘗試計算最後插入的 id 並傳回那個值。

如果下一個 id 已預先計算(例如在 Oracle 中),就應該當做 id_value 傳入。某些適配器支援 'returning` 關鍵字參數,允許定義方法的傳回值:nil 是預設值並維持預設行為。如果傳入一個欄位名稱陣列,就會從方法傳回一個陣列,表示已插入列中指定欄位的數值。

別名: create
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 195
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
  sql, binds = to_sql_and_binds(arel, binds)
  value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)

  return returning_column_values(value) unless returning.nil?

  id_value || last_inserted_id(value)
end

insert_fixture(fixture, table_name)

將給定的固定資料插入到這個資料表。在需要除了簡單插入之外的事情(例如 Oracle)的適配器中覆寫。大部分的適配器都應該實作 insert_fixtures_set 來利用大量 SQL 插入。我們保留這個方法來為像 SQLite 這些不支援大量插入的資料庫提供後備方案。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 482
def insert_fixture(fixture, table_name)
  execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
end

insert_fixtures_set(fixture_set, tables_to_delete = [])

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 486
def insert_fixtures_set(fixture_set, tables_to_delete = [])
  fixture_inserts = build_fixture_statements(fixture_set)
  table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
  statements = table_deletes + fixture_inserts

  transaction(requires_new: true) do
    disable_referential_integrity do
      execute_batch(statements, "Fixtures Load")
    end
  end
end

reset_isolation_level()

隔離的資料庫交易承諾或回滾後,呼叫的掛接點。大多數的介面卡不需要實作任何內容,因為隔離等級基於每筆交易設定。但某些資料庫(例如 SQLite)會針對每個連線層級設定,並在承諾或回滾後需要明確地重設。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 442
def reset_isolation_level
end

reset_sequence!(table, column, sequence = nil)

將順序設定為該表格欄位的最大值。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 473
def reset_sequence!(table, column, sequence = nil)
  # Do nothing by default. Implement for PostgreSQL, Oracle, ...
end

restart_db_transaction()

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 458
def restart_db_transaction
  exec_restart_db_transaction
end

rollback_db_transaction()

回滾該交易(並啟動自動提交)。如果交易區塊產生例外或傳回 false,則必須執行此動作。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 450
def rollback_db_transaction
  exec_rollback_db_transaction
rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
  # Connection's gone; that counts as a rollback
end

rollback_to_savepoint(name = nil)

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 464
def rollback_to_savepoint(name = nil)
  exec_rollback_to_savepoint(name)
end

sanitize_limit(limit)

集中管理給定的 LIMIT 參數,以防止 SQL 注入。

limit 可能任何可透過 to_s 評量為字串的任何內容。它應該看起來像一個整數,或是一個 Arel SQL 字面值。

傳回 Integer 與 Arel::Nodes::SqlLiteral 的極限不變。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 508
def sanitize_limit(limit)
  if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
    limit
  else
    Integer(limit)
  end
end

select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false)

傳回 ActiveRecord::Result 實例。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 69
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false)
  arel = arel_from_relation(arel)
  sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)

  select(sql, name, binds,
    prepare: prepared_statements && preparable,
    async: async && FutureResult::SelectAll,
    allow_retry: allow_retry
  )
rescue ::RangeError
  ActiveRecord::Result.empty(async: async)
end

select_one(arel, name = nil, binds = [], async: false)

傳回一個紀錄雜湊,包含欄位名稱作為鍵和欄位值作為值。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 84
def select_one(arel, name = nil, binds = [], async: false)
  select_all(arel, name, binds, async: async).then(&:first)
end

select_rows(arel, name = nil, binds = [], async: false)

傳回一個陣列,其中包含欄位值。順序與 columns 傳回的順序相同。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 101
def select_rows(arel, name = nil, binds = [], async: false)
  select_all(arel, name, binds, async: async).then(&:rows)
end

select_value(arel, name = nil, binds = [], async: false)

從紀錄傳回單一值

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 89
def select_value(arel, name = nil, binds = [], async: false)
  select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
end

select_values(arel, name = nil, binds = [])

傳回一個陣列,其中包含選取中第一個欄位的數值。

select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 95
def select_values(arel, name = nil, binds = [])
  select_rows(arel, name, binds).map(&:first)
end

to_sql(arel_or_sql_string, binds = [])

將 arel AST 轉換為 SQL

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 12
def to_sql(arel_or_sql_string, binds = [])
  sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
  sql
end

transaction(requires_new: nil, isolation: nil, &block)

在資料庫交易中執行給定的區塊,並傳回該區塊的結果。

Transaction 回呼

transaction 讓出 ActiveRecord::Transaction 物件,可用於註冊 callback

ActiveRecord::Base.transaction do |transaction|
  transaction.before_commit { puts "before commit!" }
  transaction.after_commit { puts "after commit!" }
  transaction.after_rollback { puts "after rollback!" }
end

巢狀交易支援

transaction 呼叫可以巢狀。預設情況下,這會讓巢狀交易區塊中的所有資料庫陳述都變成長輩交易的一部分。例如,以下是會令人驚訝的行為

ActiveRecord::Base.transaction do
  Post.create(title: 'first')
  ActiveRecord::Base.transaction do
    Post.create(title: 'second')
    raise ActiveRecord::Rollback
  end
end

這會建立「first」和「second」文章。原因是巢狀區塊中的 ActiveRecord::Rollback 例外沒有發出 ROLLBACK。由於這些例外是在交易區塊中擷取的,因此長輩區塊不會看到它,並且真正的交易會提交。

大多數資料庫不支援真正的巢狀交易。撰寫本文的當時,我們所知的唯一支援真正的巢狀交易的資料庫是 MS-SQL。

若要解決此問題,transaction 會使用儲存點模擬嵌套交易的效果:dev.mysql.com/doc/refman/en/savepoint.html

如果資料庫交易已開啟,安全地呼叫此方法,亦即在另一個 transaction 區塊內呼叫 transaction。如果巢狀呼叫,transaction 的表現如下

  • 區塊會執行,不做任何事。區塊內的所有資料庫陳述式,實質上都會附加到已開啟的資料庫交易上。

  • 不過,如果設定 :requires_new,區塊會包裝在資料庫儲存點中,作為子交易。

若要取得嵌套交易的 ROLLBACK,你可以傳遞 requires_new: true,要求一個真正的子交易。如果發生問題,資料庫會備份到子交易的開始,而不會備份母交易。如果我們將其新增到前一個範例中

ActiveRecord::Base.transaction do
  Post.create(title: 'first')
  ActiveRecord::Base.transaction(requires_new: true) do
    Post.create(title: 'second')
    raise ActiveRecord::Rollback
  end
end

只會建立標題是「first」的貼文。

請參閱 ActiveRecord::Transactions,以了解更多資訊。

注意事項

MySQL 不支援 DDL 交易。如果你執行 DDL 作業,任何建立的儲存點將自動釋放。例如,如果你建立一個儲存點,然後你執行一個 CREATE TABLE 陳述式,那麼會自動釋放建立的儲存點。

這表示在 MySQL 上,你不應該在 transaction 呼叫中執行 DDL 作業,而且你已經知道可能會建立一個儲存點。否則,transaction 會在嘗試釋放已經自動釋放的儲存點時引發例外狀況

Model.lease_connection.transaction do  # BEGIN
  Model.lease_connection.transaction(requires_new: true) do  # CREATE SAVEPOINT active_record_1
    Model.lease_connection.create_table(...)
    # active_record_1 now automatically released
  end  # RELEASE SAVEPOINT active_record_1  <--- BOOM! database error!
end

Transaction 隔離

如果你的資料庫支援為交易設定隔離層級,你可以這樣設定

Post.transaction(isolation: :serializable) do
  # ...
end

有效的隔離層級如下

  • :read_uncommitted

  • :read_committed

  • :repeatable_read

  • :serializable

你應該查看資料庫文件,以了解這些不同層級的語意

如果發生以下情況,將引發 ActiveRecord::TransactionIsolationError

  • 配接器不支援設定隔離層級

  • 你正在加入一個現有的開啟交易

  • 你正在建立一個巢狀(儲存點)交易

mysql2、trilogy 和 postgresql 配接器支援設定交易隔離層級。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 352
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
  if !requires_new && current_transaction.joinable?
    if isolation
      raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
    end
    yield current_transaction.user_transaction
  else
    within_new_transaction(isolation: isolation, joinable: joinable, &block)
  end
rescue ActiveRecord::Rollback
  # rollbacks are silently swallowed
end

transaction_isolation_levels()

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 420
def transaction_isolation_levels
  {
    read_uncommitted: "READ UNCOMMITTED",
    read_committed:   "READ COMMITTED",
    repeatable_read:  "REPEATABLE READ",
    serializable:     "SERIALIZABLE"
  }
end

transaction_open?()

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 379
def transaction_open?
  current_transaction.open?
end

truncate(table_name, name = nil)

執行 truncate 陳述式。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 218
def truncate(table_name, name = nil)
  execute(build_truncate_statement(table_name), name)
end

update(arel, name = nil, binds = [])

執行更新陳述並傳回已影響的列數。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 206
def update(arel, name = nil, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
  exec_update(sql, name, binds)
end

write_query?(sql)

判斷 SQL 陳述是否為寫入查詢。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 118
def write_query?(sql)
  raise NotImplementedError
end