DATABASE/SQL 以及 GORM相关 | 青训营笔记

268 阅读3分钟

DATABASE/SQL 与 GORM 设计与实践

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

概述

  • Go语言中 Database/SQL 的实现
  • GORM 的实现原理、以及如何使用,甚至是如何基于 GORM 做一些定制化开发

基础知识支撑

具体细节

1. 理解 Database/SQL

1.1 Database/SQL 的基本用法 --- Quick Start

import (
      "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

func main(){//最原始写法
    db, err := sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello")
    
    rows, err := db.Query("select id,name from users where id = ?",1)
    if err != nil{
        //xxx
    }
    defer func(){
        err = rows.Close()
    }
    
    var users []User
    for rows.Next(){
        var user User
        err := rows.Scan(&user.ID, &user.Name)
        
        if err != nil{
            //xxx
        }
        
        users = append(users,user)
    }
    
    if rows.Err() != nil {
        //xxx
    }
}
  • 引入 database/sql 包

image-20220516162816906.png

  • database/sql 包只是提供了连接数据库的接口,具体怎么根据不同的数据库进行实现,需要数据库自己去设计不同的 driver,这里是引入了 mysql 的driver,然后通过 driver + DSN 初始化 DB 连接

image-20220516163001681.png

  • 执行一条 SQL ,通过rows 取回返回的数据,处理完毕需要释放链接

image-20220516163219907.png

  • 数据、错误处理,将获取到的数据封装到对应的实体对象中

image-20220516163412695.png

  • 错误处理!!!
  • 【注意】defer 关闭连接时也要处理,否则会丢失错误信息

image-20220516165716948.png

image-20220516165724561.png

1.2 设计原理

image-20220516165813962.png

  • 向上提供操作接口向下提供统一的连接接口与操作接口

  • 池化技术,将获取复杂费事的资源池化,同时配套管理工具进行资源管理

(1)数据库连接池基础概念

image-20220516170245075.png

image-20220516170348037.png

(2)database/sql包执行SQL的伪代码实现

image-20220516170524392.png

  • 尝试获取连接并执行,总共3次
  • 两种获取连接的策略:有空闲连接就拿来用(尽量复用),没有(前面的链接都出错了)就新建连接
  • 使用完成后将连接放回连接池
  • 当没有报错的时候,跳出循环,执行成功。
(3)【连接接口】database/sql 包如何预留接口让不同的 driver进行实现
  • driver 接口方法 Open传入DSN 返回一个连接,基于连接进行操作
  • 函数 Register 注册全局 driver,把driver注册到全局变量 map

image-20220516172525864.png

  • driver 的使用:业务代码中 import driver,通过 main 方法建立连接通过连接进行查询

image-20220516172822143.png

  • 改良版:避免了过长的 DSN 以及重名问题 (推荐)

image-20220516173509790.png

image-20220516173534413.png

(4)【操作接口】
  • DB 连接的几种类型

    • 直接连接/ Conn
    • 预编译/Stmt
    • 事务/Tx
  • 处理返回数据的几种方式

    • Exec / ExecContext -> Result(是否成功的信息)
    • Query / QueryContext -> Rows (Columns)(以行形式返回查询到的信息)
    • QueryRow / QueryRowContext -> Row (Rows 简化)
  • 具体使用代码:

image-20220516174224104.png

image-20220516174236716.png

2. GORM基础使用

2.1 GORM基本用法

  • 从数据库中取出user的数据,封装到User实体类中
import (
	"gorm.io/gorm"
	"gorm.io/driver/mysql"
)

func main(){
	db, err := gorm.Open(
		mysql.Open("user:password@tcp(127.0.0.1:3306)/hello")
	)
	
	var users []User
	err = db.Select("id","name").Find(&users,1).Error
}
import (
	"database/sql"
    
    _ "github.com/go-sql-driver/mysql"
)

func main(){
    db, err := sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello")
    
    rows, err := db.Query("select id,name from users where id = ?",1)
    if err != nil{
        //xxx
    }
    
    defer func(){
        err = rows.Close()
    }
    
    var users []User
    for rows.Next(){
        var user User
        err := rows.Scan(&user.ID, &user.Name)
        
        if err != nil{
            //xxx
        }
        
        users = append(users,user)
    }
    
    if rows.Err() != nil {
        //xxx
    }
}
  • CRUD

image-20220516175236696.png

image-20220516175422602.png

2.2 Model 定义(实体类定义)

image-20220516175457040.png

image-20220516175515474.png

2.3 惯例约定(Model属性与表中字段如何对应)

image-20220516175616673.png

2.4 关联操作

(1)GORM 支持很多关联形式

image-20220516175755381.png

(2)CRUD

image-20220516175855506.png

(3)Preload / Joins 预加载
  • Preload:多级预加载,多条语句
  • Joins:一条语句

image-20220516180019078.png

(4)级联删除

image-20220516180141279.png

3. GORM 的设计原理

Gorm 其实相当于在 database/sql 上又加了一层,用于优化用户与数据库之间的互动(类似 Java 中,Mybatis 对于 数据库和 JDBC 的连接)

3.1 SQL 生成的机制

image-20220516180644819.png

image-20220516180700900.png

3.2 插件扩展机制

image-20220516181213176.png

(1)creat 的 callbacks

image-20220516182655572.png

image-20220516182752712.png

(2)插件使用示例

image-20220516182900524.png

  • 多租户系统

    • 创建了一个新的 callback ,从statement里取出用户id,每次进行操作的时候就主动加上,以区分用户
  • 所数据库、读写分离

image-20220516184605839.png

3.3 ConnPool 扩展机制

image-20220516184625680.png

image-20220516184645314.png

  • 像mybatis的多级缓存

image-20220516185003668.png

3.4 Dialector 扩展机制