1

以下是父级和子级之间的类定义:-

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 适配器的问题。

另外仅供参考,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 适配器

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

环境:Rails 3.2.5,Ruby 1.9.2

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

4

1 回答 1

1

如果我没有在回调中使用 DDL,这整个问题就不会发生——它会自动提交打开的事务。因此,即使其中一个 DDL 失败,父级也不能回滚,因为它已经提交了。

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

我定义了一个类来在单独的连接中处理 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 的 Rails 方式,但我正在为 DBA 开发一个应用程序来管理他们的生产环境,例如。配置用户、授予权限等,因此需要执行 DDL。我还没有找到更好的方法来做到这一点。

于 2012-07-21T01:07:33.997 回答