跳至内容 跳至搜索
方法
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

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 468
def default_sequence_name(table, column)
  nil
end

delete(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

empty_insert_statement_value(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 = [])

使用binds作为绑定替换,在当前连接的上下文中执行删除sql语句。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)

使用binds作为绑定替换,在当前连接的上下文中执行插入sql语句。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)

使用binds作为绑定替换,在当前连接的上下文中执行sql语句。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 = [])

使用binds作为绑定替换,在当前连接的上下文中执行更新sql语句。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

除非值为nil,否则将返回id_value,在这种情况下,数据库将尝试计算最后插入的 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)

将给定的 Fixture 插入到表中。在需要除了简单插入之外的操作的适配器(例如 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 limits 原样。

# 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 中第一列的值的数组。

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 对象,可以在其中注册回调

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)

执行截断语句。

# 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