Go GORM 完整使用教程(对比原生 SQL)

一、原生 database/sql 基础回顾

1. 初始化数据库连接

go

运行

1
2
3
4
5
6
7
8
9

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

// DSN格式:用户名:密码@tcp(IP:端口)/数据库名
dsn := "root:root@tcp(127.0.0.1:3306)/dorm"
db, err := sql.Open("mysql", dsn)

2. 连通性校验

go

运行

1
2
3
4
5

err = db.Ping()
if err != nil {
panic("数据库连接失败")
}

3. DML 操作(增删改)

使用 Exec(),返回受影响行数、自增 ID

go

运行

1
2

res, err := db.Exec("INSERT INTO user(name) VALUES(?)", "张三")

4. DQL 查询操作

(1)多行查询 Query () + 循环 Scan

go

运行

1
2
3
4
5
6
7
8
9

rows, err := db.Query("SELECT id,name FROM user WHERE id > ?", 1)
defer rows.Close()

for rows.Next() {
var id int
var name string
err = rows.Scan(&id, &name)
}

(2)单行查询 QueryRow ().Scan ()

go

运行

1
2
3

var name string
err := db.QueryRow("SELECT name FROM user WHERE id=?", 1).Scan(&name)

二、GORM 初始化全局 DB 实例

1. 依赖安装

bash

运行

1
2
3

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

2. 全局 DB 变量 + 连接初始化函数

go

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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

// 全局DB,项目全局调用
var DB *gorm.DB

func ConnectMysql() {
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm_study"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("GORM连接数据库失败:" + err.Error())
}
DB = db
}

三、数据表自动迁移 AutoMigrate

无需手动建表,根据 Model 结构体自动创建 / 更新表结构

go

运行

1
2
3
4
5
6
7
8
9
10
11
12
13

import "你的项目/global"
import "你的项目/model"

func MigrateTable() {
// 无表自动创建,字段变更自动新增列,不会删除原有列
err := global.DB.AutoMigrate(&model.UserModel{})
if err != nil {
fmt.Println("迁移失败", err)
return
}
fmt.Println("数据表迁移成功")
}

四、单表 CRUD 操作

前置 Model 示例

go

运行

1
2
3
4
5
6
7
8

import "gorm.io/gorm"

type UserModel struct {
gorm.Model // 内置ID、CreatedAt、UpdatedAt、DeletedAt(软删除)
Name string
Age int
}

1. 新增 Create

go

运行

1
2
3
4
5

user := UserModel{Name: "张三", Age: 20}
// 单条插入
err := global.DB.Create(&user).Error
// user.ID 会自动回填自增主键

2. 查询操作(核心方法对比)

表格

方法 接收变量 返回数据量 默认排序 无数据返回
First 单个结构体 1 条 主键升序 LIMIT 1 报错 gorm.ErrRecordNotFound
Find 结构体切片 多条 / 全部 无默认排序 返回空切片,无错误

多条件分页、排序查询

go

运行

1
2
3
4

var userList []UserModel
// Where条件 + 倒序 + 限制条数
global.DB.Where("age = ?", 100).Order("id DESC").Limit(3).Find(&userList)

常用链式调用说明

  • Where():拼接查询条件,不执行 SQL
  • Order():排序规则
  • Limit():限制返回行数
  • Offset():偏移量,配合 Limit 实现分页

3. 更新操作 Update / Updates

更新单个字段

go

运行

1
2
3

// id=1的用户,age更新为25
err := global.DB.Model(&UserModel{}).Where("id = ?", 1).Update("age", 25).Error

更新多个字段(结构体 / Map)

go

运行

1
2
3
4
5
6
7
8
9
10
11

// 方式1:结构体
u := UserModel{Name: "李四", Age: 28}
err := global.DB.Model(&UserModel{}).Where("id = ?", 2).Updates(u).Error

// 方式2:map(推荐,零值也会更新)
updateData := map[string]any{
"name": "王五",
"age": 30,
}
global.DB.Model(&UserModel{}).Where("id=?", 3).Updates(updateData)

4. 删除操作(软删除 / 真实删除)

前提:开启软删除

结构体嵌入 gorm.Model 自带 DeletedAt,GORM 默认实现软删除(UPDATE 标记删除,不物理 DELETE)

(1)软删除

go

运行

1
2
3
4
5
6
7

// 单条删除(根据主键)
user := UserModel{ID: 2}
err := global.DB.Delete(&user).Error

// 批量条件删除
err := global.DB.Model(&UserModel{}).Where("age = ?", 10).Delete(&UserModel{}).Error
(2)物理真实删除 Unscoped ()

go

运行

1
2
3
4
5
6
7

// 单条真删
user := UserModel{ID: 3}
global.DB.Unscoped().Delete(&user)

// 批量条件真删
global.DB.Unscoped().Model(&UserModel{}).Where("age < ?", 18).Delete(&UserModel{})

五、Select / Scan / Pluck 专用查询

1. Select:指定查询列

go

运行

1
2
3
4

var user UserModel
// 只查询name、age两列
global.DB.Select("name,age").Where("id=?", 1).Find(&user)

2. Scan:自定义变量接收结果(脱离 Model 绑定)

适合统计 count、自定义字段映射

go

运行

1
2
3
4
5

var total int64
// 统计成年用户总数
err := global.DB.Model(&UserModel{}).Where("age > ?", 18).
Select("count(*)").Scan(&total).Error

3. Pluck:单独提取某一列到切片

无需完整结构体,批量拿单列数据

go

运行

1
2
3
4

var ids []uint
// 批量取出所有成年用户id
global.DB.Model(&UserModel{}).Where("age >= ?", 20).Pluck("id", &ids)

六、Scope 复用查询条件(封装通用逻辑)

把重复的 Where/Order 封装成函数,多处复用,统一维护条件

1. 定义 Scope 函数

go

运行

1
2
3
4
5

// 封装:只查询成年用户
func AdultUserScope(db *gorm.DB) *gorm.DB {
return db.Where("age >= ?", 18)
}

2. 调用 Scope

go

运行

1
2
3
4

var list []UserModel
// 复用通用条件,再叠加自定义排序
global.DB.Scopes(AdultUserScope).Order("id DESC").Find(&list)

七、GORM 执行原生 SQL

1. 查询 SQL Raw () + Scan

占位符?防 SQL 注入,禁止字符串拼接

go

运行

1
2
3
4
5
6
7
8
9

// 统计总数
var cnt int64
err := global.DB.Raw("SELECT COUNT(*) FROM user_model WHERE age >= ?", 18).Scan(&cnt).Error

// 多行查询映射结构体
var userList []UserModel
sql := "SELECT * FROM user_model WHERE age > ? ORDER BY id DESC LIMIT ?"
err := global.DB.Raw(sql, 18, 10).Scan(&userList).Error

2. 增删改 SQL Exec ()

go

运行

1
2
3
4
5
6
7

sql := "INSERT INTO user_model(name,age,created_at,updated_at) VALUES (?,?,NOW(),NOW())"
res, err := global.DB.Exec(sql, "小王", 22)

// 获取自增主键、受影响行数
lastID, _ := res.LastInsertId()
rowsAffected, _ := res.RowsAffected()

八、多表关联关系(一对一演示)

1. Model 结构示例

go

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// 用户主表
type UserModel struct {
gorm.Model
Name string
Age int
UserDetail UserDetailModel // 一对一关联
}

// 用户详情附表
type UserDetailModel struct {
gorm.Model
Phone string
UserID uint // 外键关联user.id
}

2. 预加载 Preload 关联查询

正向查询(查详情,带出所属用户)

go

运行

1
2
3

var detail UserDetailModel
global.DB.Preload("UserModel").Take(&detail, "user_id=?", 15)

反向查询(查用户,带出详情)

go

运行

1
2
3

var user UserModel
global.DB.Preload("UserDetailModel").Take(&user, 15)

3. 级联删除

可在 Model 关联标签配置级联删除,删除主表数据时自动删除关联附表数据,无需手动操作多条 DELETE