iteye_3589
2009-06-11 11:13
浏览 250
已采纳

关于Rails中多表保存的事务一致性

[size=medium]关于Rails中多表操作的事务一致性[/size]

User has_one UserDetail 一个用户有一个用户明细,里面保存了用户的详细信息
User has_many MarkDetails 一个用户有多个积分明细,里面记录了每次获得积分的记录
Message 站内信

应用场景:
用户通过认证后,要更新一下users表中的是否通过验证的标志字段,同时更新一下用户明细表中的一些

信息,另外通过身份认证后,用户将得到100的积分,还要给用户发送一个站内认告诉他这件事情。

[code="ruby"]class User < ActiveRecord::Base

def confirmed_width_auth(id_card)
self.is_auth = 1
self.user_detail.id_card = id_card

mark_detail = self.mark_details.new
mark_detail.mark = 100
mark_detail.description = "身份认证得到积分"

message = Message.new
message.subject = "身份认证,得到积分"
message.body = "你得到100积分"
message.to_id = self.id

# 很明显,上面涉及到要保存三张表的信息,我应该接下来怎么处理 来保证事务的一致性呢?
self.save && mark_detail.save && message.save # 我这样做行不?
# 另外好像在关联的表,在Rails中是可以进行事务处理的,而没有关联的表好像不能进行事务处理的,是这样的吗?

end

end[/code]

在那个<[color=darkblue]应用Rails进行敏捷开发[/color]>的书上有讲到Rails中active record中的事务处理,不过那例子讲到的是在同一个表中不同的记录间所执行的操作,对于多个不同的表(甚至是没有任何关联的表),我们应该怎么来安全的处理事务呢?

因为我做的是一个电子商务方面的应用,所以对于业务操作中的事务还是非常关注的,[color=indigo]我希望能正确的安全的完成整个业务 方法的执行。[/color]

请有验证的和有想法的大大们指点一下,谢谢。
[b]问题补充:[/b]
谢谢nt,
[quote]1.捕捉异常。
2.数据库回滚,对象不回滚。(千万要记住) [/quote]
关于1是应该在controller中进行么?
关于2你想说什么?能具体 一点吗? :oops:
[b]问题补充:[/b]
[code="ruby"]
transaction do
self.save!
mark_detail.save!
message.save!
end
[/code]

我自己试了一下,以上的代码能正确工作,你为什么不用这样的代码呢?
比你刚才那圆环套圆环的方法好看一些啊。

我还是对 Rails的ar的事务处理机制不100%明白 :(
[b]问题补充:[/b]
我自己测试过的,数据库中相关表中的记录的事务能保证的,你可以试试,

不过你所说的
[color=red]非 User 对象应该无法回滚[/color]

是指user的实例 对象无法回滚到先前的状态吗?

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

5条回答 默认 最新

  • xenocide 2009-06-11 18:23
    已采纳

    这个 transaction do ... end 其实是调用了 Uer 的类方法。
    而 User.transaction 只控制 user 表的回滚, 别的表它不管的。

    这个三环可以简化一下(最后一环其实没用到):
    [code="ruby"]
    User.transaction do
    MarkDetails.transaction do
    self.save!
    # 不过这一步可能连带保存了 mark_detail ?
    # 如果只生成一条 sql,就是单连接的事务,可以回滚 ……
    mark_detail.save!
    message.save!
    end
    end
    [/code]

    你用
    [code="ruby"]
    transaction do
    self.save!
    mark_detail.save!
    message.save!
    raise 'error'
    end
    [/code]
    试试看是不是都回滚了……

    参考 [url]http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html[/url]

    [quote]
    A transaction acts on a single database connection. If you have multiple class-specific databases, the transaction will not protect interaction among them. One workaround is to begin a transaction on each class whose models you alter: [/quote]

    transaction 是连接相关的,如果能保证只在一个数据库连接中做所有事情,应该一个套就够了,但是不一定保险 ……

    已采纳该答案
    打赏 评论
  • xenocide 2009-06-11 16:37

    User.transaction do
    MarkDetails.transaction do
    Message.transaction do
    self.save!
    mark_detail.save!
    message.save!
    end
    end
    end

    save! 是异常版,如果其中出了异常,三张表都回滚。

    另外要注意的:
    1.捕捉异常。
    2.数据库回滚,对象不回滚。(千万要记住)

    打赏 评论
  • xenocide 2009-06-11 16:40

    更正: MarkDetails --> MarkDetail

    还有一点设计上的: 这个东西涉及到多个类,万一 User 膨胀到很大很大(我说的是万一……),分离出 Service 类好点。

    打赏 评论
  • xenocide 2009-06-11 17:11

    在 controller 和 model 都可以。如果不打算提供过于具体的出错信息,甚至可以写个处理异常的 filter,一次搞定。

    第二条,考虑这个情况:
    第一步保存了 user,第二步保存 mark_detail 出错,那么第一步保存的 user 记录会回滚消失。但这个回滚是数据库做的,回滚了什么东西 rails 不知道,所以 user 对象还是刚 save 时的状态,甚至还有一个子虚乌有的 id 属性…… 要继续使用这个对象,得注意这点。

    打赏 评论
  • xenocide 2009-06-11 17:40

    不对…… 你这个等于

    User.transaction do
    ...
    end

    非 User 对象应该无法回滚

    打赏 评论

相关推荐 更多相似问题