likes
comments
collection
share

GORM 基本使用

作者站长头像
站长
· 阅读数 21

mysql 数据库连接

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

const (
  mysqlHost     = "go-uccs"
  mysqlPort     = 3306
  mysqlUser     = "uccs"
  mysqlPassword = 123456
  mysqlDbname   = "uccs"
)
dsn := fmt.Sprintf("%s:%d@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=true", mysqlUser, mysqlPassword, mysqlHost, mysqlDbname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

docker 运行 mysql

先创建网络

docker create network network1

mysql 容器使用 network1 网络

docker run -d --network network1 --network-alias mysql -p 3306:3306 --name go-uccs-1 -e MYSQL_DATABASE=uccs -e MYSQL_USER=uccs -e MYSQL_PASSWORD=123456 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.15 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

配置全局 logger

newLogger := logger.New(
  log.New(os.Stdout, "\r\n", log.LstdFlags),
  logger.Config{
    SlowThreshold: time.Second,   // Slow SQL threshold
    LogLevel:      logger.Silent, // Log level
    Colorful:      false,         // Disable colors
  },
)
gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger})

log.LstdFlags:表示输出日志时要添加本地时区的日期和时间,输出的形式为:2023/07/14 21:28:20 Hello, world! SlowThreshold:设置慢 SQL 的阈值,如果 SQL 执行时间超过这个阈值,就会被记录为慢 SQL LogLevel:设置日志级别,gorm 定义了四种日志级别:SilentErrorWarnInfo。您可以根据您的需要选择合适的日志级别 Colorful:设置是否使用彩色字体来显示日志,如果设置为 false,就会禁用彩色字体

建表

type Product struct {
  gorm.Model
  Code  string
  Price uint
}
db.AutoMigrate(&Product{})

gorm.Model 是一个结构体,它是 gorm 包中定义的一个通用模型,包含了 IDCreatedAtUpdatedAtDeletedAt 四个字段

增删改查

新建一条数据

db.Create(&Product{Code: "D42", Price: 100})

对应的 SQL 语句

INSERT INTO `products` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ('2023-07-15 03:39:37.951','2023-07-15 03:39:37.951',NULL,'D42',100)

查询

查找一条数据

使用 First 查找数据,默认查找的不包含软删除的数据

var product Product
db.First(&product, 1) // 查找主键为 1 的数据

对应的 SQL 语句

SELECT * FROM `products` WHERE `products`.`id` = 1 AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` LIMIT 1

条件搜索

db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的第一条数据

对应的 SQL 语句

SELECT * FROM `products` WHERE code = 'D42' AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` LIMIT 1

错误

如果找不到数据会返回 ErrRecordNotFound 错误

ok := errors.Is(result.Error, gorm.ErrRecordNotFound)

查询所有数据

var products []Product

db.Find(&products)
for _, product := range products {
  fmt.Println(product)
}

对应的 SQL 语句

SELECT * FROM `products` WHERE `products`.`deleted_at` IS NULL

更新

db.Model(&product).Where("code = ?", "D42").Update("Price", 300)

对应的 SQL 语句

UPDATE `products` SET `price`=300,`updated_at`='2023-07-15 03:47:29.659' WHERE code = 'D42' AND `products`.`deleted_at` IS NULL

更新多个值

db.Model(&product).Where("code = ?", "D42").Updates(Product{Price: 200, Code: "F42"})

对应的 SQL 语句

UPDATE `products` SET `updated_at`='2023-07-15 03:50:35.218',`code`='F42',`price`=200 WHERE code = 'D42' AND `products`.`deleted_at` IS NULL
db.Model(&product).Where("code = ?", "F42").Updates(map[string]interface{}{"Price": 300, "Code": "D42"})

对应的 SQL 语句

UPDATE `products` SET `code`='D42',`price`=300,`updated_at`='2023-07-15 03:53:26.329' WHERE code = 'F42' AND `products`.`deleted_at` IS NULL

save 方法

save 方法会保存没有赋值的字段,默认为零值

type User {
  Name string `gorm:"column:user_name"`
  Email string
}
user := User{Name: "uccs1"}
db.Save(&user)

对应的 SQL 语句

INSERT INTO `users` (`user_name`,`email`) VALUES ('uccs1','')

Select 和 Omit

var user User
db.Model(&user).Where("user_id = ?", 1).Select("Email").Updates(User{Name: "uccs2", Email: "333"})
db.Model(&user).Where("user_id = ?", 1).Omit("Email").Updates(User{Name: "uccs333", Email: "333"})

对应的 SQL 语句

UPDATE `users` SET `email`='333' WHERE user_id = 1
UPDATE `users` SET `user_name`='uccs333' WHERE user_id = 1

删除

db.Delete(&product, 1)

对应的 SQL 语句

UPDATE `products` SET `deleted_at`='2023-07-15 03:57:07.889' WHERE `products`.`id` = 1 AND `products`.`deleted_at` IS NULL

如果一个 Modelgorm.DeletedAt 类型,会自动获得软删除的功能,在 deleted_at 字段中写入删除时间,当查询数据时,会自动忽略掉 deleted_at 字段不为空的数据

永久删除可以使用 Unscoped 方法

仅更新非零值

使用 Updates(Product{Price: 0}) 方式更新,只能更新非零值

因为在 go 中,如果一个字段没有值,它会有个默认值,这个默认值一般就是零值

这时,就想更新一个字段为零值可以用 sql.NullString 类型,int 类型有 NullInt16

type Product {
  Code sql.NullString
}
db.Model(&product).Where("code = ?", "D42").Updates(&Product{Code: sql.NullString{"", true}})

另一种是使用指针的形式

type Product {
  Code *string
}
empty := ""
db.Model(&product).Where("code = ?", "D42").Updates(&Product{Code: &empty})

对应 SQL 语句

UPDATE `products` SET `updated_at`='2023-07-15 09:13:38.229',`code`='' WHERE code = 'D42' AND `products`.`deleted_at` IS NULL

表结构定义的细节

使用 tag 就可以自定义表结构,具体可以看文档:Declaring Models

type User struct {
  UserId int    `gorm:"primaryKey"`
  Name   string `gorm:"column:user_name;type:varchar(100);not null;index:idx_user_name;unique;default:'uccs'"`
}

创建记录

// 没有 ID
user := User{
  Name: "uccs2",
}
result := db.Create(&user)

fmt.Println(result.Error)
fmt.Println(result.RowsAffected)
// 执行完 db.Create 后,会有一个 ID
fmt.Println(user.ID)

批量创建

user := []User{{Name: "uccs21"}, {Name: "uccs31"}, {Name: "uccs41"}}
db.Create(&user)

对应的 SQL 语句

INSERT INTO `users` (`user_name`) VALUES ('uccs21'),('uccs31'),('uccs41')

使用 db.CreateInBatches 批量创建,每次创建 2

user := []User{{Name: "uccs21"}, {Name: "uccs31"}, {Name: "uccs41"}}
db.CreateInBatches(user, 2)

对应的 SQL 语句

INSERT INTO `users` (`user_name`) VALUES ('uccs21'),('uccs31')
INSERT INTO `users` (`user_name`) VALUES ('uccs41')

条件查询

查询条件有三种形式:stringstructmap

// string
db.Where("name = ?", "uccs").First(&user) //  name 是数据库 column
// struct
db.Where(&User{MyName: "uccs"}).First(&user) // MyName 是 User 结构体的字段
// map
db.Where(map[string]interface{}{"name": "uccs"}).Find(&user)

要注意的是:使用 struct 形式作为条件,会屏蔽掉零值字段

往期文章

  1. go 项目ORM、测试、api文档搭建
  2. go 开发短网址服务笔记
  3. go 实现统一加载资源的入口
  4. go 语言编写简单的分布式系统
  5. go 中 rpc 和 grpc 的使用
  6. protocol 和 grpc 的基本使用
  7. go 基础知识
  8. grpc 的单向流和双向流
  9. gin 基本使用