以下是父与子之间的类定义。孩子: -
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
其中一个人说这已经在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
答案 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。我还没有找到更好的方法来做到这一点。