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

gorm + mysql增删改查

model定义
go 复制代码
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"`
}
数据库连接并设置连接池
go 复制代码
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)

}

假数据定义

go 复制代码
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: "九龙湖",
    },
}
插入数据
  • 插入一条/多条记录
go 复制代码
t1 := teacherTemp 
res := DB.Create(&t) // 指针传入
  • 正向选择, 选择某些字段插入 【gorm model里的不包含】
go 复制代码
t1 := teacherTemp
res = DB.Select("name", "age").Create(&t1)
  • 反向选择, 排除某些字段插入(忽略大小写
go 复制代码
t2 := teacherTemp
res = DB.Omit("email", "Birthday").Create(&t2)
  • 批量,按batchsize插入
go 复制代码
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)
  • 查找第一个匹配的记录并返回,若无匹配记录则插入
go 复制代码
t3 := teacherTemp
t3.Name = "oo"
out := Teacher{}
res = DB.Attrs(t3).FirstOrCreate(&out, Teacher{Name: "oo"})
删除数据

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

  • 根据ID删除
go 复制代码
DB.Delete(&Teacher{Model: gorm.Model{ID: 9}})

gorm输出语句(软删除)

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

}
  • 根据字符串条件获取记录
go 复制代码
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)
}
  • 通过结构体或集合指定条件查询
go 复制代码
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)
}
复合查询
  • 复杂条件查询
go 复制代码
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)
}
  • 分组聚合
go 复制代码
// 分组聚合
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就够用了

go 复制代码
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)

}
相关推荐
问道飞鱼1 小时前
【知识科普】认识正则表达式
数据库·mysql·正则表达式
HaiFan.1 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
PersistJiao3 小时前
3.基于 Temporal 的 Couchbase 动态 SQL 执行场景
数据库·sql
上山的月3 小时前
MySQL -函数和约束
数据库·mysql
zhcf3 小时前
【MySQL】十三,关于MySQL的全文索引
数据库·mysql
丁总学Java3 小时前
要查询 `user` 表中 `we_chat_open_id` 列不为空的用户数量
数据库·mysql
抓哇能手3 小时前
数据库系统概论
数据库·人工智能·sql·mysql·计算机
KELLENSHAW6 小时前
MySQL45讲 第三十七讲 什么时候会使用内部临时表?——阅读总结
数据库·mysql
一只淡水鱼666 小时前
【mybatis】详解 # 和 $ 的区别,两者分别适用于哪种场景,使用 $ 不当会造成什么影响
sql·spring·mybatis·sql注入
四七伵6 小时前
MySQL外键类型与应用场景总结:优缺点一目了然
mysql