跳至内容 跳至搜索
方法
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 424
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 429
def begin_db_transaction()    end

begin_isolated_db_transaction(isolation)

开始具有已设置隔离级别的事务。默认情况下会引发错误;支持设置隔离级别的适配器应实现此方法。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 454
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 468
def commit_db_transaction()   end

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

别名: insert

default_sequence_name(table, column)

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

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

执行 DELETE 语句并返回受影响的行数。

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

empty_insert_statement_value(primary_key = nil)

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

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

在当前连接的上下文中执行 DELETE sql 语句,并使用 binds 作为绑定替换。name 将与执行的 sql 语句一起记录。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 168
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)

在当前连接的上下文中执行 INSERT sql 语句,并使用 binds 作为绑定替换。name 将与执行的 sql 语句一起记录。某些适配器支持 ‘returning` 关键字参数,该参数允许控制查询的结果:`nil` 是默认值并保持默认行为。如果传递了列名数组,结果将包含插入行中指定列的值。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 160
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 150
def exec_query(sql, name = "SQL", binds = [], prepare: false)
  internal_exec_query(sql, name, binds, prepare: prepare)
end

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

在当前连接的上下文中执行 UPDATE sql 语句,并使用 binds 作为绑定替换。name 将与执行的 sql 语句一起记录。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 175
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 139
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 544
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 198
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)

将给定的 fixture 插入表中。在需要比简单插入更复杂的操作的适配器中重写(例如 Oracle)。大多数适配器应实现 insert_fixtures_set,它利用批量 SQL 插入。我们保留此方法以提供对不支持批量插入的数据库(如 SQLite)的回退。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 504
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 508
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 464
def reset_isolation_level
end

reset_sequence!(table, column, sequence = nil)

将序列设置为表列的最大值。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 495
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 480
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 472
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 486
def rollback_to_savepoint(name = nil)
  exec_rollback_to_savepoint(name)
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 72
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 87
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 104
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 92
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 查询第一列值的一个数组。

select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 98
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 对象,可以在其上注册回调。

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,则块将被包装在充当子事务的数据库保存点中。

为了使嵌套事务回滚,您可以通过传递 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 355
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
  # If we're running inside the single, non-joinable transaction that
  # ActiveRecord::TestFixtures starts around each example (depth == 1),
  # an `isolation:` hint must be validated then ignored so that the
  # adapter isn't asked to change the isolation level mid-transaction.
  if isolation && !requires_new && open_transactions == 1 && !current_transaction.joinable?
    iso = isolation.to_sym

    unless transaction_isolation_levels.include?(iso)
      raise ActiveRecord::TransactionIsolationError,
            "invalid transaction isolation level: #{iso.inspect}"
    end

    current_transaction.isolation = iso
    isolation = nil
  end

  if !requires_new && current_transaction.joinable?
    if isolation && current_transaction.isolation != 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 447
def transaction_isolation_levels
  TRANSACTION_ISOLATION_LEVELS
end

transaction_open?()

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 398
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 221
def truncate(table_name, name = nil)
  execute(build_truncate_statement(table_name), name)
end

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

执行 UPDATE 语句并返回受影响的行数。

# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 209
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 121
def write_query?(sql)
  raise NotImplementedError
end