Day2:GORM入门
1.环境的安装
在项目文件的terminal中输入下面两条命令进行gorm安装
1 2 3 | go get gorm.io/driver/mysql go get gorm.io/gorm |
2.安装好之后使用以下代码进行检测,其中的地址拼接是重点
1 | "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s" , username, password, host, port, Dbname, timeout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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(根据需求添加相关配置)
例如数据表命名的策略如下所示
1 2 3 4 5 6 7 | 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{})进行插入数据
1 2 3 4 5 6 7 8 9 | DB.AutoMigrate(&students{}) email := "123456@qq.com" s := students{ Name: "xx" , Age: 22, Gender: false, Email: &email, } DB.Create(&s) |
2.批量插入
1 2 3 4 5 6 7 8 9 | 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.单表查询
1 2 3 4 5 6 7 8 9 10 11 | 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.根据主键查询
1 2 3 | //根据主键查询,查询主键为3的数据 DB.Take(&studentList, 3) //第二个参数是主键位置 fmt.Println(studentList) |
5.根据其他条件查询
1 2 3 4 5 6 7 8 9 | //根据其他条件查询,查询名字为Robot5号的信息 err := DB.Take(&studentList, "name=?" , "Robot5号" ).Error switch err { case gorm.ErrRecordNotFound: fmt.Println( "没有找到" ) default : fmt.Println( "sql错误" ) } fmt.Println(studentList) |
6.根据结构体查询
1 2 3 4 | var student students student.ID = 3 DB.Take(&student) fmt.Println(student) |
7.查询多条记录
1 2 3 4 5 6 | count := DB.Find(&studentList).RowsAffected //获取查询的记录数 fmt.Println(count) for _, student := range studentList { data, _ := json.Marshal(student) fmt.Println(string(data)) } |
8.根据主键查询多条记录
1 2 3 | //DB.Find(&studentList, []int{1, 3, 5, 7}) //DB.Find(&studentList, 1, 3, 5, 7) // 一样的 //fmt.Println(studentList) |
9.根据其它条件查询多条记录
1 2 | DB.Find(&studentList, "name in ?" , []string{ "Robot3号" , "xx" }) fmt.Println(studentList) |
三、更新表操作
1.更新所有字段,指定字段
1 2 3 4 5 6 7 | //更新所有字段 var student students email := "123456789@qq.com" DB.Take(&student) //获取第一个数据 student.Age = 18 //更改第一个数据的年龄 student.Email = &email student.XXX=xxx //使用select更新指定字段<br>DB.Select("age").Save(&student) //重新提交数据库DB.Save(&student) //重新提交数据库 |
2.批量更新
1 2 3 | //批量更新 var studentList []students DB.Find(&studentList, "age=?" , 22).Update( "email" , "is22@qq.com" ) |
3.更新多列
1 2 3 4 5 | var studentList []students DB.Model(&studentList).Where( "age=?" , 22).Updates( map [string]any{ "name" : "坨哥" , "gender" : false, }) |
4.根据ID删除
1 2 3 | //根据ID删除行数据 var student students DB.Delete(&student, 4) |
四、在重新创建新的go文件时遇到undefined DB 问题解决方案
问题原因:在一个go文件中创建DB连接数据库后,其它的go文件无法直接调用。
解决方案:因为所有的go文件都创建在一个目录中,导致出现以上问题。因此,将连接文件单独放在一个目录中,在其它文件中去调用连接数据的文件
方法如下:
此时,在其它文件中调用DB就是已经连接好的DB状态了,调用形式如下
Day3:GORM之高级查询
一.Where语句的使用
1 2 3 | //查询用户名是xx的指令 DB.Where( "name=?" , "李元芳" ).Find(&student) fmt.Println(student) //查询用户名不是XX的指令<br>DB.Where("name<>?", "李元芳").Find(&student)<br>for _, s := range student {<br> fmt.Println(s)<br>}// 查询用户名包含 如燕,李元芳的<br>DB.Where("name in ?", []string{"李元芳", "枫枫"}).Find(&student)<br>fmt.Println(student)//模糊查询<br>DB.Where("name like ?", "李%").Find(&student)<br>fmt.Println(student)//根据多个条件查询<br>DB.Where("name like ? and email like ?", "李%", "%@lly.cn").Find(&student)<br>fmt.Println(student)//根据判断条件查询<br>DB.Where("age > ? and gender = ?", "20", "1").Find(&student)<br>fmt.Println(student)<br><br>//select 选择字段,没有选择的字段打印时都会赋零值,但是不改变表的实际内容<br>DB.Select("name", "age").Find(&student)<br>fmt.Println(student)<br><br>//使用scan,将select选择的字段存入另一个结构体中<br>var list []students<br>DB.Select("name", "age").Find(&student).Scan(&list)<br>fmt.Println(list) |
二.排序
排序分为升序和降序
1 2 3 4 5 6 | //降序排序 DB.Order( "age desc" ).Find(&student) fmt.Println(student) //升序排序 DB.Order( "age asc" ).Find(&student) fmt.Println(student) |
三.分页查询
1 2 3 4 5 6 7 8 | //分页查询 // 一页多少条数据 limit := 2 // 第几页 page := 1 offset := (page - 1) * limit DB.Limit(limit).Offset(offset).Find(&student) fmt.Println(student) |
四.去重
1 2 3 4 | //去重.DB.table("数据表名称") var ageList []int DB.Table( "f_students" ).Select( "distinct age" ).Find(&ageList) fmt.Println(ageList) |
五、分组查询
1 2 3 4 5 6 7 8 9 | //以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 |
六、子查询
1 2 3 4 5 | //子查询,嵌套查询 var student []students //(?) 由括号包裹的问号表示函数 DB.Table( "f_students" ).Where( "age>(?)" , DB.Table( "f_students" ).Select( "avg(age)" )).Find(&student) fmt.Println(student) //命名参数<br>DB.Where("name=@name and age = @age",<br> map[string]any{<br> "name": "枫枫",<br> "age": 23,<br> }).Find(&student)<br>fmt.Println(student)<br><br>//使用函数进行查询<br>var student []students<br>DB.Scopes(LikeLi).Find(&student)<br>fmt.Println(student)//创建查询函数,方便调用<br>func LikeLi(DB *gorm.DB) *gorm.DB {<br> return DB.Where("name like ?", "李%")<br>} |
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()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律