golang gorm 增删改查以及使用原生SQL(以操作mysql为例)

本文详细介绍了如何在Go语言中使用GORMORM库与MySQL进行模型定义、数据库连接设置、数据增删改查操作,包括正向选择、反向选择、批量插入、连接池配置以及原生SQL查询示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gorm + mysql增删改查

model定义
package _case

import "gorm.io/gorm"

func init() {
    DB.Migrator().AutoMigrate(Teacher{}, Course{})
}
type Roles []string

type Teacher struct {
    gorm.Model
    Name     string   `gorm:"size:256"`
    Email    string   `gorm:"size:256"`
    Salary   float64  `gorm:"scale:2;precision:7"`   // 指定小数部分宽度为2,列宽度为7. 列宽:【整数部分+小数部分的总长度】【不含小数点】
    Age      uint8    `gorm:"check:age>30"`
    Birthday int64    `gorm:"serializer:unixtime;type:time"`  // 反序列化方式 unixtime, 类型为time
    Roles    Roles    `gorm:"serializer:json"`
    JobInfo  Job      `gorm:"embedded;embeddedPrefix:job_"`   // 嵌套字段, 嵌入字段的列名前缀job_
    JobInfo2 Job      `gorm:"type:bytes;serializer:gob"`      // 字节流类型,gob反序列化,go自己的序列化方法,跨语言项目的时候,不建议用
}

type Job struct {
    Title    string
    Location string
}

type Course struct {
    gorm.Model
    Name   string  `gorm:"size:256"`
    Price  float64 `gorm:"scale:2;precision:7"`
    UserID uint    `gorm:"type:int"`
}
数据库连接并设置连接池
package _case

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "log"
    "time"
)

var DB *gorm.DB

var dsn = "root:123456@tcp(10.74.18.61:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"

func init() {
    var err error
    // 每次调用返回的都是DB对象,这是支持链式调用的
    DB, err = gorm.Open(mysql.New(mysql.Config{
        DSN: dsn,
        DefaultStringSize: 256,
    }), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
        //开启预编译,提高后续调用速度
        //开启预编译的情况下,不支持嵌套事务
        PrepareStmt: true,
    })
    if err != nil {
        log.Println(err)
        return
    }
    setPool(DB)
}


// 连接池设置
func setPool(db *gorm.DB) {
    sqlDB, err := db.DB()
    if err != nil {
        log.Println(err)
        return
    }
    // 连接存活最大时长
    sqlDB.SetConnMaxLifetime(time.Hour)
    // 最大空闲连接数
    sqlDB.SetMaxIdleConns(5)
    // 最大连接打开数
    sqlDB.SetMaxOpenConns(10)

}

假数据定义

var teacherTemp = Teacher{
    Name: "kk",
    Age: 40,
    Salary: 1234.22,
    Email: "kk123@qq.com",
    Birthday: time.Now().Unix(),
    Roles: Roles{"普通用户", "讲师"},
    JobInfo: Job{
        Title: "教授",
        Location: "九龙湖",
    },
    JobInfo2: Job{
        Title: "教授",
        Location: "九龙湖",
    },
}
插入数据
  • 插入一条/多条记录
t1 := teacherTemp 
res := DB.Create(&t) // 指针传入
  • 正向选择, 选择某些字段插入 【gorm model里的不包含】
t1 := teacherTemp
res = DB.Select("name", "age").Create(&t1)
  • 反向选择, 排除某些字段插入(忽略大小写
t2 := teacherTemp
res = DB.Omit("email", "Birthday").Create(&t2)
  • 批量,按batchsize插入
var teachers = []Teacher{{Name: "qq", Age: 50}, {Name: "pp", Age: 60}, {Name: "gg", Age: 55}, {Name: "mm", Age: 56}}
DB.CreateInBatches(teachers, len(teachers))  // 或者直接DB.Create(teachers)
  • 查找第一个匹配的记录并返回,若无匹配记录则插入
t3 := teacherTemp
t3.Name = "oo"
out := Teacher{}
res = DB.Attrs(t3).FirstOrCreate(&out, Teacher{Name: "oo"})
删除数据

gorm采用的是软删除,删除语句为update,去更新delete_at字段

  • 根据ID删除
DB.Delete(&Teacher{Model: gorm.Model{ID: 9}})

gorm输出语句(软删除)

 UPDATE `teachers` SET `deleted_at`='2023-10-09 18:51:43.901' WHERE `teachers`.`id` = 9 AND `teachers`.`deleted_at` IS NULL
  • 根据条件删除, 批量删除
DB.Where("name like ?", "yuan*").Delete(&Teacher{})
DB.Delete(&Teacher{}, []int{3,4,5,})
  • 根据主键删除(主键不一定是gorm.Model的ID)
DB.Delete(&Teacher{}, 1)
DB.Delete(&Teacher{}, []int{3,4,5,})
更新数据
  • 全部更新
teacher := Teacher{}
DB.First(&teacher)
// 更新
teacher.Age = 66
teacher.Name = "wang"
// 更新所有 save 会将所有字段保存到数据库, 无论字段有没有被改
DB.Save(teacher)
  • 更新单行数据某个列
DB.Model(&Teacher{}).Where("id = ?", teacher.ID).Update("name", "yuan")
  • 通过model指定条件更新, 只能指定ID
DB.Model(&Teacher{Model:gorm.Model{ID: teacher.ID}}).Update("name", "yuan")
  • 更新单行多个列
// 更新单行多个列 结构体方式
DB.Model(&Teacher{}).Where("id = ?", teacher.ID).Updates(Teacher{Name: "updatayuan", Age: 40})
// 更新单行多个列 map方式
DB.Model(&Teacher{}).Where("id = ?", teacher.ID).Updates(map[string]interface{}{"name": "updateY", "age": 40})
  • 批量更新 多行数据
DB.Model(&Teacher{}).Where("age > ?", 60).Updates(Teacher{Email: "update@qq.com"}) // 用map也行
  • 选定/排除某些字段更新
// 选定更新某些字段
DB.Model(&Teacher{}).Where("id = ?", 5).Select("name", "age").Updates(teacher)
// 排除某些字段 更新
DB.Model(&Teacher{}).Where("id = ?", 5).Omit("name", "age").Updates(teacher)
查询
简单查询
  • 单记录查询
func GetOnce() {
    t := Teacher{}
    // 查询一条
    // 获取主键排序第一条
    DB.First(&t)

    // 无排序规则 取第一条
    t = Teacher{}
    DB.Take(&t)

    // 主键排序最后一条
    t = Teacher{}
    DB.Last(&t)

    //查询结果填充到集合中
    result := map[string]interface{}{}
    // 可能会有特殊类型不好处理,无法完成类型转换,可以忽略一些字段
    DB.Model(&Teacher{}).Omit("Birthday", "Roles", "JobInfo2").First(&result)


    // 基于表名 查询记录
    result = map[string]interface{}{}
    DB.Table("teachers").Take(&result)

}
  • 根据字符串条件获取记录
func GetByStrCond() {
    t := Teacher{}
    var teaches []Teacher

    DB.Find(&teaches, "name IN ?", []string{"yuan", "oo", "gg"})

    DB.Where("name = ?", "gg").First(&t)

    DB.Where("name IN ?", []string{"yuan", "oo"}).Find(&teaches)
}
  • 通过结构体或集合指定条件查询
func GetByStructOrMapCond() {
    var teachers []Teacher
    t := Teacher{}
    //struct
    DB.Find(&teachers, Teacher{Name: "oo", Age: 40})
    // map
    DB.Find(&teachers, map[string]interface{}{"Name": "gg", "Age": 40})
    
    DB.Where(Teacher{Name: "yuan", Age: 40}).First(&t)
    DB.Where(map[string]interface{}{"Name": "qq", "Age": 40}).Find(&teachers)
    DB.Where([]int{10, 11, 12}).Find(&teachers)
    //指定查询条件使用的字段
    DB.Where(Teacher{Name: "cc"}, "name", "age").Find(&teachers)
}
复合查询
  • 复杂条件查询
var teachers []Teacher
// 复合查询 offset为跳过几个记录,分页会用
DB.Where(DB.Where("name = ?", "yuan").Or("name = ?", "oo")).
Where("age > ?", 40).Order("id desc").Offset(1).Limit(10).Find(&teachers)

// 查询返回行rows
rows, err := DB.Model(&Teacher{}).Select("id", "name").Where(DB.Where("name in ?", []string{"oo", "qq"})).
Order("id desc").Offset(1).Limit(10).Rows()

if err != nil {
    log.Fatal(err)
}
defer rows.Close()
// 扫描row
for rows.Next() {
    id := 0
    name := ""
    err = rows.Scan(&id, &name)
    if err != nil {
        continue
    }
    fmt.Println(id, name)
}
  • 分组聚合
// 分组聚合
type Result struct {
    Count int
    Age   int
}
list := []Result{}
DB.Model(&Teacher{}).Select("count(*) as count", "age").Not("name = ?", "yuan").
Group("age").Having("count > ?", 2).Rows()

fmt.Println(list)
// 根据年龄分组,过滤掉name为yuan的数据,保留对应分组count数大于2的结果
rows2, err := DB.Model(&Teacher{}).Select("count(*) as count", "age").Not("name = ?", "yuan").
Group("age").Having("count > ?", 2).Rows()
if err != nil {
    log.Fatal(err)
}
defer rows2.Close()

for rows2.Next() {
    count := 0
    age := 0
    err = rows2.Scan(&count, &age)
    if err != nil {
        continue
    }
    fmt.Println(count, age)
}
使用原生SQL查询

基本exec和raw就够用了

func NativeSql() {
    var teacher Teacher
    var list []Teacher
    //查询
    DB.Raw("select id,name,age from teachers where name = ?", "yuan").Scan(&teacher)
    fmt.Println(teacher)

    DB.Raw("select id,name,age from teachers where name = ?", "yuan").Scan(&list)
    fmt.Println(list)

    teacher = Teacher{}
    DB.Raw("select id,name,age from teachers where name = ?", "yuan").Find(&teacher)
    fmt.Println(teacher)

    //更新
    res := DB.Exec("update teachers set age = @age where name = @name", sql.Named("age", 22), sql.Named("name", "yuan"))
    fmt.Println(res.RowsAffected, res.Error)

}
<think>嗯,用户想了解如何在Golang使用GORM。首先,我需要确定用户对GORM的了解程度。可能他们已经有Golang的基础,现在想学习ORM库来操作数据库GORM是一个流行的ORM工具,支持多种数据库,比如MySQL、PostgreSQL等。 首先,我应该从安装开始讲起,用户需要知道如何用go get安装GORM和对应的数据库驱动。比如,安装GORMMySQL驱动的话,命令是go get gorm.io/gorm和go get gorm.io/driver/mysql。 接下来是连接数据库的步骤。需要提供一个DSN,如用户名、密码、地址、端口、数据库名等。这里要注意处理可能的错误,比如数据库连接失败的情况,所以需要检查错误并处理。 然后是定义模型结构体。GORM使用结构体来映射数据库表,默认情况下,表名是结构体名的复数形式,但也可以通过TableName()方法自定义。模型中的字段对应表的列,可以使用gorm标签来指定主键、自增等属性。比如定义一个User结构体,包含ID、Name、Email等字段,ID作为主键且自增。 接下来是基本的CRUD操作。创建记录使用Create方法,需要注意指针传递和错误处理。查询的话,可以使用First、Find等方法,First是获取第一条记录,按主键排序。更新操作使用Updates方法,可以部分更新或全量更新,这里要提醒用户注意Select和Omit的使用。删除操作使用Delete,可以是软删除或硬删除,取决于模型是否有DeletedAt字段。 然后是高级查询部分,比如条件查询、预加载关联、事务处理。条件查询可以用Where方法,预加载关联用Preload避免N+1查询问题。事务的话,使用Transaction方法,确保原子性。 关联关系处理可能比较复杂,比如一对一、一对多、多对多。这里需要举说明,比如用户有多个文章,用HasMany关联,文章属于用户,用BelongsTo关联。多对多的话,需要中间表,用Many2Many标签。 最后是注意事项,比如关闭调试模式、避免全表更新、使用连接池。这些是实际开发中容易遇到的问题,需要提醒用户注意。 总结一下,回答的结构应该是先介绍GORM,然后安装、连接数据库、定义模型,CRUD操作,高级查询,关联处理,注意事项。这样用户能逐步学习如何使用GORM,同时避免常见错误。需要确保示代码正确,符合GORM的最新语法,比如使用gorm.Open的格式,以及正确的标签写法。另外,要提醒用户处理错误,避免程序崩溃。可能用户在使用过程中会遇到问题,比如表名不对,字段未正确映射,事务处理不当等,所以注意事项部分要强调这些点。</think>以下为Golang GORM使用介绍,分为基础操作和进阶技巧两部分: 📌 基础篇 1️⃣ 安装与连接 ```go go get gorm.io/gorm go get gorm.io/driver/mysql // 以MySQL // 连接数据库 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("数据库连接失败") } ``` 2️⃣ 模型定义 ```go type User struct { ID uint `gorm:"primaryKey;autoIncrement"` Name string `gorm:"type:varchar(100);not null"` Email string `gorm:"uniqueIndex"` CreatedAt time.Time } // 自定义表名 func (User) TableName() string { return "sys_user" } ``` 3️⃣ CRUD操作 ```go // 创建 user := User{Name: "张三", Email: "zhangsan@example.com"} result := db.Create(&user) // 查询 var findUser User db.First(&findUser, "name = ?", "张三") // 更新 db.Model(&user).Updates(User{Name: "李四"}) // 删除 db.Delete(&user) ``` 🚀 进阶技巧 1️⃣ 关联查询 ```go // 预加载关联数据 db.Preload("Orders").Find(&users) // 一对多关联定义 type User struct { ID uint Orders []Order `gorm:"foreignKey:UserID"` } type Order struct { UserID uint // ...其他字段 } ``` 2️⃣ 事务处理 ```go db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&user).Error; err != nil { return err } if err := tx.Create(&order).Error; err != nil { return err } return nil }) ``` 3️⃣ 复杂查询 ```go // 条件组合 db.Where("age > ?", 18). Order("created_at desc"). Limit(10). Find(&users) // 原生SQL db.Raw("SELECT * FROM users WHERE age > ?", 18).Scan(&result) ``` ⚠️ 注意事项 1. 生产环境关闭Debug模式: ```go db, err := gorm.Open(..., &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent) }) ``` 2. 批量操作时建议分批次处理: ```go db.CreateInBatches(users, 100) ``` 3. 使用连接池配置: ```go sqlDB, _ := db.DB() sqlDB.SetMaxOpenConns(100) sqlDB.SetMaxIdleConns(10) ``` 建议结合官方文档(https://gorm.io/zh_CN/docs/)进行实践,实际开发中注意错误处理和日志记录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值