1. 使用事物的原因
保证数据一致性, 当其中出现一个失败的时候,操作可以回滚
比如:
ActiveRecord::Base.transaction do david.withdrawal(100) # withdrawal失败必须触发 exception mary.deposit(100) # deposit失败必须触发 exception end
return false 不会出发操作回滚, 所以事物中要使用!的方法, 比如 save! update!
before_save 等callback也包含在事物中, 如果希望触发事物回滚, 需要能抛出异常
如果不希回滚则不抛出异常 或者放在事物之外 用after_commit等callbacl来处理
事务陷阱
不要在事务内部去捕捉 ActiveRecord::RecordInvalid 异常。因为某些数据库下,这个异常会导致事务失效,比如 Postgres。一旦事务失效,要想让代码正确工作,就必须从头重新执行事务。
另外,测试回滚或者事务回滚相关的回调时,最好关掉 transactional_fixtures 选项,一般的测试框架中,这个选项是打开的。
常见的事务反模式
- 单条记录操作时使用事务
- 不必要的使用嵌套式事务
- 事务中的代码不会导致回滚
- 在 controller 中使用事务
Exception handling and rolling back (待翻译)
Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you should be ready to catch those in your application code.
One exception is the ActiveRecord::Rollback
exception, which will trigger a ROLLBACK when raised, but not be re-raised by the transaction block.
Warning: one should not catch ActiveRecord::StatementInvalid
exceptions inside a transaction block. ActiveRecord::StatementInvalid
exceptions indicate that an error occurred at the database level, for example when a unique constraint is violated. On some database systems, such as PostgreSQL, database errors inside a transaction cause the entire transaction to become unusable until it‘s restarted from the beginning. Here is an example which demonstrates the problem:
# Suppose that we have a Number model with a unique column called ‘i‘. Number.transaction do Number.create(i: 0) begin # This will raise a unique constraint error... Number.create(i: 0) rescue ActiveRecord::StatementInvalid # ...which we ignore. end # On PostgreSQL, the transaction is now unusable. The following # statement will cause a PostgreSQL error, even though the unique # constraint is no longer violated: Number.create(i: 1) # => "PGError: ERROR: current transaction is aborted, commands # ignored until end of transaction block" end