日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

深入理解gorm是如何和數據庫建立連接的

來源: 責編: 時間:2023-11-07 09:14:37 347觀看
導讀大家好,我是漁夫子。本期和大家一起學習下gorm是如何和數據庫建立連接的。一、gorm.Open通常情況下,我們是通過gorm.Open函數就能在應用層和數據建立連接。如下:import ( "gorm.io/driver/mysql" "gorm.io/gorm")func

大家好,我是漁夫子。V7x28資訊網——每日最新資訊28at.com

本期和大家一起學習下gorm是如何和數據庫建立連接的。V7x28資訊網——每日最新資訊28at.com

一、gorm.Open

通常情況下,我們是通過gorm.Open函數就能在應用層和數據建立連接。如下:V7x28資訊網——每日最新資訊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對象。如下:V7x28資訊網——每日最新資訊28at.com

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

在該數據結構中并沒有和數據庫連接相關的字段,那gorm.Open到底是如何和mysql數據庫建立連接的呢?我們繼續深入gorm.Open函數和mysql.Open函數的詳細內容。V7x28資訊網——每日最新資訊28at.com

二、gorm.Open函數

在gorm.Open函數中,傳入的參數是一個Dialector?接口類型的dialector變量。我們看到會將傳入的Dialector變量賦值給配置config.Dialector,如下:V7x28資訊網——每日最新資訊28at.com

config.Dialector = dialector

然后,又通過config.Dialector的Initialize函數對數據庫進行了初始化。如下:V7x28資訊網——每日最新資訊28at.com

err = config.Dialector.Initialize(db)

那么,Dialector是什么呢?Dialector是通過gorm.Open函數的第一個參數傳進來的。我們看具體的是什么。V7x28資訊網——每日最新資訊28at.com

三、Dialector參數

在gorm.Open函數中,第一個參數是Dialector類型的參數,這是一個接口類型。也就是說只要實現了該接口,就能作為一個Dialector。這也就是gorm能夠針對很多數據庫進行操作的原因。比如MySQL、ClickHouse等。Dialector接口類型定義如下:V7x28資訊網——每日最新資訊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函數的實現,如下:V7x28資訊網——每日最新資訊28at.com

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

該函數接收一個dsn的字符串,也就是第一節中我們提供的和數據庫相關的賬號密碼等連接數據的信息。然后,返回的是mysql驅動包中的Dialector對象。該對象包含了相關的配置。V7x28資訊網——每日最新資訊28at.com

然后,是在gorm.Open函數中,調用了Dialector的Initialize?函數。我們看下該函數中和數據庫連接相關的邏輯。V7x28資訊網——每日最新資訊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。V7x28資訊網——每日最新資訊28at.com

同時,在gorm.Open函數中,還將db.ConnPool對象賦值給了db.Statement.ConnPool對象。到這里是不是gorm.DB結構體中的字段就和數據庫的具體連接關聯起來。V7x28資訊網——每日最新資訊28at.com

接下來,我們再看看sql.Open函數是如何和數據庫建立連接的。V7x28資訊網——每日最新資訊28at.com

四、sql.Open函數

先看sql.Open函數的源代碼:V7x28資訊網——每日最新資訊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行是執行具體驅動程序的連接函數。V7x28資訊網——每日最新資訊28at.com

首先,我們先看從drivers中根據驅動名稱mysql獲取驅動對象的邏輯。drivers是標準庫sql中的一個map類型,如下:V7x28資訊網——每日最新資訊28at.com

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

該變量是通過sql包中的Register函數進行注冊的:V7x28資訊網——每日最新資訊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方法,如下:V7x28資訊網——每日最新資訊28at.com

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

原來,這里調用了標準庫sql中的Register函數,將“mysql”和對應的驅動對象MySQLDriver進行了注冊關聯。V7x28資訊網——每日最新資訊28at.com

我們再返回來看sql.Open函數的具體實現。那這里就繼續調用MySQLDriver的OpenConnector方法。我們看下該方法的實現如下:V7x28資訊網——每日最新資訊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)函數中的參數。V7x28資訊網——每日最新資訊28at.com

我們繼續看sql.OpenDB函數的實現,如下:V7x28資訊網——每日最新資訊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對象,同時執行了一個協程進行數據庫的連接:V7x28資訊網——每日最新資訊28at.com

go db.connectionOpener(ctx)

接著看db.connectionOpener函數的實現,如下:V7x28資訊網——每日最新資訊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函數,根據名字可知是打開新的連接。其實現如下:V7x28資訊網——每日最新資訊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函數的實現:V7x28資訊網——每日最新資訊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結構體中包含字段如下:V7x28資訊網——每日最新資訊28at.com

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

其中,netConn就是和數據庫建立的TCP的連接。V7x28資訊網——每日最新資訊28at.com

五、從mysql到gorm.DB

我們再總結下上述和mysql相關的各個對象之間的關聯關系。從mysql開始逆向推導。如下:圖片V7x28資訊網——每日最新資訊28at.com

也就是說,我們在使用gorm進行數據庫操作的時候,最終都是從gorm.Statement.ConnPool中獲取的數據庫連接來具體執行sql語句的。V7x28資訊網——每日最新資訊28at.com

好了,今天gorm是如何和mysql建立連接的過程就跟大家分享到這里。V7x28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-17406-0.html深入理解gorm是如何和數據庫建立連接的

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 藍綠部署在G行移動辦公平臺的應用實踐

下一篇: Python這些冷門特性,當初也沒教啊

標簽:
  • 熱門焦點
  • 一加Ace2 Pro真機揭曉 鈦空灰配色質感拉滿

    終于,在經過了幾波預熱之后,一加Ace2 Pro的外觀真機圖在網上出現了。還是博主數碼閑聊站曝光的,這次的外觀設計還是延續了一加11的方案,只是細節上有了調整,例如新加入了鈦空灰
  • 之家push系統迭代之路

    前言在這個信息爆炸的互聯網時代,能夠及時準確獲取信息是當今社會要解決的關鍵問題之一。隨著之家用戶體量和內容規模的不斷增大,傳統的靠"主動拉"獲取信息的方式已不能滿足用
  • 破圈是B站頭上的緊箍咒

    來源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之每年的暑期檔都少不了瞄準追劇女孩們的古偶劇集,2021年有優酷的《山河令》,2022年有愛奇藝的《蒼蘭訣》,今年卻輪到小破站抓住了追
  • 本地生活這塊肥肉,拼多多也想吃一口

    出品/壹覽商業 作者/李彥編輯/木魚拼多多也看上本地生活這塊蛋糕了。近期,拼多多在App首頁&ldquo;充值中心&rdquo;入口上線了本機生活界面。壹覽商業發現,該界面目前主要
  • 年輕人的“職場羞恥感”,無處不在

    作者:馮曉亭 陶 淘 李 欣 張 琳 馬舒葉來源:燃次元&ldquo;人在職場,應該選擇什么樣的著裝?&rdquo;近日,在網絡上,一個與著裝相關的帖子引發關注,在該帖子里,一位在高級寫字樓亞洲金
  • 馮提莫簽約抖音公會 前“斗魚一姐”消失在直播間

    來源:直播觀察提起&ldquo;馮提莫&rdquo;這個名字,很多網友或許聽過,但應該不記得她是哪位主播了。其實,作為曾經的&ldquo;斗魚一姐&rdquo;,馮提莫在游戲直播的年代影響力不輸于現
  • 小米公益基金會捐贈2500萬元馳援北京、河北暴雨救災

    8月2日消息,今日小米科技創始人雷軍在其微博上發布消息稱,小米公益基金會宣布捐贈2500萬元馳援北京、河北暴雨救災。攜手抗災,京冀安康!以下為公告原文
  • 三星Galaxy Z Fold5官方渲染圖曝光:13.4mm折疊厚度依舊感人

    據官方此前宣布,三星將于7月26日在韓國首爾舉辦Unpacked活動,屆時將帶來帶來包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy Z Flip 5、
  • iQOO 11S新品發布會

    iQOO將在7月4日19:00舉行新品發布會,推出杭州亞運會電競賽事官方用機iQOO 11S。
Top 主站蜘蛛池模板: 芦溪县| 南陵县| 高邮市| 凤台县| 新竹县| 肇源县| 孟村| 游戏| 汨罗市| 巴楚县| 文登市| 扶风县| 柳河县| 横峰县| 安龙县| 贺兰县| 河北区| 宿松县| 巴塘县| 海门市| 大埔县| 惠来县| 公安县| 弥渡县| 惠州市| 长葛市| 长岭县| 特克斯县| 都昌县| 建宁县| 遵化市| 白银市| 鲁甸县| 林州市| 宝清县| 资中县| 遂昌县| 永胜县| 东丽区| 古交市| 浙江省|