当前位置:首页-技术前沿-ORM/JPA-ActiveRecord

  • Rails源码研究之ActiveRecord:一,基本架构、CRUD封装与数据库连接
    作者: 本站会员  来源:javaEye  发布时间:2007-10-23 13:20:00
  • Rails的ORM框架ActiveRecord是马大叔的ActiveRecord模式的实现+associations+SingleTableInheritance
    ActiveRecord的作者也是Rails的作者--David Heinemeier Hansson
    ActiveRecord的key features:
    1,零Meta Data,不需要XML配置文件
    2,Database Support,现在支持mysql postgresql sqlite firebird sqlserver db2 oracle sybase openbase frontbase,写一个新的database adapter不会超过100行代码
    3,线程安全,本地Ruby Web服务器,如WEBrick/Cerise,用线程处理请求
    4,速度快,对100个对象循环查找一个值做benchmark,速度为直接数据库查询速度的50%
    5,事务支持,使用事务来确保级联删除自动执行,同时也支持自己写事务安全的方法
    6,简洁的关联,使用natural-language macros,如has_many、belongs_to
    7,内建validations支持
    8,自定义值对象

    让我们深入研读一下ActiveRecord的核心源码
    1,activerecord-1.15.3\lib\active_record.rb:

    1. $:.unshift(File.dirname(__FILE__)) unless  
       2.   $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))  
       3.   
       4. unless defined?(ActiveSupport)  
       5.   begin  
       6.     $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")    
       7.     require 'active_support'    
       8.   rescue LoadError  
       9.     require 'rubygems'  
      10.     gem 'activesupport'  
      11.   end  
      12. end  
      13.   
      14. require 'active_record/base'  
      15. require 'active_record/observer'  
      16. require 'active_record/validations'  
      17. require 'active_record/callbacks'  
      18. require 'active_record/reflection'  
      19. require 'active_record/associations'  
      20. require 'active_record/aggregations'  
      21. require 'active_record/transactions'  
      22. require 'active_record/timestamp'  
      23. require 'active_record/acts/list'  
      24. require 'active_record/acts/tree'  
      25. require 'active_record/acts/nested_set'  
      26. require 'active_record/locking/optimistic'  
      27. require 'active_record/locking/pessimistic'  
      28. require 'active_record/migration'  
      29. require 'active_record/schema'  
      30. require 'active_record/calculations'  
      31. require 'active_record/xml_serialization'  
      32. require 'active_record/attribute_methods'  
      33.   
      34. ActiveRecord::Base.class_eval do  
      35.   include ActiveRecord::Validations  
      36.   include ActiveRecord::Locking::Optimistic  
      37.   include ActiveRecord::Locking::Pessimistic  
      38.   include ActiveRecord::Callbacks  
      39.   include ActiveRecord::Observing  
      40.   include ActiveRecord::Timestamp  
      41.   include ActiveRecord::Associations  
      42.   include ActiveRecord::Aggregations  
      43.   include ActiveRecord::Transactions  
      44.   include ActiveRecord::Reflection  
      45.   include ActiveRecord::Acts::Tree  
      46.   include ActiveRecord::Acts::List  
      47.   include ActiveRecord::Acts::NestedSet  
      48.   include ActiveRecord::Calculations  
      49.   include ActiveRecord::XmlSerialization  
      50.   include ActiveRecord::AttributeMethods  
      51. end  
      52.   
      53. unless defined?(RAILS_CONNECTION_ADAPTERS)  
      54.   RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite firebird sqlserver db2 oracle sybase openbase frontbase )  
      55. end  
      56.   
      57. RAILS_CONNECTION_ADAPTERS.each do |adapter|  
      58.   require "active_record/connection_adapters/" + adapter + "_adapter"  
      59. end  
      60.   
      61. require 'active_record/query_cache'  
      62. require 'active_record/schema_dumper'

     首先$:.unshift一句将当前文件加入动态库路径,然后确保加载ActiveSupport
    然后将active_record/base/observer/validations.../attribute_methods等子目录下的文件require进来
    然后用ActiveRecord::Base.class_eval将ActiveRecord::Validations/Locking/.../AttributeMethods等子模块include进来
    RAILS_CONNECTION_ADAPTERS定义了ActiveRecord支持的database adapters的名字数组,然后循环将每个adapter文件require进来
    最后将query_cache和schema_dumper这两个文件require进来

    2,activerecord-1.15.3\lib\active_record\base.rb:

     

    1. module ActiveRecord  
       2.   
       3.   class Base  
       4.   
       5.     class << self # Class methods  
       6.   
       7.       def find(*args)  
       8.         options = extract_options_from_args!(args)  
       9.         validate_find_options(options)  
      10.         set_readonly_option!(options)  
      11.   
      12.         case args.first  
      13.           when :first then find_initial(options)  
      14.           when :all   then find_every(options)  
      15.           else             find_from_ids(args, options)  
      16.         end  
      17.       end  
      18.   
      19.       def find_by_sql(sql)  
      20.         connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }  
      21.       end  
      22.   
      23.       def exists?(id_or_conditions)  
      24.         !find(:first, :conditions => expand_id_conditions(id_or_conditions)).nil?  
      25.       rescue ActiveRecord::ActiveRecordError  
      26.         false  
      27.       end  
      28.   
      29.       def create(attributes = nil)  
      30.         if attributes.is_a?(Array)  
      31.           attributes.collect { |attr| create(attr) }  
      32.         else  
      33.           object = new(attributes)  
      34.           scope(:create).each { |att,value| object.send("#{att}=", value) } if scoped?(:create)  
      35.           object.save  
      36.           object  
      37.         end  
      38.       end  
      39.   
      40.       def update(id, attributes)  
      41.         if id.is_a?(Array)  
      42.           idx = -1  
      43.           id.collect { |id| idx += 1; update(id, attributes[idx]) }  
      44.         else  
      45.           object = find(id)  
      46.           object.update_attributes(attributes)  
      47.           object  
      48.         end  
      49.       end  
      50.   
      51.       def delete(id)  
      52.         delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ])  
      53.       end  
      54.   
      55.       def destroy(id)  
      56.         id.is_a?(Array) ? id.each { |id| destroy(id) } : find(id).destroy  
      57.       end  
      58.   
      59.       def update_all(updates, conditions = nil)  
      60.         sql  = "UPDATE #{table_name} SET #{sanitize_sql(updates)} "  
      61.         add_conditions!(sql, conditions, scope(:find))  
      62.         connection.update(sql, "#{name} Update")  
      63.       end  
      64.   
      65.       def destroy_all(conditions = nil)  
      66.         find(:all, :conditions => conditions).each { |object| object.destroy }  
      67.       end  
      68.   
      69.       def delete_all(conditions = nil)  
      70.         sql = "DELETE FROM #{table_name} "  
      71.         add_conditions!(sql, conditions, scope(:find))  
      72.         connection.delete(sql, "#{name} Delete all")  
      73.       end  
      74.   
      75.       def count_by_sql(sql)  
      76.         sql = sanitize_conditions(sql)  
      77.         connection.select_value(sql, "#{name} Count").to_i  
      78.       end  
      79.   
      80.       private  
      81.   
      82.         def find_initial(options)  
      83.           options.update(:limit => 1) unless options[:include]  
      84.           find_every(options).first  
      85.         end  
      86.   
      87.         def find_every(options)  
      88.           records = scoped?(:find, :include) || options[:include] ?  
      89.             find_with_associations(options) :   
      90.             find_by_sql(construct_finder_sql(options))  
      91.   
      92.           records.each { |record| record.readonly! } if options[:readonly]  
      93.   
      94.           records  
      95.         end  
      96.   
      97.         def find_from_ids(ids, options)  
      98.           expects_array = ids.first.kind_of?(Array)  
      99.           return ids.first if expects_array && ids.first.empty?  
     100.   
     101.           ids = ids.flatten.compact.uniq  
     102.   
     103.           case ids.size  
     104.             when 0  
     105.               raise RecordNotFound, "Couldn't find #{name} without an ID"  
     106.             when 1  
     107.               result = find_one(ids.first, options)  
     108.               expects_array ? [ result ] : result  
     109.             else  
     110.               find_some(ids, options)  
     111.           end  
     112.         end  
     113.         
     114.         def find_one(id, options)  
     115.           conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]  
     116.           options.update :conditions => "#{table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"  
     117.   
     118.           if result = find_every(options).first  
     119.             result  
     120.           else  
     121.             raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"  
     122.           end  
     123.         end  
     124.         
     125.         def find_some(ids, options)  
     126.           conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]  
     127.           ids_list   = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')  
     128.           options.update :conditions => "#{table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}"  
     129.   
     130.           result = find_every(options)  
     131.   
     132.           if result.size == ids.size  
     133.             result  
     134.           else  
     135.             raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"  
     136.           end  
     137.         end  
     138.   
     139.         def method_missing(method_id, *arguments)  
     140.           if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s)  
     141.             finder, deprecated_finder = determine_finder(match), determine_deprecated_finder(match)  
     142.   
     143.             attribute_names = extract_attribute_names_from_match(match)  
     144.             super unless all_attributes_exists?(attribute_names)  
     145.   
     146.             attributes = construct_attributes_from_arguments(attribute_names, arguments)  
     147.   
     148.             case extra_options = arguments[attribute_names.size]  
     149.               when nil  
     150.                 options = { :conditions => attributes }  
     151.                 set_readonly_option!(options)  
     152.                 ActiveSupport::Deprecation.silence { send(finder, options) }  
     153.   
     154.               when Hash  
     155.                 finder_options = extra_options.merge(:conditions => attributes)  
     156.                 validate_find_options(finder_options)  
     157.                 set_readonly_option!(finder_options)  
     158.   
     159.                 if extra_options[:conditions]  
     160.                   with_scope(:find => { :conditions => extra_options[:conditions] }) do  
     161.                     ActiveSupport::Deprecation.silence { send(finder, finder_options) }  
     162.                   end  
     163.                 else  
     164.                   ActiveSupport::Deprecation.silence { send(finder, finder_options) }  
     165.                 end  
     166.   
     167.               else  
     168.                 ActiveSupport::Deprecation.silence do  
     169.                   send(deprecated_finder, sanitize_sql(attributes), *arguments[attribute_names.length..-1])  
     170.                 end  
     171.             end  
     172.           elsif match = /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/.match(method_id.to_s)  
     173.             instantiator = determine_instantiator(match)  
     174.             attribute_names = extract_attribute_names_from_match(match)  
     175.             super unless all_attributes_exists?(attribute_names)  
     176.   
     177.             attributes = construct_attributes_from_arguments(attribute_names, arguments)  
     178.             options = { :conditions => attributes }  
     179.             set_readonly_option!(options)  
     180.   
     181.             find_initial(options) || send(instantiator, attributes)  
     182.           else  
     183.             super  
     184.           end  
     185.         end  
     186.   
     187.         def extract_attribute_names_from_match(match)  
     188.           match.captures.last.split('_and_')  
     189.         end  
     190.   
     191.         def construct_attributes_from_arguments(attribute_names, arguments)  
     192.           attributes = {}  
     193.           attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }  
     194.           attributes  
     195.         end  
     196.   
     197.       protected  
     198.   
     199.         def sanitize_sql(condition)  
     200.           case condition  
     201.             when Array; sanitize_sql_array(condition)  
     202.             when Hash;  sanitize_sql_hash(condition)  
     203.             else        condition  
     204.           end  
     205.         end  
     206.   
     207.         def sanitize_sql_hash(attrs)  
     208.           conditions = attrs.map do |attr, value|  
     209.             "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"  
     210.           end.join(' AND ')  
     211.   
     212.           replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))  
     213.         end  
     214.   
     215.         def sanitize_sql_array(ary)  
     216.           statement, *values = ary  
     217.           if values.first.is_a?(Hash) and statement =~ /:\w+/  
     218.             replace_named_bind_variables(statement, values.first)  
     219.           elsif statement.include?('?')  
     220.             replace_bind_variables(statement, values)  
     221.           else  
     222.             statement % values.collect { |value| connection.quote_string(value.to_s) }  
     223.           end  
     224.         end  
     225.   
     226.         alias_method :sanitize_conditions, :sanitize_sql    
     227.   
     228.     end  
     229.   
     230.     public  
     231.   
     232.       def save  
     233.         create_or_update  
     234.       end  
     235.   
     236.       def save!  
     237.         create_or_update || raise(RecordNotSaved)  
     238.       end  
     239.   
     240.       def destroy  
     241.         unless new_record?  
     242.           connection.delete <<-end_sql, "#{self.class.name} Destroy"  
     243.             DELETE FROM #{self.class.table_name}  
     244.             WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}  
     245.           end_sql  
     246.         end  
     247.   
     248.         freeze  
     249.       end  
     250.   
     251.       def update_attribute(name, value)  
     252.         send(name.to_s + '=', value)  
     253.         save  
     254.       end  
     255.   
     256.       def update_attributes(attributes)  
     257.         self.attributes = attributes  
     258.         save  
     259.       end  
     260.   
     261.       def update_attributes!(attributes)  
     262.         self.attributes = attributes  
     263.         save!  
     264.       end  
     265.   
     266.     private  
     267.   
     268.       def create_or_update  
     269.         raise ReadOnlyRecord if readonly?  
     270.         result = new_record? ? create : update  
     271.         result != false  
     272.       end  
     273.   
     274.       def update  
     275.         connection.update(  
     276.           "UPDATE #{self.class.table_name} " +  
     277.           "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +  
     278.           "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}",  
     279.           "#{self.class.name} Update"  
     280.         )  
     281.       end  
     282.   
     283.       def create  
     284.         if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)  
     285.           self.id = connection.next_sequence_value(self.class.sequence_name)  
     286.         end  
     287.   
     288.         self.id = connection.insert(  
     289.           "INSERT INTO #{self.class.table_name} " +  
     290.           "(#{quoted_column_names.join(', ')}) " +  
     291.           "VALUES(#{attributes_with_quotes.values.join(', ')})",  
     292.           "#{self.class.name} Create",  
     293.           self.class.primary_key, self.id, self.class.sequence_name  
     294.         )  
     295.   
     296.         @new_record = false  
     297.         id  
     298.       end  
     299.   
     300.       def method_missing(method_id, *args, &block)  
     301.         method_name = method_id.to_s  
     302.         if @attributes.include?(method_name) or  
     303.             (md = /\?$/.match(method_name) and  
     304.             @attributes.include?(query_method_name = md.pre_match) and  
     305.             method_name = query_method_name)  
     306.           define_read_methods if self.class.read_methods.empty? && self.class.generate_read_methods  
     307.           md ? query_attribute(method_name) : read_attribute(method_name)  
     308.         elsif self.class.primary_key.to_s == method_name  
     309.           id  
     310.         elsif md = self.class.match_attribute_method?(method_name)  
     311.           attribute_name, method_type = md.pre_match, md.to_s  
     312.           if @attributes.include?(attribute_name)  
     313.             __send__("attribute#{method_type}", attribute_name, *args, &block)  
     314.           else  
     315.             super  
     316.           end  
     317.         else  
     318.           super  
     319.         end  
     320.       end  
     321.   
     322.   end  
     323.   
     324. end

     base.rb这个文件比较大,它首先定义了Base类的Class Method,包括find、find_by_sql、create、update、destroy等
    然后定义了一些private方法,如find_initial、find_every、find_from_ids等方法,它们供public的find方法调用
    不出所料,private作用域里还定义了method_missing方法,它支持find_by_username、find_by_username_and_password、find_or_create_by_username等动态增加的方法
    protected作用域里定义了sanitize_sql等辅助方法,这样子类(即我们的Model)中也可以使用这些protected方法
    然后定义了Base类的public的Instance Method,如save、destroy、update_attribute、update_attributes等
    然后定义了Base类的private的Instance Method,如供public的save方法调用的create_or_update、create、update等方法
    然后定义了private的method_missing实例方法,供本类内其他实例方法访问本类的attributes

    3,activerecord-1.15.3\lib\active_record\connection_adapters\abstract\connection_specification.rb:

    1. module ActiveRecord  
       2.   class Base  
       3.     class ConnectionSpecification  
       4.       attr_reader :config, :adapter_method  
       5.       def initialize (config, adapter_method)  
       6.         @config, @adapter_method = config, adapter_method  
       7.       end  
       8.     end  
       9.   
      10.     class << self  
      11.   
      12.       def connection  
      13.         self.class.connection  
      14.       end  
      15.   
      16.       def self.establish_connection(spec = nil)  
      17.       case spec  
      18.         when nil  
      19.           raise AdapterNotSpecified unless defined? RAILS_ENV  
      20.           establish_connection(RAILS_ENV)  
      21.         when ConnectionSpecification  
      22.           clear_active_connection_name  
      23.           @active_connection_name = name  
      24.           @@defined_connections[name] = spec  
      25.         when Symbol, String  
      26.           if configuration = configurations[spec.to_s]  
      27.             establish_connection(configuration)  
      28.           else  
      29.             raise AdapterNotSpecified, "#{spec} database is not configured"  
      30.           end  
      31.         else  
      32.           spec = spec.symbolize_keys  
      33.           unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end  
      34.           adapter_method = "#{spec[:adapter]}_connection"  
      35.           unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end  
      36.           remove_connection  
      37.           establish_connection(ConnectionSpecification.new(spec, adapter_method))  
      38.       end  
      39.     end  
      40.   end  
      41. end


    connection_specification.rb文件定义了ActiveRecord::Base建立获取数据库连接相关的方法

    4,activerecord-1.15.3\lib\active_record\connection_adapters\mysql_adapter.rb:

    1. module ActiveRecord  
       2.   class Base  
       3.     def self.mysql_connection(config)  
       4.       config = config.symbolize_keys  
       5.       host = config[:host]  
       6.       port = config[:port]  
       7.       socket = config[:socket]  
       8.       username = config[:username]  
       9.       password = config[:password]  
      10.   
      11.       if config.has_key?(:database)  
      12.         database = config[:database]  
      13.       else  
      14.         raise ArgumentError, "No database specified. Missing argument: database."  
      15.       end  
      16.   
      17.       require_mysql  
      18.       mysql = Mysql.init  
      19.       mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]  
      20.   
      21.       ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)  
      22.     end  
      23.   end  
      24.   
      25.   module ConnectionAdapters  
      26.     class MysqlAdapter < AbstractAdapter  
      27.       def initialize(connection, logger, connection_options, config)  
      28.         super(connection, logger)  
      29.         @connection_options, @config = connection_options, config  
      30.   
      31.         connect  
      32.       end  
      33.   
      34.       def execute(sql, name = nil) #:nodoc:  
      35.         log(sql, name) { @connection.query(sql) }  
      36.       rescue ActiveRecord::StatementInvalid => exception  
      37.         if exception.message.split(":").first =~ /Packets out of order/  
      38.           raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information.  If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."  
      39.         else  
      40.           raise  
      41.         end  
      42.       end  
      43.   
      44.       def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:  
      45.         execute(sql, name = nil)  
      46.         id_value || @connection.insert_id  
      47.       end  
      48.   
      49.       def update(sql, name = nil) #:nodoc:  
      50.         execute(sql, name)  
      51.         @connection.affected_rows  
      52.       end  
      53.   
      54.       private  
      55.         def connect  
      56.           encoding = @config[:encoding]  
      57.           if encoding  
      58.             @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil  
      59.           end  
      60.           @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher]) if @config[:sslkey]  
      61.           @connection.real_connect(*@connection_options)  
      62.           execute("SET NAMES '#{encoding}'") if encoding  
      63.   
      64.           execute("SET SQL_AUTO_IS_NULL=0")  
      65.         end  
      66.     end  
      67.   end  
      68. end

    这个文件是mysql的数据库adapter的例子,其中mysql_connection->connect->real_connect方法会在establish_connection中调用

    5,activerecord-1.15.3\lib\active_record\vendor\mysql.rb:

    1. class Mysql  
       2.   
       3.   def initialize(*args)  
       4.     @client_flag = 0  
       5.     @max_allowed_packet = MAX_ALLOWED_PACKET  
       6.     @query_with_result = true  
       7.     @status = :STATUS_READY  
       8.     if args[0] != :INIT then  
       9.       real_connect(*args)  
      10.     end  
      11.   end  
      12.   
      13.   def real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=nil)  
      14.     @server_status = SERVER_STATUS_AUTOCOMMIT  
      15.     if (host == nil or host == "localhost") and defined? UNIXSocket then  
      16.       unix_socket = socket || ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_ADDR  
      17.       sock = UNIXSocket::new(unix_socket)  
      18.       @host_info = Error::err(Error::CR_LOCALHOST_CONNECTION)  
      19.       @unix_socket = unix_socket  
      20.     else        
      21.       sock = TCPSocket::new(host, port||ENV["MYSQL_TCP_PORT"]||(Socket::getservbyname("mysql","tcp") rescue MYSQL_PORT))  
      22.       @host_info = sprintf Error::err(Error::CR_TCP_CONNECTION), host  
      23.     end  
      24.     @host = host ? host.dup : nil  
      25.     sock.setsockopt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true  
      26.     @net = Net::new sock  
      27.   
      28.     a = read  
      29.     @protocol_version = a.slice!(0)  
      30.     @server_version, a = a.split(/\0/,2)  
      31.     @thread_id, @scramble_buff = a.slice!(0,13).unpack("La8")  
      32.     if a.size >= 2 then  
      33.       @server_capabilities, = a.slice!(0,2).unpack("v")  
      34.     end  
      35.     if a.size >= 16 then  
      36.       @server_language, @server_status = a.slice!(0,3).unpack("cv")  
      37.     end  
      38.   
      39.     flag = 0 if flag == nil  
      40.     flag |= @client_flag | CLIENT_CAPABILITIES  
      41.     flag |= CLIENT_CONNECT_WITH_DB if db  
      42.   
      43.     @pre_411 = (0 == @server_capabilities & PROTO_AUTH41)  
      44.     if @pre_411  
      45.       data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+  
      46.              (user||"")+"\0"+  
      47.                    scramble(passwd, @scramble_buff, @protocol_version==9)  
      48.     else  
      49.       dummy, @salt2 = a.unpack("a13a12")  
      50.       @scramble_buff += @salt2  
      51.       flag |= PROTO_AUTH41  
      52.       data = Net::int4str(flag) + Net::int4str(@max_allowed_packet) +  
      53.              ([8] + Array.new(23, 0)).pack("c24") + (user||"")+"\0"+  
      54.              scramble41(passwd, @scramble_buff)  
      55.     end  
      56.   
      57.     if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0  
      58.       data << "\0" if @pre_411  
      59.       data << db  
      60.       @db = db.dup  
      61.     end  
      62.     write data  
      63.     pkt = read  
      64.     handle_auth_fallback(pkt, passwd)  
      65.     ObjectSpace.define_finalizer(self, Mysql.finalizer(@net))  
      66.     self  
      67.   end  
      68.   
      69.   alias :connect :real_connect  
      70.   
      71.   def real_query(query)  
      72.     command COM_QUERY, query, true  
      73.     read_query_result  
      74.     self  
      75.   end  
      76.   
      77.   def query(query)  
      78.     real_query query  
      79.     if not @query_with_result then  
      80.       return self  
      81.     end  
      82.     if @field_count == 0 then  
      83.       return nil  
      84.     end  
      85.     store_result  
      86.   end  
      87.   
      88. end


    其中mysql.rb里的real_connect定义了Mysql数据库真正建立连接的方法

    这次主要研究了ActiveRecord的基本架构、CRUD方法的封装以及以Mysql为例子的数据库连接相关的代码,歇会再聊,咳咳

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 评论 】 【收藏】 【 推荐给朋友 】 【字体: 】 【关闭
评论:共0条

发表评论:
评论: 
    

Copyright (C) 2005 EasyJF.com 简易java框架网 渝ICP备06004507号
如有意见请与我们联系