代码整洁之道——程序员的职业素养
很难想象我在读这本书的时候是除夕的晚上,早晨在公司的书架上无疑翻到这本书,读了几个章节就被深深的吸引住了, 吸引我的并不是我很迫切的需要提高自己的职业素养,而是发现作者描述的场景和自己碰到的情况何其的相似,在某些 情况下甚至是直击内心的。在家人都休息的时候,我在深夜一点钟看完了这本书,而且我觉得有必要做一下笔记,...
本文由 Jasonliu 翻译自 Mark Daggett’s Blog。
最近一段时间,我的任务是为已存在的项目写事务测试,这给了我更多学习代码和提高测试覆盖率的机会。一般情况下,大部分事务相关的代码看起来都没有问题,但是有些却被误用,或使用的不够高效,我认为这是由于对事务在rails中工作原理的误解导致的, 并且我认为我应该花一些时间描述一下我发现的最常见的错误和使用事务的经验。
这篇文章中的大部分例子都不是我自己写的,它们来源于rails源码中的示例。
http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
我们使用事务把SQL语句包裹起来,以确保数据库的更改只发生在所有操作均成功的情况下。事务帮助开发者确保应用数据的完整性。最典型的例子是银行交易,现金被存入然后取出,如果其中一步失败,整个交易过程应当被重置。这个例子可以用以下代码来描述:
ActiveRecord::Base.transaction do
david.withdrawal(100)
mary.deposit(100)
end
在rails中,事务作为类或实例方法可以被所有的ActiveRecord model
使用,所以这样写也是合法的:
Client.transaction do
@client.users.create!
@user.clients(true).first.destroy!
Product.first.destroy!
end
@client.transaction do
@client.users.create!
@user.clients(true).first.destroy!
Product.first.destroy!
end
你可能注意到了,这几个事务示例都引用了不同的model类。在一个单独的事务代码块中混合model类是毫无问题的,因为事务是被绑定到数据库连接而不是model实例。通常情况下,数据库中的多个记录更新必须作为一个单元被成功执行的时候才需要事务。并且rails已经将save
和destroy
方法放在了事务当中,所以更新一个单独的记录是不需要事务的。
事务通过一个叫rollback的进程去重置记录的状态。在rails中,rollback
只会被异常触发,这是了解事务的一个关键点。在一些项目中,我发现了有些事务代码块根本就不会回滚,因为其中的代码不会抛出异常。在这里,我对之前的银行示例代码稍微做了修改:
ctiveRecord::Base.transaction do
david.update_attribute(:amount, david.amount -100)
mary.update_attribute(:amount, 100)
end
rails中,当更新操作失败后,update_attribute
不会抛出异常,它会返回false
,正因为如此,所以你应该确保方法在遇到失败的时候抛出异常。更好的做法是:
ActiveRecord::Base.transaction do
david.update_attributes!(:amount => -100)
mary.update_attributes!(:amount => 100)
end
注意:在rails中,[!]表示方法失败时会抛出异常。
我也看到过一些这样的例子:事务中使用find_by
查找记录,当没有找到记录的时候,find_by
会返回nil,而find
方法则会抛出一个ActiveRecord::RecordNotFound
的异常。
通常情况下,抛出一个异常会导致事务回滚并且异常会被传递出去,但是如果你抛出一个ActiveRecord::Rollback
异常,事务会回滚,但是异常不会被传递出去。
在编程的时候,一个常见的错误是误用和过度使用嵌套事务。当你在一个事务中嵌套另外一个事务时,子事务会成为父事务的一部分,这会导致意想不到的结果,让我们看看rails API 文档中的例子:
User.transaction do
User.create(:username => 'Kotori')
User.transaction do
User.create(:username => 'Nemu')
raise ActiveRecord::Rollback
end
end
正如之前提到的,异常ActiveRecord::Rollback
并不会传播到事务之外,以至于父事务并不会收到子事务中抛出的异常,因此子事务和父事务中的记录都被创建了。可能这样想会简单一点,嵌套事务就像子事务的内容被移到父事务中去了,子事务中的内容为空。
为了确保rollback被父事务接收到,你必须添加:requires_new => true
选项到子事务。再次使用rails源码中的例子,你可以像这样触发嵌套事务回滚:
User.transaction do
User.create(:username => 'Kotori')
User.transaction(:requires_new => true) do
User.create(:username => 'Nemu')
raise ActiveRecord::Rollback
end
end
一个事务只取决于当前的数据库连接,如果你的应用需要一次写多个数据库,你需要把方法放入嵌套事务当中。例如:
Client.transaction do
Product.transaction do
product.buy(@quantity)
client.update_attributes!(:sales_count => @sales_count + 1)
end
end
正如之前所说,save
和destroy
已经被放入了事务当中,这意味着像after_save
这样的回调函数也是事务的一部分,也会被回滚。因此,如果你有一些在事务之外需要执行的代码,可以使用像after_commit
或after_rollback
这样的回调函数。
在事务中,不要去捕获ActiveRecord::RecordInvalid
这样的异常,因为对于Postgres
这样的数据库,这种异常会导致事务失效。一旦事务失效,为了保证工作正确,你必须重启事务。
当只有单独的记录被更新的时候使用事务
无必要的嵌套事务
包含不会回滚的代码的事务
在controller中使用事务
作者在此并没有解释为什么不要在
controller
中使用事务。我认为是完全可以在controller
中使用事务的,这和在model
中使用事务的效果是一样的。但是rails是标准的MVC
架构,在controller
中使用事务违反了MVC
架构的设计原则:控制层不应该直接和数据库打交道,与数据库的交互应该交给model
层来完成。
很难想象我在读这本书的时候是除夕的晚上,早晨在公司的书架上无疑翻到这本书,读了几个章节就被深深的吸引住了, 吸引我的并不是我很迫切的需要提高自己的职业素养,而是发现作者描述的场景和自己碰到的情况何其的相似,在某些 情况下甚至是直击内心的。在家人都休息的时候,我在深夜一点钟看完了这本书,而且我觉得有必要做一下笔记,...
概览
rails 中的循环依赖
最近项目上遇到一个问题,某个 http 请求会返回 413 的状态码,一般来讲,这个问题是很好解决的, 网上有非常多的解决方案,但是我依然花了很长的时间找到问题根源,所以我觉得可以把这个过程记录下来。
什么是 WebSocket?
docker-elk Git repo
远程登录到 mongodb 实例
mongodb 创建索引和查询需要遵循的原则
一、默认添加 Mongoid::Timestamps
1. response out of time limit or subscription is canceled hint: (45015)
1. How to kill slow query?
领域驱动设计——软件核心复杂性应对之道
AppID、redirect_uri 参数错误
分布式的实时消息平台
Cassandra 在 Discord 中的应用
类和对象
基本数据结构和控制结构
使用 supervisor 管理进程
celery 的安装,配置及使用
更改tmux默认快捷键
使用 Vagrant 构建虚拟开发环境
寻找 python 和 ruby 的不同之处
使用 jQuery 实现自动补全功能
Rails 源码解读
实现软删除(soft delete)功能
使用 StackOverflow 帐户登录应用
使用 turbolinks 实现网页进度条
使用 GitHub 帐户登录应用
在 Rails 中使用 Ajax
收藏功能的实现
使用 gravatar 生成用户头像
支持 Markdown 语法和代码高亮
实现简单的搜索功能
在应用中使用 Bootstrap
实现简单的用户访问权限控制功能
rails
分页功能的实现
添加用户注册和登录功能
简单介绍如何在 Rails 项目中使用 MongoDB 及模型之间的关联
Rails 源码解读
List, Set, Map
java
方法覆盖和方法重载
内部类,局部内部类,匿名内部类,静态嵌套类
static, abstract, final
介绍 Ruby 多态的实现方式
包括访问权限, mixin, code load 等
Blocks,Procs 和 Lambdas 是 Ruby 最强有力的武器,也最难理解
Capistrano 是 Rails App 最常用的自动化部署工具,本篇文章将介绍 Capistrano 3的安装及配置
rails
java
ruby
rspec
git 常见操作
使用事务过程中常见的错误和解决方案
理解 go 语言中的 interface
golang
golang
Rails 虽然易于使用,但也很容易被滥用。本文聚焦于10个最常见的 Rails 陷进,并指导你如何避免它们以及由它们引发的问题
Uniqueness Validation
异常处理
mysql 常见操作
linux
冒泡排序,快速排序,希尔排序
sublime
你不会学到任何与 Ruby 相关的编程技巧,但你可以更好的理解垃圾回收器是如何工作的
总结了自己在工作中和 stackoverflow 上学到的一些很有用的方法
Active Record, mysql2