浏览 3996 次
精华帖 (17) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-05-20
第一篇:ActiveRecord如何与MySQL交互 我们自定义的MyModel继承于ActiveRecord::Base类 MyModel < ActiveRecord::Base MyModel.find_xxx -> find_by_sql -> connection.select_all(sql) MyModel.create_xxx -> connection.insert(sql) MyModel.update_xxx -> connection.update(sql) MyModel.destroy -> connection.delete(sql) 这个connection是什么东西?让我们一步步往下看 可以看到MyModel的一些方法都在base.rb里实现为connection对象相关的方法 如果我们使用的是MySQL数据库,则这里的connection为ActiveRecord::ConnectionAdapters::MysqlAdapter对象 这是在Rails初始化时做的: environment.rb的Rails::Initializer.run -> initializer.rb的process方法 -> initializer.rb的initialize_database方法 -> ActiveRecord::Base.establish_connection(connenction_specification.rb里对ActiveRecord::Base添加该方法和connection方法) -> mysql_adapter.rb的mysql_connection方法 -> mysql.rb的real_connect方法 -> TCPSocket::new/UNIXSocket::new 而MysqlAdapter继承于AbstractAdapter,AbstractAdapter又include了DatabaseStatements所以: MyModel < ActiveRecord::Base MyModel.find_xxx -> find_by_sql -> DatabaseStatements.select_all(sql) -> MysqlAdapter.select(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql) MyModel.create_xxx -> MysqlAdapter.insert(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql) MyModel.update_xxx -> MysqlAdapter.update(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql) MyModel.destroy -> DatabaseStatements.delete(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql) 这里的MysqlAdapter.@connection在mysql_adapter.rb的mysql_connection方法里初始化了: require_mysql mysql = Mysql.init mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey] ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config) 继续追踪: @connection.query(sql) -> real_query(sql) -> command(sql) -> Net.read/write -> socket.read/write 至此,Rails自带的MySQL Adapter了解清楚了。 第二篇:ActiveRecord对MySQL的事务封装 首先看看MySQL的事务控制(摘自MySQL5.1参考手册): 引用 START TRANSACTION或BEGIN语句可以开始一项新的事务 COMMIT可以提交当前事务,使变更成为永久变更 ROLLBACK可以回滚当前事务,取消其变更 SET AUTOCOMMIT语句可以禁用或启用默认的autocommit模式,用于当前连接 默认情况下,MySQL采用autocommit模式运行 这意味着,当您执行一个用于更新(修改)表的语句之后,MySQL立刻把更新存储到磁盘中 如果您正在使用一个事务安全型的存储引擎(如InnoDB, BDB或NDB簇),则您可以使用以下语句禁用autocommit模式:SET AUTOCOMMIT=0 通过把AUTOCOMMIT变量设置为零,禁用autocommit模式之后,您必须使用COMMIT把变更存储到磁盘中,或着如果您想要忽略从事务开始进行以来做出的变更,使用ROLLBACK 使用START TRANSACTION,autocommit仍然被禁用,直到您使用COMMIT或ROLLBACK结束事务为止。然后autocommit模式恢复到原来的状态 开始追踪: # transactions.rb def self.included(base) base.extend(ClassMethods) base.class_eval do [:destroy, :save, :save!].each do |method| alias_method_chain method, :transactions end end end 这就是Rails里所谓的“隐式事务”,给你安排好了 这表示ActiveRecord对save,save!和destroy方法都包上了事务,而create、update、delete等方法最终都是调用save、save!和destroy方法 追踪: transactions.rb的transaction -> connection.transaction -> MysqlAdapter.transaction(没有) -> AbstractAdapter.transaciton(没有) -> DatabaseStatements.transaction 看下database_statements.rb里的transaciton方法: def transaction(start_db_transaction = true) transaction_open = false begin if block_given? if start_db_transaction begin_db_transaction transaction_open = true end yield end rescue Exception => database_transaction_rollback if transaction_open transaction_open = false rollback_db_transaction end raise end ensure commit_db_transaction if transaction_open end 马上又回到mysql_adapter.rb里对transaction打开、提交、回滚的实现: def begin_db_transaction #:nodoc: execute "BEGIN" rescue Exception # Transactions aren't supported end def commit_db_transaction #:nodoc: execute "COMMIT" rescue Exception # Transactions aren't supported end def rollback_db_transaction #:nodoc: execute "ROLLBACK" rescue Exception # Transactions aren't supported end execute即Mysql.query,上面提到了。 那么为啥ActiveRecord的transaction不支持嵌套呢? 还是看transaction.rb: def transaction(*objects, &block) previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" } increment_open_transactions begin unless objects.empty? ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0. See http://www.rubyonrails.org/deprecation for details.", caller objects.each { |o| o.extend(Transaction::Simple) } objects.each { |o| o.start_transaction } end result = connection.transaction(Thread.current['start_db_transaction'], &block) objects.each { |o| o.commit_transaction } return result rescue Exception => object_transaction_rollback objects.each { |o| o.abort_transaction } raise ensure decrement_open_transactions trap('TERM', previous_handler) end end private def increment_open_transactions #:nodoc: open = Thread.current['open_transactions'] ||= 0 Thread.current['start_db_transaction'] = open.zero? Thread.current['open_transactions'] = open + 1 end def decrement_open_transactions #:nodoc: Thread.current['open_transactions'] -= 1 end 原来,Rails每次遇到调用transaction方法时,看看当前线程里open transaction的次数: 1, 如果有多次,则调用connection.transaction(false, &block) 2, 如果为零次,则调用connection.transaction(true, &block) 而database_statements.rb里的transaciton方法根据true or false参数来判断是否重复open transaction 这样,Rails遇到最外层的transaction打开后,就不会再打开内部的transaction了,这也是手动写多层嵌套transaction时只有最外层的起作用的原因 至此,Rails对transaction的封装了解清楚。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-05-26
blog主题这么搞成这样,用ff看很晕的。。。
|
|
返回顶楼 | |
发表时间:2008-06-14
问一下,大家以前应该都是做java的吧,java里面有jta,rail里面有没有jta这个东西,或者第三方工具包也行。我找了没有发现。感觉现在的rail像是php,适合单数据库事务操作,跨库操作就麻烦很多!
|
|
返回顶楼 | |
发表时间:2008-06-14
这是你写一个模拟JTA跨库操作的Ruby库的好机会
|
|
返回顶楼 | |