gorm框架的用法

连接数据库

增加&查询

package main

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

type Student struct {
    ID     uint   `gorm:"size:3"`
    Name   string `gorm:"size:8"`
    Age    int    `gorm:"size:3"`
    Gender bool
    Email  *string `gorm:"size:32"`
}

func main() {
    DB.AutoMigrate(&Student{})

    //添加记录
    email := "123456"

    //批量插入
    var studentList []Student

    for i := 0; i < 10; i++ {
     studentList = append(studentList, Student{
        Name:   "User" + strconv.Itoa(i+1),
        Age:    18,
        Gender: true,
        Email:  &email,
     })
    }
    
    err := DB.Create(&studentList).Error

    fmt.Println(err)

    //单条记录的查询
    //var student Student
    DB = DB.Session(&gorm.Session{
       Logger: mysqlLogger,
    })
    DB.Take(&student) //会查第一条数据
    DB.First(&student)
    fmt.Println(student)
    DB.Last(&student)
    fmt.Println(student)
    fmt.Println(student)

    //根据主键查询
    DB.Take(&student, 2)
    fmt.Println(student)

    //查询
    DB.Take(&student, "name = ?", "ei")

    //查询多条记录
    var studentList []Student
    
    count := DB.Find(&studentList).RowsAffected
    fmt.Println(count)
    //转为json
    data, _ := json.Marshal(studentList)
    fmt.Println(string(data))

    //根据主键列表查询
    var studentList []Student
    DB.Find(&studentList, []int{2, 4, 6, 8})
    fmt.Println(studentList)

    //根据其他条件查询
    var studentList []Student
    DB.Find(&studentList, []int{2, 4, 6, 8})
    DB.Find(&studentList, "name in (?)", []string{"User1", "User2"})
    fmt.Println(studentList)
}

更新

//sava更新
	var student Student
	DB.Take(&student, 11)
	student.Name = "lin"
	DB.Save(&student)

	//改多个字段
	var studentList []Student
	DB.Find(&studentList, []int{9, 10, 11}).Update("gender", false)

	//改多行多列
	DB.Find(&studentList, []int{9, 10}).Updates(Student{Age: 50, Gender: true})

	//使用map
	DB.Find(&studentList, []int{9, 10}).Updates(map[string]any{
		"name": "in",
	})

删除

//删除
var student Student
DB.Delete(&student, 8)
DB.Delete(&student, []int{9, 10})

//先查再删
DB.Take(&student)
DB.Delete(&student)

创建HOOK钩子

func (user *Student) BeforeCreate(tx *gorm.DB) (err error) {
    email := "test@qq.com"
    user.Email = &email
    return nil
}
func main(){
	DB = DB.Session(&gorm.Session{
		Logger: mysqlLogger,
	})
	
	DB.Create(&Student{
		Name: "six",
		Age:  18,
	})
}

查询

var users []Student
//查询用户名是枫枫的,存储在users中
DB.Where("name = ?", "枫枫").Find(&users)
fmt.Println(users)
//查询名字不是枫枫的
DB.Where("name <> ?", "枫枫").Find(&users)
fmt.Println(users)
// 查询用户名包含 如燕,李元芳的
DB.Where("name in ?", []string{"如燕", "李元芳"}).Find(&users)
fmt.Println(users)
// 查询姓李的
DB.Where("name like ?", "李%").Find(&users)
fmt.Println(users)
// 查询年龄大于23,是qq邮箱的
DB.Where("age > ? and email like ?", "23", "%@qq.com").Find(&users)
fmt.Println(users)
// 查询是qq邮箱的,或者是女的
DB.Where("gender = ? or email like ?", false, "%@qq.com").Find(&users)
fmt.Println(users)

使用结构体查询

使用结构体会过滤零值,如果是查询条件是零值的话,会不考虑这个条件

并且结构体中的条件都是and关系

//结构体查询会过滤零值
	DB.Where(&Student{Name: "元芳", Age: 12}).Find(&users)
	fmt.Println(users)

使用map查询

不会过滤零值

//map不会过滤零值
DB.Where(map[string]any{"name": "元芳", "age": 0}).Find(&users)
fmt.Println(users)

Not

// 排除年龄大于23的
DB.Not("age > 23").Find(&users)
fmt.Println(users)

Or

DB.Or("gender = ?", false).Or(" email like ?", "%@qq.com").Find(&users)
fmt.Println(users)

Select

没有被选中,会被赋零值

DB.Select("name", "age").Find(&users)
fmt.Println(users)
//也可以存入另一个结构体中
type User struct {
  Name string
  Age  int
}
var students []Student
var users []User
DB.Select("name", "age").Find(&students).Scan(&users)
fmt.Println(users)
//这样会查询两次

查询一次的话有两种方法

var users []User
DB.Model(&Student{}).Select("name", "age").Scan(&users)
fmt.Println(users)
var users []User
DB.Table("student").Select("name", "age").Scan(&users)
fmt.Println(users)

排序

var users []Student
DB.Order("age desc").Find(&users)
fmt.Println(users)
// desc    降序
// asc     升序

分页查询

var users []Student
// 一页两条,第1页
DB.Limit(2).Offset(0).Find(&users)
fmt.Println(users)
// 第2页
DB.Limit(2).Offset(2).Find(&users)
fmt.Println(users)
// 第3页
DB.Limit(2).Offset(4).Find(&users)
fmt.Println(users)

更通用的方法

var users []Student
//一页多少条
limit := 2
//第几页
page := 1
offset := (page - 1) * limit
DB.Limit(limit).Offset(offset).Find(&users)
fmt.Println(users)

加个for循环打印出所有的

var users []Student
	limit := 2  // 每页多少条
	page := 1   // 从第一页开始
	offset := 0 // 偏移量
for {
	// 计算偏移量
	offset = (page - 1) * limit

	// 查询当前页的数据
	DB.Limit(limit).Offset(offset).Find(&users)

	// 如果当前页没有数据了,说明已经查询完所有数据
	if len(users) == 0 {
		break
	}

	// 打印当前页的数据
	fmt.Println(users)
    
	// 清空切片,准备下一次查询
	users = []Student{}

	// 增加页码
	page++
}

去重

//去重
var ageList []int
DB.Table("students").Select("age").Distinct("age").Scan(&ageList)
//或者
//DB.Table("students").Select("distinct age").Scan(&ageList)
fmt.Println(ageList)

分组查询

//查询男生的人数和女生的个数
var ageList []int
DB.Table("students").Select("count(id)").Group("gender").Scan(&ageList)
fmt.Println(ageList)

//精确一点
type AggeGroup struct {
  Gender int
  Count  int `gorm:"column:count(id)"`
}
var agge []AggeGroup
//查询男生的个数和女生的个数
DB.Table("students").Select("count(id)", "gender").Group("gender").Scan(&agge)
fmt.Println(agge)

//更精确一点
type AggeGroup struct {
    Gender int
    Count  int    `gorm:"column:count(id)"`
    Name   string `gorm:"column:group_concat(name)"`
}
//GROUP_CONCAT是 MySQL 中的一个聚合函数,用于将分组内的某个字段值连接成一个字符串
var agge []AggeGroup
//查询男生的个数和女生的个数
DB.Table("students").Select("count(id)", "gender", "group_concat(name)").Group("gender").Scan(&agge)
fmt.Println(agge)

执行原生sql

type AggeGroup struct {
  Gender int
  Count  int    `gorm:"column:count(id)"`
  Name   string `gorm:"column:group_concat(name)"`
}

var agge []AggeGroup
DB.Raw(`SELECT count(id), gender, group_concat(name) FROM students GROUP BY gender`).Scan(&agge)

fmt.Println(agge)

子查询

# 原生sql
select * from students where age > (select avg(age) from students);

//相当于
var users []Student
DB.Model(Student{}).Where("age > (?)", DB.Model(Student{}).Select("avg(age)")).Find(&users)
fmt.Println(users)

命名参数

var users []Student
DB.Where("name = @name and age = @age", sql.Named("name", "枫枫"), sql.Named("age", 23)).Find(&users)
DB.Where("name = @name and age = @age", map[string]any{"name": "枫枫", "age": 23}).Find(&users)
fmt.Println(users)

Find到map

var res []map[string]any
DB.Table("students").Find(&res)
fmt.Println(res)

查询引用scope

可以再model层写一些通用的查询方式,这样外界就可以直接调用方法即可

func Age23(db *gorm.DB) *gorm.DB {
  return db.Where("age > ?", 23)
}

func main(){
  var users []Student
  DB.Scopes(Age23).Find(&users)
  fmt.Println(users)
}

一对多关系

创建表

type User struct {
    ID       uint
    Name     string `gorm:"size:8"`
    Articles []Article
}

type Article struct {
    ID     uint
    Title  string `gorm:"size:16"`
    UserID uint
    User   User
}

func main() {
    //创建表
    DB.Debug().AutoMigrate(&User{}, &Article{})
}
//删除表
DB.Debug().Migrator().DropTable(&User{})

创建&关联数据

//创建用户,带上文章
DB.Create(&User{
  Name: "枫枫",
  Articles: []Article{
     {
        Title: "python",
     },
     {
        Title: "golang",
     },
  },
})

//创建文章,关联用户一起创建
DB.Create(&Article{
  Title: "java",
  User: User{
     Name: "lin",
  },
})

//创建文章,关联用户
var user User
DB.Take(&user, 2)
DB.Create(&Article{
  Title: "C++",
  User:  user,
})

//创建文章,关联用户
a1 := Article{Title: "golang零基础入门", UserID: 1}
DB.Create(&a1)

//外键添加
//给现有用户绑定文章
var user User
DB.Take(&user, 2)

var article Article
DB.Take(&article, 5)
//使用append
DB.Model(&user).Association("Articles").Append(&article)
//其他方法
//user.Articles = []Article{article}
//DB.Save(&user)

//给现有文章关联用户
var article Article
DB.Take(&article, 5)

article.UserID = 1
DB.Save(&article)

//使用append
var article Article
DB.Take(&article, 5)

DB.Model(&article).Association("User").Append(&user)

查询

预加载
//显示文章列表
var user User
DB.Preload("Articles").Take(&user, 1)
fmt.Println(user)

//查询文章,显示文章的用户信息
var article Article
DB.Preload("User").Take(&article, 1)
fmt.Println(article)
嵌套预加载
var article Article
DB.Preload("User.Articles").Take(&article, 1)
fmt.Println(article)
带条件的预加载

查询用户下的所有文章列表,过滤某些文章

var user User
DB.Preload("Articles", "id = ?", 1).Take(&user, 1)
fmt.Println(user)

这样,就只有id为1的文章被预加载出来了

自定义预加载
var user User
DB.Preload("Articles", func(db *gorm.DB) *gorm.DB {
  return db.Where("id in ?", []int{1, 2})
}).Take(&user, 1)
fmt.Println(user)

删除

级联删除

删除用户,与用户关联的文章也会删除

var user User
DB.Take(&user, 1)
DB.Select("Articles").Delete(&user)
清除外键关系

删除用户,与将与用户关联的文章,外键设置为null

var user User
DB.Preload("Articles").Take(&user, 2)
DB.Model(&user).Association("Articles").Delete(&user.Articles)

一对一关系

//添加用户,会自动添加用户详情
DB.Create(&User{
  Name:   "lin",
  Age:    18,
  Gender: true,
  UserInfo: UserInfo{
     Addr: "河南",
     Like: "sleep",
  },
})

//查询
var user User
DB.Preload("UserInfo").Take(&user)
fmt.Println(user)

多对多关系

添加

//添加文章,并创建标签
DB.Create(&Article{
    Title: "《python》",
    Tags: []Tag{
       {Name: "python"},
       {Name: "编程"},
    },
})

//添加文章,选择标签
var tags []Tag
DB.Find(&tags, "name = ?", "编程基础")
DB.Create(&Article{
    Title: "《golang》",
    Tags:  tags,
})

查询

//查询文章,显示文章标签列表
var article Article
DB.Preload("Tags").Take(&article, 1)
fmt.Println(article)

//查询标签显示文章列表
var tag Tag
DB.Preload("Articles").Take(&tag, 2)
fmt.Println(tag)

多对多更新

//更新
//移除文章标签
var article Article
DB.Preload("Tags").Take(&article, 1)
DB.Model(&article).Association("Tags").Delete(article.Tags)
fmt.Println(article)

//更新文章标签
var article Article
var tags []Tag
DB.Find(&tags, []int{1})

DB.Preload("Tags").Take(&article, 2)
DB.Model(&article).Association("Tags").Replace(tags)
fmt.Println(article)

自定义连接表

DB.Debug().Migrator().DropTable(ArticleTag{})
//自定义连接表
//显示除了双方的主键id的其他列
//生成表结构
//设置Article的Tags表为ArticleTag
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
//如果tag要反向应用article,那么也要加上
DB.SetupJoinTable(&Tag{}, "Articles", &ArticleTag{})
err := DB.AutoMigrate(&Article{}, &Tag{}, &ArticleTag{})
fmt.Println(err)

//添加文章标签,并自动关联
DB.SetupJoinTable(&Article{},"Tags",&ArticleTag{})//必须先设置这个才能走到自定义连接表
DB.Create(&Article{
  Title: "《gorm入门》",
  Tags: []Tag{
     {Name: "go"},
     {Name: "web"},
     {Name: "后端"},
  },
})

//添加文章,关联已有标签
DB.SetupJoinTable(&Article{},"Tags",&ArticleTag{})//必须先设置这个才能走到自定义连接表
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
var tags []Tag
DB.Find(&tags, "name is ?", []string{"web", "后端"})
DB.Create(&Article{
  Title: "《gin框架入门》",
  Tags:  tags,
})

//给已有文章关联标签
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
article := Article{
    Title: "《django基础》",
}
DB.Create(&article)
var at Article
var tags []Tag
DB.Find(&tags, "name in ?", []string{"go", "web"})
DB.Take(&at, article.ID).Association("Tags").Append(tags)

//替换已有文章的标签
DB.Model(&article).Association("Tags").Replace(tags)

//查询文章列表,显示标签
DB.Preload("Tags").Find(&article)
fmt.Println(article)

自定义连接表主键

type ArticleModel struct {
  ID    uint
  Title string
  Tags  []TagModel `gorm:"many2many:article_tags;joinForeignKey:ArticleID;JoinReferences:TagID"`
}

type TagModel struct {
  ID       uint
  Name     string
  Articles []ArticleModel `gorm:"many2many:article_tags;joinForeignKey:TagID;JoinReferences:ArticleID"`
}

type ArticleTagModel struct {
  ArticleID uint `gorm:"primaryKey"` // article_id
  TagID     uint `gorm:"primaryKey"` // tag_id
  CreatedAt time.Time
}

func main(){
    DB.SetupJoinTable(&ArticleModel{}, "Tags", &ArticleTagModel{})
	DB.SetupJoinTable(&TagModel{}, "Articles", &ArticleTagModel{})
	err := DB.AutoMigrate(&ArticleModel{}, &TagModel{}, &ArticleTagModel{})
	fmt.Println(err)
}

### 关于 GORM 框架的常见面试问题 #### 什么是 GORMGORM 是 Go 语言的一个对象关系映射(ORM)库,它简化了数据库操作的过程。通过 GORM开发者可以轻松地将结构体映射到数据库表,并执行各种复杂的数据库操作而无需手动编写大量 SQL 语句[^1]。 #### 如何在 GORM 中定义一个模型并将其映射到数据库中的表? 在 GORM 中,可以通过定义一个结构体来表示数据库中的表。例如: ```go type User struct { ID uint `gorm:"primaryKey"` Name string `gorm:"size:255;not null"` Email string `gorm:"uniqueIndex;not null"` Password string `gorm:"size:100;not null"` } ``` 上述代码中,`User` 结构体会被自动映射到名为 `users` 的表上。如果需要自定义表名,可以在初始化时调用 `TableName()` 方法或者使用标签指定。 #### 如何在 GORM 中执行基本的 CRUD 操作? CRUD 操作是任何 ORM 库的核心功能之一,在 GORM 中实现这些操作非常简单。以下是几个常见的例子: - **创建记录** ```go db.Create(&user) ``` - **读取记录** ```go var user User db.First(&user, 1) // 查询 id=1 的用户 ``` - **更新记录** ```go db.Model(&user).Update("Name", "NewName") ``` - **删除记录** ```go db.Delete(&user) ``` 以上方法均支持链式调用和条件过滤器。 #### 如何在 GORM 中执行原生 SQL 语句? 尽管 GORM 提供了许多高级特性,但在某些情况下可能仍需执行原始 SQL 语句。这可以通过以下方式完成: ```go db.Exec("UPDATE users SET name = ? WHERE email = ?", "new_name", "email@example.com") ``` 对于更复杂的情况,还可以结合扫描结果: ```go var count int64 db.Raw("SELECT COUNT(*) FROM users").Scan(&count) ``` 这种方式提供了极大的灵活性。 #### 如何在 GORM 中使用事务? 事务管理在多步操作中非常重要,GORM 支持显式的事务控制。下面是一个简单的示例: ```go tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Create(&order).Error; err != nil { tx.Rollback() } else { tx.Commit() } ``` 此代码片段展示了如何安全地提交或回滚事务。 #### 如何在 GORM 中执行联合查询? 联合查询通常涉及多个表之间的关联数据提取。GORM 可以通过预加载机制解决这一需求: ```go // 使用 Preload 加载关联的数据 db.Preload("Orders").Find(&users) // 或者使用 Joins 手动构建联结查询 db.Joins("JOIN orders ON orders.user_id = users.id").Find(&users) ``` 这两种方式各有优劣,具体取决于实际场景的需求。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值