gorm的引入肯定是为了我们更好的操作数据库

在使用原生sql查询的时候

最大的问题就是没办法映射到结构体上

我们不难发现,数据库里面的记录就很想一个结构体,数据库的一列对应一个字段

所以数据库到结构体的映射就是orm的一个最大特点

所有的orm都是如此

使用一个类或者一个结构体表示一张表

然后使用一个实例化对象表示一条记录

字段表示列

安装

go
go get -u gorm.io/gorm
go get gorm.io/driver/mysql

一个简单的示例

go
package main
import (
//"database/sql"
//"fmt"
//"log"
"fmt"
//引入驱动
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 用结构体来表示一个表
type User struct {
ID int
Username string
Age int
}
// 在结构体中指定表名
func (User) TableName() string {
return "user"
}
func main() {
//连接数据库
//数据源名称(用户名:密码@tcp(地址)/库名)
dsn := "root:888888@tcp(127.0.0.1:3306)/mydb"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
return
}
//fmt.Println(db)
//查询
//实例化切片
var userList []User
db.Find(&userList)
fmt.Println(userList)
}
bash
[{1 wangwu 19} {2 lisi 20} {3 zhangsan 18} {4 fengfeng 23} {5 fangfang 18} {6 fengfengzhidao 25} {7 feifei12138 28} {8 lier 29}]

单表模型

用一个 Go 结构体(struct)来对应数据库中的一张表。结构体的字段对应表的列

表结构

在gorm中,使用结构体表示一张表

例如User表

通常情况下,我们会使用Model结尾表示它是表的结构体

go
type UserModel struct {
ID int64 `gorm:"primaryKey"` // 主键
Name string `gorm:"not null;unique"` // 不能为空,且唯一
CreatedAt time.Time // 在创建记录时自动设置为当前时间
}

标签参考这个 https://gorm.io/zh_CN/docs/models.html#字段标签

为了方便可以建一个全局变量

sql
package global
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func Connect() {
dsn := "root:888888@tcp(127.0.0.1:3306)/mydb"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
return
}
DB=db
}

自动生成表

让 GORM 根据结构体的定义,自动在数据库里创建或更新表结构

生成表结构只会新增字段,不会删除字段和更新字段

go
package main
import (
"fmt"
"gorm_study/global"
"time"
)
type UserModel struct {
ID int //一般不写就是默认主键
Usernamme string `gorm:"not null;unique"`
Age int `gorm:"default:18"`
CreatedAt time.Time
}
func migrate() {
fmt.Println(global.DB)
//表迁移
err := global.DB.AutoMigrate(&UserModel{})
if err != nil {
fmt.Println("表结构迁移失败:", err)
return
}
fmt.Println("表结构迁移成功")
}
func main() {
global.Connect()
migrate()
}

单表操作

插入数据

为了方便可以单独创建 models 下一个 user_model

go
package models
import "time"
type UserModel struct {
ID int //一般不写就是默认主键
Usernamme string `gorm:"not null;unique"`
Age int `gorm:"default:18"`
CreatedAt time.Time
}
go
package main
import (
"fmt"
"gorm_study/global"
"gorm_study/models"
)
func main() {
global.Connect()
//插入数据(一次插入)
err := global.DB.Create(&models.UserModel{
Usernamme: "张三",
Age: 25,
}).Error
if err != nil {
fmt.Println(err)
}
}

回填式创建

go
//回填式插入
user := models.UserModel{
Usernamme: "lisi",
Age: 25,
}
err := global.DB.Create(&user).Error
if err != nil {
fmt.Println(err)
}
fmt.Println(user.ID, user.CreatedAt, user.Usernamme)

添加多个

go
var userList = []models.UserModel{
{Usernamme: "李四"},
{Usernamme: "wangwu"},
}
err := global.DB.Create(&userList).Error
if err != nil {
fmt.Println(err)
}

特别注意:Create函数接收的参数是结构体的指针或者是切片

插入数据的钩子

go
package models
import (
"errors"
"fmt"
"time"
"gorm.io/gorm"
)
type UserModel struct {
ID int //一般不写就是默认主键
Usernamme string `gorm:"not null;unique"`
Age int `gorm:"default:18"`
CreatedAt time.Time
}
// 钩子函数
func (u *UserModel) BeforeCreate(*gorm.DB) error {
fmt.Println("创建钩子")
return errors.New("xxxx")
}

查询数据

go
func query() {
var userList []models.UserModel
//查全部
global.DB.Find(&userList)
fmt.Println(userList)
}

注意此时会报错,连接数据库需要加参数

go
dsn := "root:888888@tcp(127.0.0.1:3306)/mydb?parseTime=true&loc=Local"
go
// 查全部
var users []UserModel
DB.Find(&users)
fmt.Println(users)
// 带条件查询
users = []UserModel{}
DB.Find(&users, "name = ?", "张三")
fmt.Println(users)
// 获取一条记录
var user UserModel
DB.Take(&user)
fmt.Println(user)
// 如果查不到则会报错
user = UserModel{}
err := DB.Take(&user, "id = ?", 100).Error
if err == gorm.ErrRecordNotFound {
fmt.Println("不存在的记录")
}
// 如果不想出现错误
user = UserModel{}
err = DB.Limit(1).Find(&user, "id = ?", 100).Error
fmt.Println(err)
// 打印实际的sql
DB.Debug().Take(&user, "id = ?", 1)

更新

有很多方法,Save、Update、UpdateColumn、Updates

不同的方法有不同的区别,如下:

  1. Save,有主键记录就是更新,并且可以更新零值,否则就是创建
  2. Update,可以更新零值,必须要有条件
  3. UpdateColumn,可以更新零值,不会走更新的Hook
  4. Updates,如果是结构体,则更新非零值,map可以更新零值

save

go
func updateSave() {
var user models.UserModel
//user.ID = 3
user.CreatedAt = time.Now()
user.Usernamme = "miaomiao2"
global.DB.Save(&user)
fmt.Println(user)
}

update

go
func update() {
var user = models.UserModel{ID: 1}
global.DB.Model(&user).Update("usernamme", "zhangsan")
fmt.Println(user)
}

更新的钩子

go
func (u *UserModel) BeforeUpdate(tx *gorm.DB) (err error) {
fmt.Println("更新的钩子函数")
return nil
}

Updates

go
func updates() {
var user = models.UserModel{ID: 1}
//global.DB.Model(&user).Updates(models.UserModel{
// Usernamme: "张三",
// Age: 18,
//})
global.DB.Model(&user).Updates(map[string]any{
"Usernamme": "张三",
"Age": 0,
})
fmt.Println(user)
}

Expr

通常用于获取原字段的数据

例如年龄加一

go
global.DB.Model(&user).Update("age", gorm.Expr("age+?", 1))

删除

go
var user = UserModel{ID: 10}
DB.Delete(&user)
DB.Delete(&UserModel{}, 9)
// 批量删除
DB.Delete(&UserModel{}, []int{1, 2, 3})
go
func (u *UserModel) BeforeDelete(tx *gorm.DB) (err error) {
fmt.Println("删除钩子")
return
}

软删除

如果你的模型包含了 gorm.DeletedAt字段(该字段也被包含在gorm.Model中),那么该模型将会自动获得软删除的能力

当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录。

go
type UserModel struct {
ID int //一般不写就是默认主键
Usernamme string `gorm:"not null;unique"`
Age int `gorm:"default:18"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}

新增加了列要迁移

go
func Migrate() {
err := DB.AutoMigrate(&models.UserModel{})
if err != nil {
log.Fatalf("数据库迁移失败: %s", err)
}
fmt.Println("数据库迁移成功")
}

查找被删除的记录

go
var user models.UserModel
global.DB.Unscoped().Take(&user, 3)
fmt.Println(user)

永久删除

go
global.DB.Unscoped().Delete(&user)