当前位置:首页 > 科技  > 软件

深入理解gorm是如何和数据库建立连接的

来源: 责编: 时间:2023-11-07 09:14:37 245观看
导读大家好,我是渔夫子。本期和大家一起学习下gorm是如何和数据库建立连接的。一、gorm.Open通常情况下,我们是通过gorm.Open函数就能在应用层和数据建立连接。如下:import ( "gorm.io/driver/mysql" "gorm.io/gorm")func

大家好,我是渔夫子。o8W28资讯网——每日最新资讯28at.com

本期和大家一起学习下gorm是如何和数据库建立连接的。o8W28资讯网——每日最新资讯28at.com

一、gorm.Open

通常情况下,我们是通过gorm.Open函数就能在应用层和数据建立连接。如下:o8W28资讯网——每日最新资讯28at.com

import (  "gorm.io/driver/mysql"  "gorm.io/gorm")func main() {  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})}

在该代码片段中,我们传入了数据库的用户名、密码、地址以及数据库和数据库对应的配置。然后通过gorm.Open函数就和数据库建立连接了,gorm.Open函数返回的是一个gorm.DB对象。如下:o8W28资讯网——每日最新资讯28at.com

// DB GORM DB definitiontype DB struct { *Config Error        error RowsAffected int64 Statement    *Statement clone        int}

在该数据结构中并没有和数据库连接相关的字段,那gorm.Open到底是如何和mysql数据库建立连接的呢?我们继续深入gorm.Open函数和mysql.Open函数的详细内容。o8W28资讯网——每日最新资讯28at.com

二、gorm.Open函数

在gorm.Open函数中,传入的参数是一个Dialector�接口类型的dialector变量。我们看到会将传入的Dialector变量赋值给配置config.Dialector,如下:o8W28资讯网——每日最新资讯28at.com

config.Dialector = dialector

然后,又通过config.Dialector的Initialize函数对数据库进行了初始化。如下:o8W28资讯网——每日最新资讯28at.com

err = config.Dialector.Initialize(db)

那么,Dialector是什么呢?Dialector是通过gorm.Open函数的第一个参数传进来的。我们看具体的是什么。o8W28资讯网——每日最新资讯28at.com

三、Dialector参数

在gorm.Open函数中,第一个参数是Dialector类型的参数,这是一个接口类型。也就是说只要实现了该接口,就能作为一个Dialector。这也就是gorm能够针对很多数据库进行操作的原因。比如MySQL、ClickHouse等。Dialector接口类型定义如下:o8W28资讯网——每日最新资讯28at.com

// Dialector GORM database dialectortype Dialector interface { Name() string Initialize(*DB) error Migrator(db *DB) Migrator DataTypeOf(*schema.Field) string DefaultValueOf(*schema.Field) clause.Expression BindVarTo(writer clause.Writer, stmt *Statement, v interface{}) QuoteTo(clause.Writer, string) Explain(sql string, vars ...interface{}) string}

具体到mysql的数据库,我们看到是通过gorm.io/driver/mysql�库的Open函数来初始化的。我们看下mysql.Open函数的实现,如下:o8W28资讯网——每日最新资讯28at.com

func Open(dsn string) gorm.Dialector { dsnConf, _ := mysql.ParseDSN(dsn) return &Dialector{Config: &Config{DSN: dsn, DSNConfig: dsnConf}}}

该函数接收一个dsn的字符串,也就是第一节中我们提供的和数据库相关的账号密码等连接数据的信息。然后,返回的是mysql驱动包中的Dialector对象。该对象包含了相关的配置。o8W28资讯网——每日最新资讯28at.com

然后,是在gorm.Open函数中,调用了Dialector的Initialize�函数。我们看下该函数中和数据库连接相关的逻辑。o8W28资讯网——每日最新资讯28at.com

func (dialector Dialector) Initialize(db *gorm.DB) (err error) { if dialector.DriverName == "" {  dialector.DriverName = "mysql" } if dialector.DefaultDatetimePrecision == nil {  dialector.DefaultDatetimePrecision = &defaultDatetimePrecision } if dialector.Conn != nil {  db.ConnPool = dialector.Conn } else {  db.ConnPool, err = sql.Open(dialector.DriverName, dialector.DSN)  if err != nil {   return err  } }    // 省略其他代码}

大家看到,在第13行的地方,是通过sql.Open函数来进行具体的和数据库进行连接的。然后返回的对象是sql.DB类型,大家注意,这里的sql.DB类型是go标准库中的DB,而非gorm库中的DB。返回的sql.DB对象赋值给了gorm中DB对象中的ConnPool。o8W28资讯网——每日最新资讯28at.com

同时,在gorm.Open函数中,还将db.ConnPool对象赋值给了db.Statement.ConnPool对象。到这里是不是gorm.DB结构体中的字段就和数据库的具体连接关联起来。o8W28资讯网——每日最新资讯28at.com

接下来,我们再看看sql.Open函数是如何和数据库建立连接的。o8W28资讯网——每日最新资讯28at.com

四、sql.Open函数

先看sql.Open函数的源代码:o8W28资讯网——每日最新资讯28at.com

func Open(driverName, dataSourceName string) (*DB, error) { driversMu.RLock() driveri, ok := drivers[driverName] driversMu.RUnlock() if !ok {  return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) } if driverCtx, ok := driveri.(driver.DriverContext); ok {  connector, err := driverCtx.OpenConnector(dataSourceName)  if err != nil {   return nil, err  }  return OpenDB(connector), nil } return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil}

我们先简单分析下上述代码。在第3行处,从drivers中获取对应的驱动名称的具体驱动对象。这里的driverName是mysql。然后从第9行到第14行是执行具体驱动程序的连接函数。o8W28资讯网——每日最新资讯28at.com

首先,我们先看从drivers中根据驱动名称mysql获取驱动对象的逻辑。drivers是标准库sql中的一个map类型,如下:o8W28资讯网——每日最新资讯28at.com

drivers   = make(map[string]driver.Driver)

该变量是通过sql包中的Register函数进行注册的:o8W28资讯网——每日最新资讯28at.com

func Register(name string, driver driver.Driver) { driversMu.Lock() defer driversMu.Unlock() if driver == nil {  panic("sql: Register driver is nil") } if _, dup := drivers[name]; dup {  panic("sql: Register called twice for driver " + name) } drivers[name] = driver}

该函数又是在哪里进行调用的呢?我们再回调gorm.Open函数中,第一个参数调用的是mysql.Open函数。也就是说引入了库gorm.io/driver/mysql�,在该库中,我们看到又引入了github.com/go-sql-driver/mysql�库。该库中有一个init方法,如下:o8W28资讯网——每日最新资讯28at.com

func init() { sql.Register("mysql", &MySQLDriver{})}

原来,这里调用了标准库sql中的Register函数,将“mysql”和对应的驱动对象MySQLDriver进行了注册关联。o8W28资讯网——每日最新资讯28at.com

我们再返回来看sql.Open函数的具体实现。那这里就继续调用MySQLDriver的OpenConnector方法。我们看下该方法的实现如下:o8W28资讯网——每日最新资讯28at.com

// OpenConnector implements driver.DriverContext.func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) { cfg, err := ParseDSN(dsn) if err != nil {  return nil, err } return &connector{  cfg: cfg, }, nil}

该函数首先通过ParseDSN解析dsn字符串中的用户名,地址,密码等配置选项。然后返回一个connector对象。该connector对象就是在sql.Open函数中执行的OpenDB(connector)函数中的参数。o8W28资讯网——每日最新资讯28at.com

我们继续看sql.OpenDB函数的实现,如下:o8W28资讯网——每日最新资讯28at.com

func OpenDB(c driver.Connector) *DB { ctx, cancel := context.WithCancel(context.Background()) db := &DB{  connector:    c,  openerCh:     make(chan struct{}, connectionRequestQueueSize),  lastPut:      make(map[*driverConn]string),  connRequests: make(map[uint64]chan connRequest),  stop:         cancel, } go db.connectionOpener(ctx) return db}

这里首先构建了一个sql.DB对象,同时执行了一个协程进行数据库的连接:o8W28资讯网——每日最新资讯28at.com

go db.connectionOpener(ctx)

接着看db.connectionOpener函数的实现,如下:o8W28资讯网——每日最新资讯28at.com

// Runs in a separate goroutine, opens new connections when requested.func (db *DB) connectionOpener(ctx context.Context) { for {  select {  case <-ctx.Done():   return  case <-db.openerCh:   db.openNewConnection(ctx)  } }}

这里,有一个db.openNewConnection函数,根据名字可知是打开新的连接。其实现如下:o8W28资讯网——每日最新资讯28at.com

// Open one new connectionfunc (db *DB) openNewConnection(ctx context.Context) { ci, err := db.connector.Connect(ctx)    // ...省略代码 dc := &driverConn{  db:         db,  createdAt:  nowFunc(),  returnedAt: nowFunc(),  ci:         ci, } if db.putConnDBLocked(dc, err) {  db.addDepLocked(dc, dc) } else {  db.numOpen--  ci.Close() }}

这里我们看到有一个db.connector.Connect函数,connector就是github.com/go-sql-driver/mysql�库中的connector对象。我们回到该库,查看其Connect函数的实现:o8W28资讯网——每日最新资讯28at.com

// Connect implements driver.Connector interface.// Connect returns a connection to the database.func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { var err error // New mysqlConn mc := &mysqlConn{  maxAllowedPacket: maxPacketSize,  maxWriteSize:     maxPacketSize - 1,  closech:          make(chan struct{}),  cfg:              c.cfg, } mc.parseTime = mc.cfg.ParseTime // Connect to Server dialsLock.RLock() dial, ok := dials[mc.cfg.Net] dialsLock.RUnlock() if ok {     //...省略代码 } else {  nd := net.Dialer{Timeout: mc.cfg.Timeout}  mcConn, err = nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr) } // Enable TCP Keepalives on TCP connections if tc, ok := mcConn.(*net.TCPConn); ok {  if err := tc.SetKeepAlive(true); err != nil {            //...省略代码  } } mc.buf = newBuffer(mcConn) //... // Reading Handshake Initialization Packet authData, plugin, err := mc.readHandshakePacket() if err != nil {  mc.cleanup()  return nil, err } // Send Client Authentication Packet authResp, err := mc.auth(authData, plugin) if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {  mc.cleanup()  return nil, err } // Handle response to auth packet, switch methods if possible if err = mc.handleAuthResult(authData, plugin); err != nil {  mc.cleanup()  return nil, err } return mc, nil}

这里我们主要看第22到23行,这里进行了实际的拨号操作,也就是和数据库真正的建立了连接。再看第27行,断言是一个TCP连接。第37行,进行了握手处理;第45行,进行了认证处理。最终返回了一个mysqlConn对象。该mysqlConn结构体中包含字段如下:o8W28资讯网——每日最新资讯28at.com

type mysqlConn struct { buf              buffer netConn          net.Conn rawConn          net.Conn // underlying connection when netConn is TLS connection.    // ...}

其中,netConn就是和数据库建立的TCP的连接。o8W28资讯网——每日最新资讯28at.com

五、从mysql到gorm.DB

我们再总结下上述和mysql相关的各个对象之间的关联关系。从mysql开始逆向推导。如下:图片o8W28资讯网——每日最新资讯28at.com

也就是说,我们在使用gorm进行数据库操作的时候,最终都是从gorm.Statement.ConnPool中获取的数据库连接来具体执行sql语句的。o8W28资讯网——每日最新资讯28at.com

好了,今天gorm是如何和mysql建立连接的过程就跟大家分享到这里。o8W28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-17406-0.html深入理解gorm是如何和数据库建立连接的

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 蓝绿部署在G行移动办公平台的应用实践

下一篇: Python这些冷门特性,当初也没教啊

标签:
  • 热门焦点
  • K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    Redmi的后性能时代战略发布会今天下午如期举办,在本次发布会上,Redmi公布了多项关于和联发科的深度合作,以及新机K60 Ultra在软件和硬件方面的特性,例如:“K60 至尊版,双芯旗舰
  • 一年经验在二线城市面试后端的经验分享

    一年经验在二线城市面试后端的经验分享

    忠告这篇文章只适合2年内工作经验、甚至没有工作经验的朋友阅读。如果你是2年以上工作经验,请果断划走,对你没啥帮助~主人公这篇文章内容来自 「升职加薪」星球星友 的投稿,坐
  • 让我们一起聊聊文件的操作

    让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • JavaScript学习 -AES加密算法

    JavaScript学习 -AES加密算法

    引言在当今数字化时代,前端应用程序扮演着重要角色,用户的敏感数据经常在前端进行加密和解密操作。然而,这样的操作在网络传输和存储中可能会受到恶意攻击的威胁。为了确保数据
  • 一篇文章带你了解 CSS 属性选择器

    一篇文章带你了解 CSS 属性选择器

    属性选择器对带有指定属性的 HTML 元素设置样式。可以为拥有指定属性的 HTML 元素设置样式,而不仅限于 class 和 id 属性。一、了解属性选择器CSS属性选择器提供了一种简单而
  • 慕岩炮轰抖音,百合网今何在?

    慕岩炮轰抖音,百合网今何在?

    来源:价值研究所 作者:Hernanderz&ldquo;难道就因为自己的一个产品牛逼了,从客服到总裁,都不愿意正视自己产品和运营上的问题,选择逃避了吗?&rdquo;这一番话,出自百合网联合创
  • 认真聊聊东方甄选:如何告别低垂的果实

    认真聊聊东方甄选:如何告别低垂的果实

    来源:山核桃作者:财经无忌爆火一年后,俞敏洪和他的东方甄选依旧是颇受外界关心的&ldquo;网红&rdquo;。7月5日至9日,为期5天的东方甄选&ldquo;甘肃行&rdquo;首次在自有App内直播,
  • 三星显示已开始为AR设备研发硅基LED微显示屏

    三星显示已开始为AR设备研发硅基LED微显示屏

    7月18日消息,据外媒报道,随着苹果首款头显产品Vision Pro在6月份正式推出,AR/VR/MR等头显产品也就将成为各大公司下一个重要的竞争领域,对显示屏这一关
  • 英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    据10 月 30 日外媒 TheVerge 消息报道,英特尔 Xe HPG Arc Alchemist 的正面实被曝光,不仅拥有 512 EU 版显卡,还拥有 128EU 的单风扇版本。另外,这款显卡 PCB
Top