Go的三个库 | 青训营笔记

66 阅读6分钟

前言

三个库的简介

database/sql

database/sql官方示例 它是Go语言标准库中的一个包,提供了一组通用的接口和功能,用于与各种关系型数据库进行交互。(标准库的意思就是语言自带,负责开发go的工程师开发了database/sql)

使用database/sql,开发人员可以编写通用的数据库操作代码,而不需要针对每种数据库实现特定的代码。

它提供了连接数据库执行查询和事务处理等功能。

但是,database/sql只提供了较低级别的接口,需要开发人员编写更多的代码来处理查询结果、映射数据库和执行常见的数据库操作。

缺点:示例较少,新手不友好

GORM

GORM官方文档 它是Go语言中一个流行的对象关系映射(ORM)库,建立在database/sql之上,为开发人员提供了更高级别的接口和功能。

ORM库的目标是简化数据库操作,通过将数据库表映射到Go语言的结构体,开发人员可以使用面向对象的方式进行数据库操作,而无需编写大量的SQL语句。

GORM提供了丰富的功能,包括模型定义、查询构建、关联关系管理、事务支持和数据库迁移等。它还支持多种数据库后端,如MySQL、PostgreSQL、SQLite等。

sqlc + migrate

sqlc + migrate sqlc是一个用于生成类型安全的Go代码的工具,用于与数据库进行交互。它通过解析 SQL 文件并生成相应的 Go 代码来提供类型安全的数据库访问层。

使用 sqlc,可以定义 SQL 查询和数据库表结构,并使用它们生成类型安全的 Go 代码。生成的代码包括查询方法、结构体定义和数据库表映射,使得可以在编译时捕获错误,并以类型安全的方式访问数据库。

三个库的优缺点

问chatGPT后得到的总结,进行简化

database/sql

优点:

  1. 官方包,得到GO社区的支持和维护
  2. 轻量级,不会引入额外的依赖
  3. 良好的跨数据库兼容性(支持的数据库多)

缺点:

  1. 缺少高级功能,如查询构建器,关联,迁移等
  2. 手动管理SQL语句,易出错且难以维护

GORM

优点:

  1. 提供了丰富的功能,如查询构建器、关联、迁移等
  2. 支持自动迁移数据库
  3. 提供了更高级的查询接口,减少了手动编写 SQL 语句的需求
  4. 良好的文档和社区支持

缺点:

  1. 依赖更多的外部库,可能导致项目不稳定
  2. 抽象层可能导致性能损失
  3. 可能需要更多的学习成本

sqlc

优点

  1. 将 SQL 查询转换为类型安全的 Go 代码,提高代码的可读性和安全性
  2. 通过生成代码,可以减少手动编写 SQL 语句的错误
  3. 支持 PostgreSQL 和 MySQL 数据库。
  4. 自动生成代码,易于维护。

缺点

  1. 没有支持所有类型的数据库。
  2. 可能需要对 SQL 语句进行调整以生成正确的 Go 代码。
  3. 自动生成的代码可能难以理解或调试。
  4. 功能相对有限,缺少一些高级功能,如关联、迁移等。

后端小白基于任务学习三个库

在日常web开发中,用到数据库大概需要完成什么需求?

  • 创建数据库
  • 数据迁移
  • 基本增删改查
  • 复杂查询:过滤条件,join
  • 事务处理:item_count
  • 错误处理
  • 性能测试

标准库的学习

使用标准库创建表和数据迁移

package database
import (
	"database/sql"
	"fmt"
	"log"
	_ "github.com/lib/pq"
)
const (
	host = "pg-for-go-mangosteen"
	port = 5432
	user = "mangosteen"
	password = "123456"
	dbname = "mangosteen_dev"
)
func PgConnect() {
	connStr := fmt.Sprintf(
		"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		host, port, user, password, dbname,
	)
	db, err := sql.Open("postgres", connStr)
	if err != nil {
		log.Fatalln(err)
	}
	DB = db         //放到全局变量里面。同一个包可以用的
	err = db.Ping() //测试数据库是否可以连接
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Successfully connect to db")
}

func PgCreateTables() {
	// 创建 users 表
	_, err := DB.Exec(`CREATE TABLE IF NOT EXISTS users (
		id SERIAL PRIMARY KEY,
		email VARCHAR(100) NOT NULL,
		created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
		updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
	)`)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Successfully create users table")
}

func Migreate(){
	_,err := DB.Exec(`ALTER TABLE users ADD COLUMN phone VARCHAR(50)`)
	if err != nil{
		log.Fatalln(err)
	}
	log.Println("Successfully add phone column to users table")
	
	_,err = DB.Exec(`ALTER TABLE users ADD COLUMN address VARCHAR(200)`)
	if err != nil{
		log.Fatalln(err)
	}
	log.Println("Successfully add address column to users table")

	//新增items表 SERIAL是自增的意思 因为PRIMARY KEY不会自增
	_, err = DB.Exec(`CREATE TABLE IF NOT EXISTS items (
		id SERIAL PRIMARY KEY,  
		amount INT NOT NULL,
		happened_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
		created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
		updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
	)`)
	if err != nil{
		log.Fatalln(err)
	}
	log.Println("Successfully add items table")
}

func PgClose() {
	DB.Close()
	log.Println("Successfully close db")
}

使用标准库进行增删改查

func Crud() {
	//创建一个user 需要返回结果 所以最好用Query 不需要返回结果的用Exec
	//增加一个用户
	result, err := DB.Query(`INSERT INTO users (email) values ('1@qq.com')`)
	if err != nil {
		log.Fatalln(err)
	}
	if result.Next() {
		var email string
		result.Scan(&email)
		log.Println("email:", email)
	}
	log.Println("Successfully create a user")

	//改一个用户
	_, err = DB.Exec(`UPDATE users SET phone = 18382088888 where email = '1@qq.com'`)
	if err != nil {
		log.Println(err)
	} else {
		log.Println("Successfully update a user")
	}
	//查一个用户
	_, err = DB.Query(`SELECT phone FROM users WHERE email = '1@qq.com' offset 0 limit 3`)
    if err != nil {
            log.Println(err)
    } else {
            if result.Next() {
                    var phone string
                    result.Scan(&phone)
                    log.Println("phone:", phone)
            }
            log.Println("Successfully find a user")
    }

}

GORM的学习

这一段是官方示例,先连接到PostgreSQL的数据库,再引入gorm

import ( 
        "gorm.io/driver/postgres" 
        "gorm.io/gorm" )  
dsn := "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai" 
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})

使用GORM创建表

func Connect() {
	dsn := fmt.Sprintf(
		"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		host, port, user, password, dbname,
	)
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalln(err)
	}
	DB = db //放到全局变量里面。同一个包可以用的
	if err != nil {
		log.Fatalln(err)
	} else {
		log.Println("Successfully connect to db")
	}
}
type User struct {
	ID        int
	Email     string
	CreatedAt time.Time
	UpdatedAt time.Time
}
func CreateTables() {
	u1 := User{Email: "fy@qq.com"}
	// 创建 users 表
	err := DB.Migrator().CreateTable(&u1)
	if err != nil {
		log.Fatalln(err)
	} else {
		log.Println("Successfully create users table")
	}
}

创建多个表

type User struct {
	ID        int
	Email     string
	Phone     string
	CreatedAt time.Time
	UpdatedAt time.Time
}
type item struct{
	ID int
	UserID int
	Amount int
	HappenedAt time.Time
	CreatedAt time.Time
	UpdatedAt time.Time
}
type Tag struct{
	ID int
}
var models = []any{&User{}, &item{}, &Tag{}}
func CreateTables() {
	for _, model := range models {
		err := DB.Migrator().CreateTable(model)
		if err != nil {
			log.Fatalln(err)
		} 
	}
	log.Println("Successfully create users table")
}
func Migreate() {
	DB.AutoMigrate(models...)
}

使用GORM迁移数据库

如何给user加一个字段,简单来讲在gorm中使用AutoMigrate就行,但是要删除一个字段要曲线救国

例如给user添加一个phone字段

type User struct {
    ID        int
    Email     string 'gorm:"uniqueIndex"'
    Phone     string
    CreatedAt time.Time
    UpdatedAt time.Time
}

func CreateTables() {
	// 创建 users 表 ,有字段 但是都为零值
    err := DB.Migrator().CreateTable(&User{})
    if err != nil {
            log.Fatalln(err)
    } else {
            log.Println("Successfully create users table")
    }
}

func Migreate() {
    DB.AutoMigrate(&User{})
}

使用GORM进行增删改查

增删改查有两种类型,一种是类型安全的,另一种是类型不安全的

sqlc的学习