深入分析rails数据库连接流程

第一部分:
Method 1)
def self.establish_connection(spec = nil)
casespec
# 1
whennil
raise AdapterNotSpecifiedunlessdefined?RAILS_ENV
establish_connection(RAILS_ENV)
whenConnectionSpecification
# 2
clear_active_connection_name
@active_connection_name
= name
@@defined_connections[name]
= spec
whenSymbol,String
# 3
pconfigurations
if configuration = configurations[spec.to_s]
establish_connection(configuration)
else
raise AdapterNotSpecified, " #{spec}databaseisnotconfigured "
end
else # 4
spec = spec.symbolize_keys
unlessspec.key?(:adapter)then
raise AdapterNotSpecified, " databaseconfigurationdoesnotspecifyadapter " end
adapter_method
= " #{spec[:adapter]}_connection "
unlessrespond_to?(adapter_method)then
raise AdapterNotFound, " databaseconfigurationspecifiesnonexistent#{spec[:adapter]}adapter " end
remove_connection
establish_connection(ConnectionSpecification.new(spec,adapter_method))
end
end
如果不带参数调用 establish_connection,进入#1,rails会给一个默认参数,那就是你得rails环境 (production, development, test),这是进入#2,rails会根据你得参数在databse.yml下找到对应的配置,此时是一个 hash对象,进入#3, 首先查看你得hash中key为:adpater的item,不存在就抛错,如果存在(本例为mysql),则继续看是否存在mysql_connection方法,不存在就抛错,你可以在#{activerecordhome}\lib\active_record\connection_adapters,看到各种数据库驱动接口文件,每个文件重新打开了ActionRecode:Base类,并定义了数据库类型_connection的方法,比如mysql_adapter.rb含有mysql_connection , db2_adapter.rb含有db2_adapter.rb等等,如果存在这样的方法,说明你得配置文件正确,于是开始调用remove_connection(见Method2),然后 实例化一个ConnectionSpecification作为参数进入#2调用clear_active_connection_name(见Method3),然后将类的实例变量@active_connection_name复值为类名,再将一个key为类名,value为上面那个ConnectionSpecification实例变量的item添加到类变量 @@defined_connections(一个hash)
Method 2)
def self.remove_connection(klass = self)
spec
= @@defined_connections[klass.name]
konn
= active_connections[klass.name]
@@defined_connections.delete_if{
| key,value | value == spec}
active_connections.delete_if{
| key,value | value == konn}
konn.disconnect!
if konn
spec.config
if spec
end
删除@@defined_connectionshash中value即配置相同的item
删除@@active_connectionshash中value即数据库连接相同的item,并关闭数据库连接
返回配置hash
Method 3)
def clear_active_connection_name # :nodoc:
@active_connection_name = nil
subclasses.each{
| klass | klass.clear_active_connection_name}
end
清除 @active_connection_name以及递归清除子类的 @active_connection_name
第二部分
以一个普通的查询开始
比如有个model类User
class User < ActiveRecord::Base;end
调用User.count
Method 4)
def connection

if @active_connection_name && (conn = active_connections[@active_connection_name])
conn

else
# retrieve_connectionsetsthecachekey.
conn = retrieve_connection
active_connections[@active_connection_name]
= conn
end
end
显然如果存在数据库连接active_connections[@active_connection_name],则返回这个连接对象以供数据库操作
如果不存在,那么调用方法retrieve_connection(见Method5),并将Method5返回的连接对象缓存到hash
@@active_connections,以@active_connection_name为key,连接对象为value
Method 5)
def self.retrieve_connection # :nodoc:
# Nameisnilifestablish_connectionhasn'tbeencalledfor
# someclassalongtheinheritancechainuptoAR::Baseyet.
if name = active_connection_name
if conn = active_connections[name]
# Verifytheconnection.
conn.verify!(@@verification_timeout)
elsifspec
= @@defined_connections[name]
# Activatethisconnectionspecification.
klass = name.constantize
klass.connection
= spec
conn
= active_connections[name]
end
end

conn
or raise ConnectionNotEstablished
end
首先调用active_connection_name(见Method6)获取连接key,如果key存在,再利用这个key,从hash
@@active_connections取出连接,如果连接存在则调用verify!,否则利用key,从hash @@defined_connections取出配置,通过调用constantize,将key转化为一个类对象,并调用该类对象的
connection =方法(以配置为参数)(见Method7 )
Method6:
def active_connection_name # :nodoc:
@active_connection_name ||=
if active_connections[name] || @@defined_connections[name]
name
elsifself
== ActiveRecord::Base
nil
else
superclass.active_connection_name
end
end
返回active_connection_name
Method7:
defself . connection = (spec) # :nodoc:
if spec . kind_of ? (ActiveRecord :: ConnectionAdapters :: AbstractAdapter) # 1
active_connections[name] = spec
elsif spec . kind_of ? (ConnectionSpecification) # 2
config = spec . config . reverse_merge( : allow_concurrency => @ @allow_concurrency )
self
. connection = self . send (spec . adapter_method , config)
elsif spec . nil ? # 3
raiseConnectionNotEstablished
else # 4
establish_connectionspec
end
end
如果直接传入的是一个连接对象,那么push到hash @@active_connections,以类名为key
如果传入是配置对象,则开始调用适配器方法(见Method8),在使用mysql的情况下是调用
mysql_connection,传入参数配置hash
然后跳到#1
如果未传参数,直接抛错
如果上面上个条件都不满足,则调用establish_connection
通常情况下,此方法进入#2,通过Method8返回连接对象,再进入#1
Method8
见文件mysql_adapter.rb
def self.mysql_connection(config) # :nodoc:
config = config.symbolize_keys
host
= config[:host]
port
= config[:port]
socket
= config[:socket]
username
= config[:username]?config[:username].to_s: ' root '
password
= config[:password].to_s

if config.has_key?(:database)
database
= config[:database]
else
raise ArgumentError, " Nodatabasespecified.Missingargument:database. "
end

require_mysql
# 1
mysql = Mysql.init # 2
mysql.ssl_set(config[:sslkey],config[:sslcert],config[:sslca],config[:sslcapath],config[:sslcipher]) if config[:sslkey] # 3

ConnectionAdapters::MysqlAdapter.new(mysql,logger,[host,username,password,database,port,socket],config)
# 4
end
前面很好理解,从#1开始,#1加载active_record/vendor/mysql,#2调用init(Method9)初始化active_record/vendor/mysql文件中定义的类Mysql,返回Mysql类的实例mysql,#3看config中是否含有sskey,如果有就调用ssl_set,我发现并不存在这个方法,通过调用mysql.respond_to?(:ssl_set)发现运行过程中返回false,可能调用这个方法还需要其他的lib,默认的rails1.2.3是没有的,但因为sskey这种key,所以也不会出错#4实例化ConnectionAdapters::MysqlAdapter(Method10)
Method9
见文件mysql.rb
class << Mysql
def init()
Mysql::new:INIT
end
##。。。。。
end
def initialize( * args)
@client_flag
= 0
@max_allowed_packet
= MAX_ALLOWED_PACKET
@query_with_result
= true
@status
= :STATUS_READY
if args[0] != :INITthen
real_connect(
* args)
end
end
实例化Mysql传入参数:INIT,所以并不调用real_connect
Method10
见文件mysql_adapter.rb
def initialize(connection,logger,connection_options,config)
super(connection,logger)
@connection_options,@config
= connection_options,config

connect
end

实际调用 connect(Method11
Method11
见文件mysql_adapter.rb
def connect
encoding
= @config[:encoding]
if encoding
@connection.options(Mysql::SET_CHARSET_NAME,encoding)rescuenil
end
@connection.ssl_set(@config[:sslkey],@config[:sslcert],@config[:sslca],@config[:sslcapath],@config[:sslcipher])
if @config[:sslkey]
@connection.real_connect(
* @connection_options) # 1
execute( " SETNAMES'#{encoding}' " ) if encoding

# Bydefault,MySQL'whereidisnull'selectsthelastinsertedid.
# Turnthisoff.http://dev.rubyonrails.org/ticket/6778
execute( " SETSQL_AUTO_IS_NULL=0 " )
end
首先设置编码,最终的是#1,调用连接对象 real_connect进行物理连接
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值