Gorm源码学习-数据库连接
1 前言
gorm源码地址: Gorm , 本文基于commit:cef3de694d9615c574e82dfa0b50fc7ea2816f3e
官方入门指南: Dosc
2 连接数据库代码示例
目前Gorm官方支持的数据库类型有:MySQL, PostgreSQL, SQLite, SQL Server.
目前Go官方支持MySQL驱动,代码地址:mysql-driver
下面来看连接MySQL的数据库的基本代码
package main
import (
"fmt"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?timeout=%s&readTimeout=%s&writeTimeout=%s",
"root", "zbwmysql", "127.0.0.1", "3306", "user_db", "100ms", "2s", "3s")
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Printf("gorm open fail, err:%v dsn:%v\n", err, dsn)
}
mysqlDB, err := db.DB()
if err != nil {
fmt.Printf("get mysql db fail, err:%v\n", err)
}
// 参考 https://github.com/go-sql-driver/mysql#important-settings 获取详情
mysqlDB.SetConnMaxLifetime(time.Minute * 3) // 客户端将空闲连接主动断开的超时时间,官方建议小于5分钟
mysqlDB.SetMaxOpenConns(10) // 取决于服务器的配置
mysqlDB.SetMaxIdleConns(10) // 官方建议和SetMaxOpenConns相同
}
这里有必要看下 timeout
,readTimeout
,writeTimeout
,SetConnMaxLifetime
三个参数
timeout
是指 建立连接的一个超时时间
readTimeout
是指 I/O 读操作的超时时间
writeTimeout
是指 I/O 写操作的超时时间
SetConnMaxLifetime
是指客户端将空闲连接主动断开的超时时间,
如果设置为0,则连接池的连接将这一直被复用,但是系统会主动将长时间的连接杀掉,
因此若客户端再次使用长时间空闲的连接将会报错,driver: bad connection
,具体如下
问题的修复记录,可以看 issues-1120
3 连接数据库代码分析
从上一节看,Gorm连接数据库的过程只需要调用一个函数,
func Open(dialector Dialector, opts ...Option) (db *DB, err error)
但是Gorm目前是MySQL, PostgreSQL, SQLite, SQL Server,四种类型的数据库的,这个是怎么做到的呢?
这就需要具体看下请求参数Dialector
和 返回参数DB
及Open
函数内部实现
首先让我们先看看Golang的interface
类型
3.1 interface
理解
An interface type is defined as a set of method signatures.
A value of interface type can hold any value that implements those methods.
A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword.
以上摘抄自A Tour of Go , interface
是一种包含方法定义的类型,通过实现该interface
的所有方法来隐式实现该接口。
3.2 Dialector
接口定义
Dialector
定义如下,这里对部分方法加了注释,方便理解。
// Dialector GORM database dialector
type 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 // SQL语句格式化输出
}
不仅仅Mysql驱动,PostgreSQL, SQLite, SQL Server驱动都得实现Dialector
中定义的全部方法。
并且这些方法恰恰是不同数据库的区别所在,比如不同数据库的数据类型是有差异的,即使含义相同,写法也可能不同,
因此gorm的数据类型映射到不同数据库能识别的类型,这就是DataTypeOf
实现的功能。
可以在go-gorm找到各种数据库的Dialector实现,如PostgreSQL Dialector
,MySQL Dialector
3.3 DB
结构体定义
DB
定义如下,这里对部分方法加了注释,方便理解。
// DB GORM DB definition
type DB struct {
*Config // 连接及其连接相关信息等
Error error
RowsAffected int64
Statement *Statement // SQL语句执行相关信息
clone int
}
其中,Config
中会保留连接ConnPool
、CRUD相关的函数callbacks
等信息,部分代码代码如下,完整代码见gorm.Config
// Config GORM config
type Config struct {
// ClauseBuilders clause builder
ClauseBuilders map[string]clause.ClauseBuilder
// ConnPool db conn pool
ConnPool ConnPool
// Dialector database dialector
Dialector
// Plugins registered plugins
Plugins map[string]Plugin
callbacks *callbacks
cacheStore *sync.Map
}
看了Open
函数的请求参数和返回参数,接下来我们看看内部的具体实现
3.3 Gorm.Open
实现分析
Gorm.Open
完整代码可以在Github上看到。这里重点关注两个地方
- 注册CRUD的回调函数,
db.callbacks = initializeCallbacks(db)
,具体实现如下
func initializeCallbacks(db *DB) *callbacks {
return &callbacks{
processors: map[string]*processor{
"create": {db: db},
"query": {db: db},
"update": {db: db},
"delete": {db: db},
"row": {db: db},
"raw": {db: db},
},
}
}
- 调用具体函数的初始化方法
if config.Dialector != nil {
err = config.Dialector.Initialize(db)
}
这里会根据具体Dialector
的具体值调用对应的Initialize
方法。
如果Dialector
为 mysql.Open(dsn)
的返回值,那就会调用Gorm MySQL
驱动的Initialize
方法。
3.4MySQL Dialector
的 Initialize
方法实现分析
Initialize
主要干了两件事情,调用sql.Open
、注册CRUD的处理函数及对应的钩子函数。
钩子函数是在创建、查询、更新、删除等操作之前、之后调用的函数。
3.4.1 调用sql.Open,该函数可能只是校验下参数,并没有实际建立连接
db.ConnPool, err = sql.Open(dialector.DriverName, dialector.DSN)
其中,sql.Open
声明如下
func Open(driverName, dataSourceName string) (*DB, error)
db.ConnPool
是interface
类型,定义如下
// ConnPool db conns pool interface
type ConnPool interface {
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}
sql.DB
是结构体类型,gorm.ConnPool
是 interface
类型,因此sql.DB
实现了gorm.ConnPool
定义的四个方法,因此CRUD操作会通过gorm.ConnPool
调用到sql.DB
实现的这四个函数实现。
这里也应证了前面的说明
A value of interface type can hold any value that implements those methods.
通过看源码,sql.Conn
和sql.Tx
也实现了gorm.ConnPool
定义的四个方法。
3.4.2 注册CRUD相关函数,这里只截取callbacks.RegisterDefaultCallbacks
的部分实现。
func RegisterDefaultCallbacks(db *gorm.DB, config *Config) {
createCallback := db.Callback().Create()
createCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction)
createCallback.Register("gorm:before_create", BeforeCreate)
createCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(true))
createCallback.Register("gorm:create", Create(config))
createCallback.Register("gorm:save_after_associations", SaveAfterAssociations(true))
createCallback.Register("gorm:after_create", AfterCreate)
createCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction)
createCallback.Clauses = config.CreateClauses
}
在gorm.Open的过程中注册了创建记录时的回调函数createCallback.Register("gorm:create", Create(config))
具体的细节在后续章节展开,这里就细说。
此外,从代码可以看出,这里注册了在创建操作之前、之后调用的钩子方法。
Gorm源码学习-数据库连接的更多相关文章
- Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题
Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...
- Mybatis源码学习之DataSource(七)_2
接上节数据源,本节我们将继续学习未完成的部分,包括无连接池情况下的分析.为什么使用连接池.及mybatis连接池的具体管理原理 不使用连接池的UnpooledDataSource 当 的type属性为 ...
- Mybatis源码学习之整体架构(一)
简述 关于ORM的定义,我们引用了一下百度百科给出的定义,总体来说ORM就是提供给开发人员API,方便操作关系型数据库的,封装了对数据库操作的过程,同时提供对象与数据之间的映射功能,解放了开发人员对访 ...
- Seata 1.5.2 源码学习
文章有点长,我决定用半个小时来给您分享~ 基于Seata 1.5.2,项目中用 seata-spring-boot-starter 1. SeataDataSourceAutoConfiguratio ...
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- jQuery源码学习感想
还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
随机推荐
- AVL Tree (1) - Definition, find and Rotation
1. 定义 (15-1) [AVL tree]: 一棵空二叉树是 AVL tree; 若 T 是一棵非空二叉树, 则 T 满足以下两个条件时, T 是一棵 AVL tree: T_LeftSubtre ...
- OKR之剑(理念篇)02—— OKR布道之旅
作者:vivo互联网平台产品研发团队 1.我们是如何引入的 1.1.企业文化匹配 大概是在2013年底,一些创业者在硅谷深受OKR洗礼,并在自己的公司内小范围运用,以此OKR开始传入中国.而vivo初 ...
- 静态文件:Static Files
官方文档地址:https://fastapi.tiangolo.com/zh/tutorial/static-files/ from fastapi import FastAPI from fasta ...
- Elasticsearch:Java 运用示例
在今天的文章中,我们来介绍如何使用Java来访问Elasticsearch. 首先,我们必须在我们的系统中安装Elasticsearch. Maven 配置 针对Java的开发,我们必须在pom.xm ...
- 21. Fluentd输出插件:rewrite_tag_filter用法详解
我们在做日志处理时,往往会从多个源服务器收集日志,然后在一个(或一组)中心服务器做日志聚合分析. 源服务器上的日志可能属于同一应用类型,也可能属于不同应用类型.我们可能需要在聚合服务器上对这些不同类型 ...
- echarts BAR堆叠图显示百分比
在使用echarts的堆叠图时,我们鼠标悬停的浮窗里的信息可能并不是我们想要的信息,这个时候需要我们配置一下,因为堆叠图的trigger的类型是坐标轴触发的,数据是多组数据,所以我们需要遍历一下数据, ...
- Vue子->父组件传值
父组件引入: Import Test from'' 父页面使用: <Test ref="test" @m1="m2"><Test/> 子 ...
- TCP和UDP的区别与联系以及网络字节序和主机字节序的转换函数实践
TCP和UDP的区别 TCP是一个面向连接的.可靠的.基于字节流的传输层协议. 而UDP是一个面向无连接的传输层协议. 具体来分析,和 UDP 相比,TCP 有三大核心特性: 面向连接:所谓的连接,指 ...
- Mybatis PageHelper 使用的注意事项
什么时候会导致不安全的分页? PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的. 只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查 ...
- 记一次某制造业ERP系统 CPU打爆事故分析
一:背景 1.讲故事 前些天有位朋友微信找到我,说他的程序出现了CPU阶段性爆高,过了一会就下去了,咨询下这个爆高阶段程序内部到底发生了什么? 画个图大概是下面这样,你懂的. 按经验来说,这种情况一般 ...