当自动保存关联无法执行DDL时,保存在具有嵌套属性的模型上不会回滚

时间:2012-07-19 23:21:36

标签: ruby-on-rails activerecord transactions ruby-on-rails-3.2 oci

以下是父与子之间的类定义。孩子: -

class System
  has_many :members, :dependent => :destroy
  accepts_nested_attributes_for :members
  attr_accessible :name, :members_attributes
  validates_presence_of :name , :members

class Member
   belongs_to :system
   attr_accessible  :name
   validates_presence_of :name
   before_create :create_physical_schema_for_user

在控制器中,我一次创建整个关系: -

@system = System.new(params[:system])
...
@system.save

在子进程中的before_create回调之一失败并出现OCIError(由ruby-oci8 gem引发)导致ActiveRecord :: StatementInvalid异常之前,情况一直很好。在此阶段,即使尚未保存其中一个子关联,也会保存父级。我不确定,如果这是rails或oracle适配器的问题。

另外,FYI,create_physical_schema_for_user方法以下列方式运行一组DDL: -

def self.create_physical_schema_for_user(name)
  ddl_stmt = ['...',name]
  self.connection.execute(sanitize_sql(ddl_stmt))
end

我发现这些链接正在讨论相同的问题(只是回调不同)

https://rails.lighthouseapp.com/projects/8994/tickets/3391-nested-attributes-vs-before_save

https://rails.lighthouseapp.com/projects/8994/tickets/3045-nested_attributes-doesnt-rollback-parent-when-before_saveafter_save-callbacks-fail

其中一个人说这已经在Rails 2中合并了,我仍然在rails 3.2.5中遇到了这个问题但是使用了Oracle Adapters

我想知道在单个事务中固有地保存父级及其所有关联的最佳方法是什么,这样即使一个子级创建失败,我们也会回滚整个父事务。

环境: Rails 3.2.5,Ruby 1.9.2

数据库: Oracle 11g XE通过ruby-oci8(2.1.2)& activerecord-oracle_enhanced-adapter(1.4.1)gem

1 个答案:

答案 0 :(得分:1)

如果我没有在回调中使用DDL,那么整个问题就不会发生 - 它会自动提交开放事务。因此,即使其中一个DDL失败,父级也无法回滚,因为它已经被提交。

我已经完成了以下操作,在单独的数据库连接中执行DDL,因此它们的失败不会影响Model的事务,可以安全地回滚。

我定义了一个类来处理单独连接中的DDL执行。

class SqlSystem  < ActiveRecord::Base
 establish_connection Rails.configuration.database_configuration[Rails.env]
 private
 def self.execute_ddl(stmt)
    self.connection.execute(sanitize_sql(stmt))
 end
end

并在成员类的before_create回调中: -

def self.create_physical_schema_for_user(name)
  begin 
    SqlSystem.execute_ddl('...')
  rescue => error
    return false # Or Re-Raise the exception, whichever suits
  end
end

注意:我知道它不是从ActiveRecord执行DDL的轨道方式,但我正在为DBA开发一个应用程序来管理他们的生产环境,例如。供应用户,授予权限等因此需要执行DDL。我还没有找到更好的方法来做到这一点。