如何在Rails / ActiveRecord中的事务之外执行数据库操作

时间:2012-07-26 17:10:23

标签: mysql ruby-on-rails activerecord

我需要在我的Rails应用程序中执行一些原始SQL。如果在事务中执行查询,则会导致隐式提交。我们正在使用带有InnoDB的MySQL,并且查询将包括例如创建表。

使用ActiveRecord::Base.connection.execute执行查询会触发有问题的隐含提交。

感觉我只需要一个单独的连接来执行我的查询。 ActiveRecord可以提供吗?我已经看到了连接到多个数据库但没有多个连接到同一个数据库的讨论。

如果有更好的方法,解决方案不必涉及ActiveRecord。

我们的Rails和ActiveRecord版本是3.2.3。

3 个答案:

答案 0 :(得分:16)

数据库连接是基于每个线程完成的(这基本上是线程安全所必需的),您可以利用它:只需在单独的线程中执行代码,例如

ActiveRecord::Base.transaction do
  # ...
  Thread.new do
    ActiveRecord::Base.connection.execute "..." # in a new connection
  end.join
end

从rails 4开始,activerecord不再自动获取以这种方式创建的连接。为避免泄漏连接,您需要将它们返回池中。正如Matt Connelly建议的那样,最简单的方法是使用with_connection方法,该方法将在块的末尾检查连接,例如

Thread.new do
  ActiveRecord::Base.connection_pool.with_connection do
    ...
  end
end

答案 1 :(得分:2)

DDl和其他一些查询会触发隐式提交,因此无法根据mysql文档回滚它们

http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html

这些隐式结束当前会话中活动的任何事务,就像在执行语句之前已完成COMMIT一样。

如果之间没有此类查询,则可以使用SAVEPOINT功能。 (This does not work with DDL statements

活动记录中有一个选项可帮助创建使用保存点的子事务

   ActiveRecord::Base.transaction do
     # ...
   ActiveRecord::Base.transaction(:requires_new => true) do #creates save point
       # perform task 
       # if error occurs rollbacks only till the save point.
     end
   end

查看rails doc了解详情。

答案 2 :(得分:2)

重要的是,如果在线程中使用连接,则在完成后将连接返回到连接池。最简单的方法就是这样:

Thread.new do
  ActiveRecord::Base.connection_pool.with_connection do |connection|
    connection.execute "..." 
    # ensures the connection is returned to the pool when the thread is done.        
  end
end.join