跳到主要内容

3.2-gorm 的使用

Create by fall on 24 Sep 2025
Recently revised in 06 Nov 2025

gorm 数据库操作

使用 gorm 批量插入数据

连接数据库

连接数据库并对 ID 行查询

package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
func getUsername(userId int)(string, error){
// 连接数据库
db, err := sql.Open("postgres", "postgresql://username:password@localhost/mydb?sslmode=disable")
if err != nil {
return "", fmt.Errorf("failed to connect database: %w", err)
}
defer db.Close()
// 进行查询 - 之后的示例只对该模块进行替换
var username string
err = db.QueryRow("Select username From users WHERE id = $1", userId).Scan(&username)
if err != nil {
return "", fmt.Errorf("failed to get username: %w", err)
}
// 查询结束
return username, nil
}
func main(){
getUsername(124)
}

之后的示例只对上方的查询模块进行替换

数据模型

假设你拥有如下的模型

package models
import "gorm.io/gorm"
type Word struct {
gorm.Model
Word string `json:"word"`
Translate string `json:"translate"`
Example string `json:"example"`
CreatedAt string `json:"created_at"`
}

同时创建多个数据

数据插入

分批插入多个数据

import (
"gorm.io/gorm"
)
func InsertToDb(db:gorm.DB){
words := []models.Word{}
result := db.CreateInBatches(words, 2) // 一次插入 2 条
if result.Error != nil {
panic("failed to create in batches: " + result.Error.Error())
}
// 保证批量插入的原子性
// err := db.Transaction(func(tx *gorm.DB) error {
// if err := tx.CreateInBatches(words, 100).Error; err != nil {
// return err // 返回错误会自动回滚事务
// }
}

更新

查询数据

单个数据

多个数据

链式调用

func GetDaily(){
err := s.db.Model(&models.Order{}).
Select("DATE(created_at) as date, COUNT(*) as count, SUM(amount) as total_amount").
Where("created_at BETWEEN ? AND ?", startDate, endDate).
Group("DATE(created_at)").
Order("date ASC").
Find(&results).Error
}

分组查询

多条件分页查询

func MultiSearch(db:db.Database){ 
// 构建查询
query := db.Model(&models.Order{})

// 用户ID条件
if params.UserID > 0 {
query = query.Where("user_id = ?", params.UserID)
}

// 状态条件
if params.Status != "" {
query = query.Where("status = ?", params.Status)
}
}

自定义查询

func GetYesterdayStat(db *gorm.DB) ([]HourlyStats, error){
yesterdayStart := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location())
todayStart := yesterdayStart.Add(24 * time.Hour)
query := `
SELECT
HOUR(created_at) as hour,
COUNT(*) as count,
COALESCE(SUM(value), 0) as sum,
COALESCE(AVG(value), 0) as average
FROM data_records
WHERE created_at >= ? AND created_at < ?
GROUP BY HOUR(created_at)
ORDER BY hour
`
var stats []HourlyStats
err := db.Raw(query, yesterdayStart, yesterdayEnd).Scan(&stats).Error
if err != nil {
return nil, err
}
}

数据常见关系

多对一

type User struct {
gorm.Model
Name string
CompanyRefer int
Company Company `gorm:"foreignKey:CompanyRefer"`
// 自定义外键,使用 CompanyRefer 作为外键
// 默认外键为类型加上 Company 的主键
}

type Company struct {
ID int
Name string
}

一对多

// User 有多张 CreditCard,UserID 是外键
type User struct {
gorm.Model
CreditCards []CreditCard
}

type CreditCard struct {
gorm.Model
Number string
UserID uint
}
// 检索用户列表并预加载信用卡
func GetAll(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("CreditCards").Find(&users).Error
return users, err
}

一对一

关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例。

// User 有一张 CreditCard,UserID 是外键
type User struct {
gorm.Model
CreditCard CreditCard
}

type CreditCard struct {
gorm.Model
Number string
UserID uint
}

// 检索用户列表并预加载信用卡
func GetAll(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("CreditCard").Find(&users).Error
return users, err
}

多对多

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
gorm.Model
Name string
}

当使用 GORM 的 AutoMigrateUser 创建表时,GORM 会自动创建连接表

反向引用声明

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
}

type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages;"`
}

检索

// 检索 User 列表并预加载 Language
func GetAllUsers(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("Languages").Find(&users).Error
return users, err
}

// 检索 Language 列表并预加载 User
func GetAllLanguages(db *gorm.DB) ([]Language, error) {
var languages []Language
err := db.Model(&Language{}).Preload("Users").Find(&languages).Error
return languages, err
}

附录

连接至 mysql

package main

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

type User struct { // 定义模型结构体
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100"`
Age int
}

func main() {
dsn := "user:password@tcp(localhost:3306)/your_database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database: " + err.Error())
}

// 可选:自动迁移表结构
err = db.AutoMigrate(&User{})
if err != nil {
panic("failed to auto migrate: " + err.Error())
}

// 1. 准备要批量插入的用户数据切片
users := []User{
{Name: "张三", Age: 25},
{Name: "李四", Age: 30},
{Name: "王五", Age: 28},
{Name: "赵六", Age: 35},
// ... 可以准备更多数据
}

// 2. 使用 CreateInBatches 分批插入,这里每批插入 2 条记录
result := db.CreateInBatches(users, 2) // 一次插入2条
if result.Error != nil {
panic("failed to create in batches: " + result.Error.Error())
}
// 保证批量插入的原子性
// err := db.Transaction(func(tx *gorm.DB) error {
// if err := tx.CreateInBatches(users, 100).Error; err != nil {
// return err // 返回错误会自动回滚事务
// }
return nil
})

// 3. 输出插入信息
fmt.Printf("插入了 %d 条记录。\\n", result.RowsAffected)
}