4

TL;DR:在 AR::Base 保存事务中插入重复的连接表记录失败(由于唯一约束)导致保存失败并回滚。不添加重复的连接表记录很好。不储蓄是不好的。


我正在将 mysql 应用程序迁移到 postgres ......我曾经在 mysql-land 中遵循类似的模式将连接表记录添加到数据库:

class EventsSeries < ActiveRecord::Base
  #  UNIQUE KEY `index_events_series_on_event_id_and_series_id` (`event_id`,`series_id`)
  belongs_to :event
  belongs_to :series
end

class Series < ActiveRecord::Base

  has_many :events_series
  before_validation :add_new_event

private

  def add_new_event
    # boils down to something like this
    EventSeries.new.tap do |es|
      es.event_id = 1
      es.series_id = 1
      begin
        es.save!
      rescue ActiveRecord::RecordNotUnique
        # Great it exists
        # this isn't really a problem
        # please move on
      end
    end
  end
end

像这样调用:

Series.first.save 
# should not blow up on duplicate join record, cause i don't care

然而,postgres 对此大发雷霆。这里有一个很好的解释:

http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

...在“异常处理和回滚”部分(参见警告)

基本上#save启动一个事务,重复记录插入导致数据库异常,使#save的事务无效,这是sadface。

有没有更好的模式可以在 postgres-land 中使用?

谢谢!


编辑:

我坚信将这个逻辑保留在 Series 的保存事务中是有意义的……模式如下所示:

s = Series.new
s.new_event_id = 123 # this is just an attr_accessor
s.save # callbacks on Series know how to add the new event.

...它使我的控制器超级小。

4

1 回答 1

3

如果您在事务中并且要从错误中恢复并避免使整个事务无效,则必须使用保存点。

当您使用命令SAVEPOINT some-label时,您可以稍后运行命令ROLLBACK TO SAVEPOINT some-label以在事务中返回到该状态并忽略在采取保存点后的所有操作(包括错误)。

请在主键违规错误后继续交易中查看我的其他答案,以获得更详尽的解释。

于 2012-04-26T22:34:14.447 回答