连接数据库

下面是某个博客网站使用gorm连接mysql的代码片段。

if DB, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{Logger: newLogger}); err == nil {
        sqlDB, _ := DB.DB()
        sqlDB.SetMaxOpenConns(global.CONFIG.MySQLConfig.MaxOpenConns) // 设置数据库最大连接数
        sqlDB.SetMaxIdleConns(global.CONFIG.MySQLConfig.MaxIdleConns) // 设置上数据库最大闲置连接数
        global.DB = DB
    } else {
        panic("connect server failed")
    }

gorm.Open()需要两个参数,一个是关于mysql的相关配置,另一个是gorm的相关配置。gorm.Open()的作用就是连接数据库,初始化一些配置信息,并返回一个gorm.DB结构体,这个结构体封装了GORM框架所有的数据库操作方法。

gorm.DB这个结构体的内容如下:

type DB struct {
	*Config
	Error        error
	RowsAffected int64
	Statement    *Statement
	clone        int
}

gorm.DB这个结构体的第一个变量是一个指针,指向gorm包里面的Config结构体,这个结构体的主要作用就是可以自定义设置数据库的一些配置信息。比如关于事务、表名、外键、创建时间、预编译等配置信息可以自定义配置,如果不去配置这些信息就会使用系统默认的配置。关于这个Config结构体详见这几篇博客文章:


  • GORM 配置(官方文档)

对代码中sqlDb, _ := DB.DB()的理解如下:DB这个结构体封装了GORM框架所有的数据库操作方法。但是为什么需要使用sqlDb对象来操作连接池呢?用DB对象不行吗?DB()方法的源代码如下所示:

// DB returns `*sql.DB`
func (db *DB) DB() (*sql.DB, error) {
	connPool := db.ConnPool

	if dbConnector, ok := connPool.(GetDBConnector); ok && dbConnector != nil {
		return dbConnector.GetDBConn()
	}

	if sqldb, ok := connPool.(*sql.DB); ok {
		return sqldb, nil
	}

	return nil, ErrInvalidDB
}

从源码中可以看到,该方法的接收者是gorm包里面的bd对象(该对象的类型就是前面讲的DB结构体类型),但返回值是sql包里面的DB类型(并不是gorm包里面的DB类型)。再来看看sql包里面的DB类型是什么样子:

type DB struct {
	// Atomic access only. At top of struct to prevent mis-alignment
	// on 32-bit platforms. Of type time.Duration.
	waitDuration int64 // Total time waited for new connections.

	connector driver.Connector
	// numClosed is an atomic counter which represents a total number of
	// closed connections. Stmt.openStmt checks it before cleaning closed
	// connections in Stmt.css.
	numClosed uint64

	mu           sync.Mutex    // protects following fields
	freeConn     []*driverConn // free connections ordered by returnedAt oldest to newest
	connRequests map[uint64]chan connRequest
	nextRequest  uint64 // Next key to use in connRequests.
	numOpen      int    // number of opened and pending open connections
	// Used to signal the need for new connections
	// a goroutine running connectionOpener() reads on this chan and
	// maybeOpenNewConnections sends on the chan (one send per needed connection)
	// It is closed during db.Close(). The close tells the connectionOpener
	// goroutine to exit.
	openerCh          chan struct{}
	closed            bool
	dep               map[finalCloser]depSet
	lastPut           map[*driverConn]string // stacktrace of last conn's put; debug only
	maxIdleCount      int                    // zero means defaultMaxIdleConns; negative means 0
	maxOpen           int                    // <= 0 means unlimited
	maxLifetime       time.Duration          // maximum amount of time a connection may be reused
	maxIdleTime       time.Duration          // maximum amount of time a connection may be idle before being closed
	cleanerCh         chan struct{}
	waitCount         int64 // Total number of connections waited for.
	maxIdleClosed     int64 // Total number of connections closed due to idle count.
	maxIdleTimeClosed int64 // Total number of connections closed due to idle time.
	maxLifetimeClosed int64 // Total number of connections closed due to max connection lifetime limit.

	stop func() // stop cancels the connection opener.
}

上面sql包里面的DB结构体的大致内容就是关于数据库的连接数量,连接状态和时间等信息。那么现在回到DB()方法里面。该方法的主要功能就是创建一个连接池,然后就能通过一些参数来设置连接池或者关闭连接。下面是一些网上博主的观点:

创建gorm.DB对象的时候连接并没有被创建,在具体使用的时候才会创建。gorm内部,准确的说是database/sql内部会维护一个连接池,可以通过参数设置最大空闲连接数,连接最大空闲时间等。使用者不需要管连接的创建和关闭。

以上就对gorm的连接做了一个大致的分析,由于数据库底层驱动,引擎这些底层原理还没有怎么了解过,所以上面的解释也不是很深入。

mysql.Config的各个字段

MySQL 驱动程序提供了一些配置可以在初始化过程中使用

type Config struct {
    DriverName                    string
    ServerVersion                 string
    DSN                           string
    DSNConfig                     *mysql.Config
    Conn                          gorm.ConnPool
    SkipInitializeWithVersion     bool
    DefaultStringSize             uint
    DefaultDatetimePrecision      *int
    DisableWithReturning          bool
    DisableDatetimePrecision      bool
    DontSupportRenameIndex        bool
    DontSupportRenameColumn       bool
    DontSupportForShareClause     bool
    DontSupportNullAsDefaultValue bool
    DontSupportRenameColumnUnique bool
}

DSN(数据源名称)只是连接数据库的其中一个参数,通过这一个参数就能连接数据库。像其他参数,比如

DriverName:可以自定义驱动名;

Conn:可以通过一个现有的数据库连接来初始化 *gorm.DB;

SkipInitializeWithVersion:是否根据当前 MySQL 版本自动配置

DefaultStringSize:为字符串(string)字段设置大小。默认情况下,对于没有大小、没有主键、没有定义索引且没有默认值的字段,将使用db类型“longext”

DisableDatetimePrecision:禁用日期时间精度支持。但是这在MySQL 5.6之前不支持

DefaultDatetimePrecision:默认日期时间精度。比如:DefaultDatetimePrecision:&datetimePrecision

DontSupportRenameIndex:重命名索引时删除并创建索引。但是在MySQL 5.7、MariaDB之前不支持重命名索引

DontSupportRenameColumn:重命名列时使用change。但是在MySQL 8、MariaDB之前不支持重命名

其他的字段用的非常少,当有需要的时候在去研究。