Database/sql及GORM相关解读(1)day04 | 青训营笔记

386 阅读6分钟

一、Database/sql

基本用法

  1. 导入数据库驱动
  2. 使用driver + DSN(Data Source Name)初始化DB连接
  3. 执行SQL语句,返回获得的数据
  4. 处理完毕,释放连接
  5. 数据处理或错误处理等
//以mysql为例创建一个简单的连接
package main  
  
import (  
    "database/sql"  
    "fmt"  
    "log"  
  
    // 使用_别名来匿名导入驱动,使用sql.DB提供的方法,驱动的导出名字不会出现在当前作用域中。  
    // 导入时,驱动的初始化函数会调用sql.Register将自己注册  
    // 在database/sql包的全局变量sql.drivers中,以便以后通过sql.Open访问。  
    _ "github.com/go-sql-driver/mysql"  
)  
  
type User struct {  
    Id int `db:"id"`  
    Name string `db:"name"` 
}  
  
func main() {  
    // 创建数据库连接  
    //sql.Open()并未实际建立起到数据库的连接,也不会验证驱动参数。第一个实际的连接会惰性求值,延迟到第一次需要时建立。  
    db, _ := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/GolangTest")  
    if err := db.Ping(); err != nil {  
        log.Fatal(err)  
        return  
    }  
    //获取数据  
    rows, err := db.Query("select id, name from user where id = ?", 1)  
    if err != nil {  
        // do something to handle errors  
    }  

    defer rows.Close()  
    var users []User  

    // 遍历数据并暂存  
    for rows.Next() {  
        var user User  
        err := rows.Scan(&user.Id, &user.Name)  
        if err != nil {  
            // do something to handle errors  
        }  
        users = append(users, user)  
        }  
        if rows.Err() != nil {  
            // ...  
        }  
    fmt.Println(users)  
 }

user表数据: image.png

// output
[{1 Jack}]

设计原理

database/sql采用极简设计原则,为上层应用提供操作接口,对下层暴露简单的驱动接口(连接接口操作接口),在database/sql包中实现连接池等管理,其中真正与数据库交互的是各个数据库对应的驱动实现,在使用时需要先注册对应驱动,然后就可以使用database/sql中定义的接口统一操作数据库。

image.png

二、GORM基础用法

移步GORM官方文档

三、GORM设计原理

GORM相当于在database/sql包中增加了一层 image.png

SQL生成

GORM的SQL生成是由其内部的gorm.DB对象负责的。gorm.DB对象是GORM的核心对象,它提供了大量的API来操作数据库。在GORM中,我们可以使用链式调用来构建SQL语句,例如:

db.Where("age > ?", 18).Order("name desc").Find(&users)

这个链式调用会返回一个新的gorm.DB对象,它包含了查询条件和排序规则的信息。接下来,当调用Find方法时,gorm.DB对象会将这些信息转换为SQL语句并发送给数据库服务器。具体来说,gorm.DB对象的SQL生成过程可以分为两个阶段:

  1. AST构建阶段

在链式调用期间,gorm.DB对象会根据用户提供的条件和方法构建一个抽象语法树(AST)。AST是一个树形结构,它将查询条件和排序规则表示为一系列节点。在GORM中,AST是用来组装SQL语句的核心组件。

例如,在下面的链式调用中:

db.Where("age > ?", 18).Order("name desc").Find(&users)

gorm.DB对象会将WhereOrder方法解析为两个AST节点,然后将它们连接起来,形成一个完整的AST,如下所示:

SELECT
  *
FROM
  users
WHERE
  age > 18
ORDER BY
  name DESC
  1. SQL生成阶段

在AST构建完成后,gorm.DB对象会将AST转换为SQL语句。gorm.DB会遍历AST的节点,并根据节点类型生成相应的SQL语句。例如,在上面的AST中,gorm.DB对象会将SELECTFROMWHEREORDER BY节点转换为相应的SQL语句,然后将它们连接起来,形成最终的SQL语句。

在执行SQL语句之前,gorm.DB对象还会对SQL语句进行一些处理,例如将参数转换为预编译语句中的占位符,以避免SQL注入攻击。同时,它还会对SQL语句进行一些优化,例如使用索引来加速查询等。

GORM的SQL生成过程是由gorm.DB对象负责的,它通过AST构建和SQL生成两个阶段来将链式调用转换为SQL语句,并最终将SQL语句发送给数据库服务器。这个过程非常灵活和高效,为GORM提供了非常方便的API来操作数据库。

ConnPool

在GORM中,ConnPool是一种管理数据库连接池的机制。连接池是一种重要的数据库优化技术,它可以在应用程序和数据库服务器之间维护一组已经建立好的数据库连接,以避免频繁地创建和销毁数据库连接,从而提高数据库访问的效率。

image.png

在GORM中,每个gorm.DB对象都有一个内部的ConnPool对象,用来管理该gorm.DB对象的数据库连接池。ConnPool对象提供了一系列的API来管理连接池,例如设置最大连接数、设置最大空闲连接数、设置连接的超时时间等。在使用GORM时,我们可以通过gorm.Config中的MaxIdleConnsMaxOpenConns选项来设置连接池的大小。例如:

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    MaxIdleConns: 10,
    MaxOpenConns: 100,
})

在上面的示例中,设置了连接池的最大空闲连接数为10,最大连接数为100。这意味着,在使用gorm.DB对象访问数据库时,如果连接池中的连接数小于等于10,GORM会尝试从连接池中获取一个连接来执行操作;如果连接池中的连接数大于10且小于等于100,GORM会尝试创建一个新的连接来执行操作;如果连接池中的连接数已经达到了最大连接数,GORM会等待连接池中有空闲连接时再执行操作。

连接池的大小应该根据具体的应用程序和数据库服务器的负载情况进行调整,以避免资源浪费或者连接池溢出等问题。同时,为了避免数据库连接的泄漏,应在使用完数据库连接后及时将其释放回连接池中,以方便其他的应用程序使用。

Dialector

在GORM中,Dialector是一种抽象的数据库驱动接口,它定义了与数据库通信的标准化接口,允许我们通过实现不同的Dialector来支持不同的数据库。Dialector对象封装了数据库的连接信息,并提供了一系列的方法来生成数据库连接URL、设置连接池大小、配置日志等。

在GORM中,每个数据库都需要一个对应的Dialector来支持。GORM自带了一组内置的Dialector,包括MySQL、PostgreSQL、SQLite等。我们也可以自己实现Dialector接口来支持其他的数据库。

下面是一个使用MySQL数据库的示例:

import (
    "gorm.io/gorm"
    "gorm.io/driver/mysql"
)

dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    // handle error
}

var users []User
db.Where("age > ?", 18).Find(&users)

在上面的示例中,使用了gorm.io/driver/mysql包中的mysql驱动来实现MySQL的Dialector。通过调用mysql.Open方法和一个DSN字符串来创建一个新的Dialector对象,并将其传递给gorm.Open方法来创建一个新的gorm.DB对象。

在使用Dialector时,应确保使用的Dialector版本与GORM的版本兼容。如果使用的Dialector版本过低或者过高,可能会导致GORM无法正常工作。