Rails事务在验证错误时不会回滚

11

我有两个模型:用户和公司。它们都是从一个表单创建的,我正在使用以下事务:

 User.transaction do

  @user.save!

  @company.user = @user
  @company.save!

  @user.reload
  @user.company = @company
  @user.save!

 flash[:notice] = "Thank you for your registration."
  redirect_to_index
end

即使公司的其中一个验证失败,用户仍会被保存到数据库中。我尝试了添加ActiveRecord::RecordInvalid的显式错误处理,但没有帮助。我认为验证会抛出错误以回滚事务。非常感谢任何帮助。

谢谢


10
你正在使用哪种关系型数据库管理系统?它是否支持事务处理(MyISAM与InnoDB)? - Marcel Jackwerth
啊,我想就这样了。大多数表都是InnoDB,但也有一些MyISAM。谢谢。 - Shagymoe
请参见:https://dev59.com/M0zSa4cB1Zd3GeqPoq9u。 - James A. Rosen
5个回答

3
尝试同时保存新条目并修改现有条目(基于新条目),遇到了类似的问题。尝试使用救援失败验证的事务,但最终采用了以下解决方案:
if @new_entry.valid? && @existing_entry.valid?
  ActiveRecord::Base.transaction do
    @new_entry.save!
    @existing_entry.save!
  end
end

代码首先进行验证。只有在两个条目都有效时,它才尝试保存。如果数据库支持,则事务语义可防止不完整的条目或其他错误。希望这是一个好的解决方案。


2

您必须使用支持ACID事务的数据库引擎。对于mysql,这是INNODB。

show table status\G

如果用户或公司没有使用InnoDB引擎,您可以使用以下命令进行更改。
ALTER TABLE <table name> ENGINE INNODB;

@company.save! 抛出的异常应该触发向数据库发送ROLLBACK命令。当您在DEBUG日志级别下运行script / server时,可以在控制台/日志文件中验证这一点。


不要在事务中捕获ActiveRecord :: RecordInvalid异常,因为此异常会使某些数据库(如Postgres)的事务无效。一旦事务无效,您必须从头开始重新启动它才能正常工作。http://markdaggett.com/blog/2011/12/01/transactions-in-rails/ - Sig

0

save()和destroy()总是在事务中(请参见http://railsapi.com/doc/rails-v2.3.5/classes/ActiveRecord/Transactions/ClassMethods.html)。

我认为你想要做的是

  begin
    @company = Company.create!(params[:company])
    @user    = User.create!(params[:user]) { |user| user.company => @company }
  rescue => ex
    # ... handle your validation errors
  end

当公司验证失败时,会引发一个异常,这样就可以解决您的问题,而用户创建语句也不会执行。


0

您需要考虑以下情况:

  1. 使用支持事务的数据库系统
  2. 重构代码以实现更合理的方法

例如,如果您只有一对一的关系,可以采用更优化的方式处理。

Company has_one User
User belongs_to Company

现在,在公司模型中

@company.user.build(user_attributes)
@company.save  # will save company as well as user

这只是我脑海中的一个例子,我还没有进行测试。请问我是否正确理解了问题?


-3

不是这样的。如果你想要做更多的事情而不仅仅是简单的回滚,你需要一个 begin..rescue..end 块,但是在堆栈上方的异常处理程序可以在回滚完成后同样容易地处理它。你也可以让 Rails 处理它并显示错误页面。 - Steve Madsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接