Day2:GORM入门
1.环境的安装
在项目文件的terminal中输入下面两条命令进行gorm安装
go get gorm.io/driver/mysql go get gorm.io/gorm
2.安装好之后使用以下代码进行检测,其中的地址拼接是重点
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, Dbname, timeout
func init() {
username := "root" //账号
password := "147258" //密码
host := "127.0.0.1" //数据库地址,可以是Ip或者域名
port := 3306 //数据库端口
Dbname := "gorm" //数据库名
timeout := "10s" //连接超时,10秒
// root:root@tcp(127.0.0.1:3306)/gorm?
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, Dbname, timeout)
//连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "f_", // 表名前缀
NoLowerCase: false, // 关闭小写转换
},
})
if err != nil {
panic("连接数据库失败, error=" + err.Error())
}
// 连接成功
fmt.Println(db)
DB = db
}
3.连接数据库的重点语句是gorm.open(数据库类型.open(数据库拼接地址)),在Open中添加&gorm.config(根据需求添加相关配置)
例如数据表命名的策略如下所示
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "f_", // 表名前缀
SingularTable: false, // 单数表名
NoLowerCase: false, // 关闭小写转换
},
})
4.打印日志的三种方式
1 1.DB.Debug().AutoMigrate(&Student{}) 2 2.展示部分日志:var model Student session := DB.Session(&gorm.Session{Logger: newLogger}) 3 session.First(&model) 4 3.var mysqlLogger logger.Interface 5 // 要显示的日志等级 mysqlLogger = logger.Default.LogMode(logger.Info) 6 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: mysqlLogger, })
Day2:GORM模型定义、单表操作
一、模型定义
1.当自动创建表的结构太大时,可以使用gorm:size:大小,进行操作,如下图所示

操作方式,实现结果如下:



字段标签,将gorm后面的size替换下面指令就行
type 定义字段类型
size 定义字段大小
column 自定义列名
primaryKey 将列定义为主键
unique 将列定义为唯一键
default 定义列的默认值
not null 不可为空
embedded 嵌套字段
embeddedPrefix 嵌套字段前缀
comment 注释
多个标签之前用 ; 连接
二、单表操作
1.使用DB.create(interface{})进行插入数据
DB.AutoMigrate(&students{})
email := "123456@qq.com"
s := students{
Name: "xx",
Age: 22,
Gender: false,
Email: &email,
}
DB.Create(&s)
2.批量插入
for i := 0; i < 10; i++ {
studentsList = append(studentsList, students{
Name: fmt.Sprintf("Robot%d号", i+1), //使用Sprintf可以拼接字符串
Age: 22,
Gender: true,
Email: nil,
})
}
DB.Create(&studentsList)
3.单表查询
var student students
DB.Take(&student) //查询一个student数据
fmt.Println(student)
DB = DB.Session(&gorm.Session{Logger: mysqlLogger}) //打印操作日志
var studentList []students //查询所有students的数据
DB.Take(&studentList)
fmt.Println(studentList)
DB.First(&studentList)
fmt.Println(studentList)
DB.Last(&studentList)
fmt.Println(studentList)
4.根据主键查询
//根据主键查询,查询主键为3的数据 DB.Take(&studentList, 3) //第二个参数是主键位置 fmt.Println(studentList)
5.根据其他条件查询
//根据其他条件查询,查询名字为Robot5号的信息
err := DB.Take(&studentList, "name=?", "Robot5号").Error
switch err {
case gorm.ErrRecordNotFound:
fmt.Println("没有找到")
default:
fmt.Println("sql错误")
}
fmt.Println(studentList)
6.根据结构体查询
var student students student.ID = 3 DB.Take(&student) fmt.Println(student)
7.查询多条记录
count := DB.Find(&studentList).RowsAffected //获取查询的记录数
fmt.Println(count)
for _, student := range studentList {
data, _ := json.Marshal(student)
fmt.Println(string(data))
}
8.根据主键查询多条记录
//DB.Find(&studentList, []int{1, 3, 5, 7})
//DB.Find(&studentList, 1, 3, 5, 7) // 一样的
//fmt.Println(studentList)
9.根据其它条件查询多条记录
DB.Find(&studentList, "name in ?", []string{"Robot3号", "xx"})
fmt.Println(studentList)
三、更新表操作
1.更新所有字段,指定字段
//更新所有字段 var student students email := "123456789@qq.com" DB.Take(&student) //获取第一个数据 student.Age = 18 //更改第一个数据的年龄 student.Email = &email student.XXX=xxx
//使用select更新指定字段
DB.Select("age").Save(&student) //重新提交数据库
DB.Save(&student) //重新提交数据库
2.批量更新
//批量更新
var studentList []students
DB.Find(&studentList, "age=?", 22).Update("email", "is22@qq.com")
3.更新多列
var studentList []students
DB.Model(&studentList).Where("age=?", 22).Updates(map[string]any{
"name": "坨哥",
"gender": false,
})
4.根据ID删除
//根据ID删除行数据 var student students DB.Delete(&student, 4)
四、在重新创建新的go文件时遇到undefined DB 问题解决方案

问题原因:在一个go文件中创建DB连接数据库后,其它的go文件无法直接调用。
解决方案:因为所有的go文件都创建在一个目录中,导致出现以上问题。因此,将连接文件单独放在一个目录中,在其它文件中去调用连接数据的文件
方法如下:


此时,在其它文件中调用DB就是已经连接好的DB状态了,调用形式如下

Day3:GORM之高级查询
一.Where语句的使用
//查询用户名是xx的指令
DB.Where("name=?", "李元芳").Find(&student)
fmt.Println(student)
//查询用户名不是XX的指令
DB.Where("name<>?", "李元芳").Find(&student)
for _, s := range student {
fmt.Println(s)
}
// 查询用户名包含 如燕,李元芳的
DB.Where("name in ?", []string{"李元芳", "枫枫"}).Find(&student)
fmt.Println(student)
//模糊查询
DB.Where("name like ?", "李%").Find(&student)
fmt.Println(student)
//根据多个条件查询
DB.Where("name like ? and email like ?", "李%", "%@lly.cn").Find(&student)
fmt.Println(student)
//根据判断条件查询
DB.Where("age > ? and gender = ?", "20", "1").Find(&student)
fmt.Println(student)
//select 选择字段,没有选择的字段打印时都会赋零值,但是不改变表的实际内容
DB.Select("name", "age").Find(&student)
fmt.Println(student)
//使用scan,将select选择的字段存入另一个结构体中
var list []students
DB.Select("name", "age").Find(&student).Scan(&list)
fmt.Println(list)
二.排序
排序分为升序和降序
//降序排序
DB.Order("age desc").Find(&student)
fmt.Println(student)
//升序排序
DB.Order("age asc").Find(&student)
fmt.Println(student)
三.分页查询
//分页查询 // 一页多少条数据 limit := 2 // 第几页 page := 1 offset := (page - 1) * limit DB.Limit(limit).Offset(offset).Find(&student) fmt.Println(student)
四.去重
//去重.DB.table("数据表名称")
var ageList []int
DB.Table("f_students").Select("distinct age").Find(&ageList)
fmt.Println(ageList)
五、分组查询
//以gender分类统计每类的总数,并且使用group_concat(name),将name也同时分类
type AggeGroup struct {
Gender int
Count int
Name string
}
var agg []AggeGroup
DB.Table("f_students").Select("count(id) as count", "gender", "group_concat(name) as name").Group("gender").Scan(&agg)
fmt.Println(agg
六、子查询
//子查询,嵌套查询
var student []students
//(?) 由括号包裹的问号表示函数
DB.Table("f_students").Where("age>(?)", DB.Table("f_students").Select("avg(age)")).Find(&student)
fmt.Println(student)
//命名参数
DB.Where("name=@name and age = @age",
map[string]any{
"name": "枫枫",
"age": 23,
}).Find(&student)
fmt.Println(student)
//使用函数进行查询
var student []students
DB.Scopes(LikeLi).Find(&student)
fmt.Println(student)
//创建查询函数,方便调用
func LikeLi(DB *gorm.DB) *gorm.DB {
return DB.Where("name like ?", "李%")
}
Day4:GORM之一对多关系
一、一对多关系创建
package main import "GROM_study/conjunction" var DB = conjunction.DB 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.AutoMigrate(&User{}, &Article{}) }
二、一对多的添加
1 //DB.AutoMigrate(&User{}, &Article{}) 2 a1 := Article{Title: "python"} 3 a2 := Article{Title: "golang"} 4 user := User{Name: "枫枫", Articles: []Article{a1, a2}} 5 DB.Create(&user) 6 7 //创建文章关联用户 8 a1 := Article{Title: "golang零基础入门", UserID: 1} 9 DB.Create(&a1) 10 //给现有用户绑定文章 11 var user User 12 DB.Take(&user, 2) //取出第二个用户 13 var article Article 14 DB.Take(&article, 5) //取出第五篇文章 15 user.Articles = []Article{article} //将第五篇文章关联到第二个用户 16 DB.Save(&user) 17 18 //更改现有关联 19 var article Article 20 DB.Take(&article, 5) 21 article.UserID = 1 22 DB.Save(&article)
三、一对多关系的查询和删除
1.查询
//查询 var user User DB.Take(&user, 1) fmt.Println(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 user User DB.Preload("Articles", "id = ?", 1).Take(&user, 1) fmt.Println(user) //自定义的预加载 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)
2.删除
1 //删除 2 //级联删除,删除用户,与用户关联的文章也会删除 3 var user User 4 DB.Take(&user, 1) 5 DB.Select("Articles").Delete(&user) 6 7 //删除用户与文章之间的关联关系 8 var user User 9 DB.Preload("Articles").Take(&user, 2) 10 DB.Model(&user).Association("Articles").Delete(&user.Articles)
Day5:GORM之多对多关系
1.创建多对多关系表
1 package main 2 3 import ( 4 "GROM_study/conjunction" 5 "fmt" 6 "time" 7 ) 8 9 var DB = conjunction.DB 10 11 /** 12 文章有ID,名称title和标签tags,因为一个文章可能含有多个标签 13 同时一个标签可能属于多个文章 14 因此文章和标签之间存在多对多的关系 15 */ 16 type Article struct { 17 ID uint 18 Title string 19 Tags []Tag `gorm:"many2many:article_tags"` 20 } 21 22 /** 23 标签有ID和名称 24 */ 25 type Tag struct { 26 ID uint 27 Name string 28 } 29 30 /** 31 建立文章标签关系表,分别将这两个表的ID作为主键 32 且参数名称是文章和标签表的名称拼接上ID 33 最后设置一个创建时间 34 */ 35 type ArticleTag struct { 36 ArticleID uint `gorm:"primaryKey"` 37 TagID uint `gorm:"primaryKey"` 38 CreatedAt time.Time 39 } 40 41 func main() { 42 // 设置Article表与Tag表的联系为ArticleTag 43 DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{}) 44 // 如果tag要反向应用Article,那么也得加上 45 // DB.SetupJoinTable(&Tag{}, "Articles", &ArticleTag{}) 46 err := DB.AutoMigrate(&Article{}, &Tag{}, &ArticleTag{}) 47 fmt.Println(err) 48 }
2.对多对多关系的表进行相关操作
//给文章添加标签 DB.Create(&Article{ Title: "语文", Tags: []Tag{ {Name: "文科"}, {Name: "难学"}, {Name: "不易考高分"}, }}) //添加文章,关联已有标签 var tags []Tag DB.Find(&tags, "name in ?", []string{"文科", "不易考高分"}) DB.Create(&Article{ Title: "政治", Tags: tags, }) //给文章打上标签 article := Article{Title: "Python"} DB.Create(&article) var tags []Tag var ac Article DB.Find(&ac, "title = ?", "Golang") DB.Find(&tags, "name in ?", []string{"后端开发", "编程", "工科"}) DB.Model(&ac).Association("Tags").Append(tags) //删除文章的标签,只保留后端开发这个标签 var article Article var tags []Tag DB.Find(&tags, "name in ?", []string{"后端开发"}) DB.Find(&article, "title = ?", "Golang") DB.Model(&article).Association("Tags").Replace(&tags) //查询文章列表,并显示标签 var articles []Article DB.Preload("Tags").Find(&articles) fmt.Println(articles)
Day6.续GORM之多对多关系,自定义连接表主键
1 type UserModel struct { 2 ID uint 3 Name string 4 Collects []ArticleModel `gorm:"many2many:user_collect_models;joinForeignKey:UserID;JoinReferences:ArticleID"` 5 } 6 7 type ArticleModel struct { 8 ID uint 9 Title string 10 } 11 12 // UserCollectModel 用户收藏文章表 13 type UserCollectModel struct { 14 UserID uint `gorm:"primaryKey"` // article_id 15 UserModel UserModel `gorm:"foreignKey:UserID"` 16 ArticleID uint `gorm:"primaryKey"` // tag_id 17 ArticleModel ArticleModel `gorm:"foreignKey:ArticleID"` 18 CreatedAt time.Time 19 }
var collects []UserCollectModel var user UserModel DB.Take(&user, "name = ?", "枫枫") // 这里用map的原因是如果没查到,那就会查0值,如果是struct,则会忽略零值,全部查询 DB.Debug().Preload("UserModel").Preload("ArticleModel").Where(map[string]any{"user_id": user.ID}).Find(&collects) for _, collect := range collects { fmt.Println(collect) }
type Status int type Host struct { ID uint `json:"id"` IP string `json:"ip"` Status Status `gorm:"size:8" json:"status"` } const ( Running Status = 1 Except Status = 2 OffLine Status = 3 ) //在枚举中都要实现这个方法 func (s Status) MarshalJSON() ([]byte, error) { return json.Marshal(s.String()) } func (s Status) String() string { var str string switch s { case Running: str = "Running" case Except: str = "Except" case OffLine: str = "Status" } return str } func main(){ DB.AutoMigrate(&Host{}) //DB.Create(&Host{ // IP: "192.168.200.12", // Status: Running, //}) var host Host DB.Take(&host) fmt.Println(host) fmt.Printf("%#v,%T\n", host.Status, host.Status) data, _ := json.Marshal(host) fmt.Println(string(data)) }
事务的提交
package main import ( "GROM_study/conjunction" "fmt" "gorm.io/gorm" ) var DB = conjunction.DB type User struct { ID int `json:"id"` Name string `json:"姓名"` Money int `json:"money"` } func main() { //DB.AutoMigrate(&User{}) var p1, p2 User DB.Take(&p1, "name = ?", "张三") DB.Take(&p2, "name = ?", "李四") //开启事务 DB.Transaction(func(tx *gorm.DB) error { //p1转账100给p2 if p1.Money >= 100 { p1.Money -= 100 err := tx.Model(&p1).Update("money", p1.Money).Error if err != nil { fmt.Println(err) return err } //转账成功再给p2加100 p2.Money += 100 err = tx.Model(&p2).Update("money", p2.Money).Error if err != nil { fmt.Println(err) return err } } return nil }) }
手动提交
// 开始事务 tx := db.Begin() // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db') tx.Create(...) // ... // 遇到错误时回滚事务 tx.Rollback() // 否则,提交事务 tx.Commit()
具体示例如下
var zhangsan, lisi User DB.Take(&zhangsan, "name = ?", "张三") DB.Take(&lisi, "name = ?", "李四") // 张三给李四转账100元 tx := DB.Begin() // 先给张三-100 zhangsan.Money -= 100 err := tx.Model(&zhangsan).Update("money", zhangsan.Money).Error if err != nil { tx.Rollback() } // 再给李四+100 lisi.Money += 100 err = tx.Model(&lisi).Update("money", lisi.Money).Error if err != nil { tx.Rollback() } // 提交事务 tx.Commit()
浙公网安备 33010602011771号