数据库连接时的一些配置
gorm
连接数据库时,可以配置一些参数,如下:
go
复制代码
var dsn = "root:123456@tcp(go-uccs-1:3306)/zero_study?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ SkipDefaultTransaction: true, // 跳过默认事务 NamingStrategy: schema.NamingStrategy{ TablePrefix: "f_", // 表名前缀 SingularTable: true, // 表名单数 NoLowerCase: true, // 表名不转小写 }, })
log 配置
gorm
默认只打印错误和慢 SQL
,默认是不展示 log
,配置 log
有三种方式
全局配置
go
复制代码
var log = logger.Default.LogMode(logger.Info) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: log, })
局部配置
go
复制代码
db = db.Session(&gorm.Session{ Logger: log, })
db
对象配置
go
复制代码
db.Debug().AutoMigrate(&Student{})
gorm json tag
type
: 定义字段类型size
: 定义字段大小column
: 自定义列名primaryKey
: 将列定义为主键autoIncrement
: 将列定义为自增foreignKey
: 定义外键关联unique
: 将列定义为唯一键default
: 定义列的默认值not null
: 定义列为非空comment
: 定义列的注释embedded
: 嵌入结构体embeddedPrefix
: 嵌入结构体的前缀many2many
: 定义多对多关系joinForeignKey
: 外键关联,修改主键名joinReferences
: 外键关联,修改引用名
Create
指针不传值,默认是 null
,其他类型,不传值,默认是零值
go
复制代码
db.Create(&Student{Name: "zero", Age: 18})
对应的 sql
语句
sql
复制代码
INSERT INTO `student` (`name`,`age`) VALUES ('uccs',123)
批量
go
复制代码
students := []Student{{Name: "zero", Age: 18}, {Name: "uccs", Age: 183}} DB.Create(&students)
对应的 sql
语句
sql
复制代码
INSERT INTO `student` (`name`,`age`) VALUES ('zero',18),('uccs',183)
Take
查询数据库中第一条数据
go
复制代码
student := Student{} DB.Take(&student)
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` LIMIT 1
第二个参数
Take
第二个参数是主键查询
go
复制代码
DB.Debug().Take(&student, 2)
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` WHERE `student`.`id` = '2' LIMIT 1
Find
查询所有数据,如果结构体不是 slice
类型,会获取第一条数据,如果是 slice
类型,会获取所有数据
go
复制代码
student := Student{} DB.Find(&student) // 获取第一条数据 students := []Student{} DB.Find(&students) // 获取所有数据
对应的 sql
语句
sql
复制代码
SELECT * FROM `student`
Update 和 Updates
go
复制代码
student := []Student{} DB.Take(&student, 1).Update("name", "uccs") // 更新一条记录的一个属性 DB.Debug().Take(&student, 1).Updates(&Student{Name: "astak", Age: 16}) // 更新一条记录的多个属性
对应的 sql
语句
sql
复制代码
UPDATE `student` SET `name`='uccs' WHERE `student`.`id` = 1 AND `id` = 1 LIMIT 1
Delete
go
复制代码
student := Student{} DB.Debug().Delete(&student, 1)
对应的 sql
语句
sql
复制代码
DELETE FROM `student` WHERE `student`.`id` = 1
Where
go
复制代码
student := &Student{} DB.Debug().Where("name = ?", "zero").Find(&student) // 等同于 DB.Debug().Find(&student, "name=?", "uccs")
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` WHERE name = 'zero'
Select
Select
选择需要查询的字段
go
复制代码
student := &Student{} DB.Debug().Select("name").Find(&student)
对应的 sql
语句
sql
复制代码
SELECT `name` FROM `student`
Scan
Scan
是根据 json tag
来选择字段
go
复制代码
s := []struct { Name string `gorm:"column:name"` }{} student := &Student{} DB.Debug().Model(student).Select("name").Scan(&s)
对应的 sql
语句
sql
复制代码
SELECT `name` FROM `student`
Order
go
复制代码
student := &[]Student{}
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` ORDER BY age desc
Limit 和 Offset
go
复制代码
student := &[]Student{} DB.Debug().Limit(2).Offset(2).Find(student)
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` LIMIT 2 OFFSET 2
Distinct
go
复制代码
ageList := []int{} DB.Debug().Model(&Student{}).Select("age").Distinct("age").Scan(&ageList) // 效果是一样的 DB.Debug().Model(student).Select("distinct age").Scan(&ageList)
对应的 sql
语句
sql
复制代码
SELECT DISTINCT `age` FROM `student`
Group
go
复制代码
g := []struct { Count int Name string }{} DB.Debug().Model(&Student{}).Select("count(name) as count, name").Group("name").Scan(&g)
对应的 sql
语句
sql
复制代码
SELECT count(name) as count, name FROM `student` GROUP BY `name`
go
复制代码
g := []struct { Count int Name string NameList string }{} DB.Debug().Model(&Student{}).Select("group_concat(name) as name_list, count(name) as count, name").Group("name").Scan(&g)
对应的 sql
语句
sql
复制代码
SELECT group_concat(name) as name_list, count(name) as count, name FROM `student` GROUP BY `name`
Raw
Raw
执行原生 sql
go
复制代码
s := []struct { Id int Name string Age int }{} DB.Raw("SELECT * FROM student WHERE id = ?", 6).Scan(&s)
Scopes
go
复制代码
s := []Student{} DB.Debug().Model(&Student{}).Scopes(GreaterThan).Find(&s)
子查询
go
复制代码
s := []Student{} DB.Debug().Model(&Student{}).Where("age > (?)", DB.Model(&Student{}).Select("avg(age) as age")).Find(&s)
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` WHERE age > (SELECT avg(age) as age FROM `student`)
具名参数
go
复制代码
DB.Raw("SELECT * FROM student_table WHERE id = @id", sql.Named("id", 6)).Scan(&s) DB.Raw("SELECT * FROM student_table WHERE id = @id", map[string]any{"id": 6}).Scan(&s)
一对多
Class.Id
和 Student.ClassId
是外键关联,他们的 json tag
必须一致
外键关联:表名 + Id
go
复制代码
type Class struct { Id int `gorm:"primaryKey;autoIncrement;"` Name string Students []Student } type Student struct { Id int `gorm:"primaryKey;autoIncrement"` Name string Age int ClassId int Class Class } db.AutoMigrate(&Student{}, &Class{})
重写外键关联
两边都要加上 foreignKey:ClassId1
的 json tag
go
复制代码
type Class struct { Id int `gorm:"primaryKey;autoIncrement;"` Name string Students []Student } type Student struct { Id int `gorm:"primaryKey;autoIncrement"` Name string Age int ClassId int Class Class } db.AutoMigrate(&Student{}, &Class{})
创建 class 带上 student
go
复制代码
DB.Debug().Create(&Class{ Name: "一年级1班", Students: []Student{ {Name: "uccs", Age: 8}, {Name: "astak", Age: 7}, }, })
对应的 sql
语句
sql
复制代码
INSERT INTO `student` (`name`,`age`,`class_id`) VALUES ('uccs',8,1),('astak',7,1) ON DUPLICATE KEY UPDATE `class_id`=VALUES(`class_id`) INSERT INTO `class` (`name`) VALUES ('一年级1班')
创建 student 关联 class
classId
必须存在go
复制代码
DB.Debug().Create(&Student{ Name: "李四", Age: 10, ClassId: 1, // 如果 classId 不存在会报错 })
对应的sql
语句sql
复制代码
INSERT INTO `student` (`name`,`age`,`class_id`) VALUES ('李四',10,1)
- 创建
student
时同时创建class
go
复制代码
DB.Debug().Create(&Student{ Name: "李四", Age: 10, Class: Class{ Name: "一年级2班", }, })
对应的sql
语句sql
复制代码
INSERT INTO `class` (`name`) VALUES ('一年级2班') ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `student` (`name`,`age`,`class_id`) VALUES ('李四',10,2)
- 先查询已存在的数据
go
复制代码
c := Class{} DB.Debug().Take(&c, 1) DB.Debug().Create(&Student{ Name: "小红", Age: 10, Class: c, })
对应的sql
语句sql
复制代码
SELECT * FROM `class` WHERE `class`.`id` = 1 LIMIT 1 INSERT INTO `class` (`name`,`id`) VALUES ('一年级1班',1) ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `student` (`name`,`age`,`class_id`) VALUES ('小红',10,1)
给 class 添加 student
go
复制代码
c := Class{} DB.Debug().Take(&c, 1) // 查询 Class s := Student{} DB.Debug().Take(&s, 8) // 查询 Student // 给 class 的 Students 添加 student DB.Debug().Model(&c).Association("Students").Append(&s)
对应的 sql
语句
sql
复制代码
SELECT * FROM `class` WHERE `class`.`id` = 1 LIMIT 1 SELECT * FROM `student` WHERE `student`.`id` = 8 LIMIT 1 INSERT INTO `student` (`name`,`age`,`class_id`,`id`) VALUES ('王五',3,1,8) ON DUPLICATE KEY UPDATE `class_id`=VALUES(`class_id`)
给 student 添加 class
go
复制代码
c := Class{} DB.Debug().Take(&c, 1) s := Student{} DB.Debug().Take(&s, 8) DB.Debug().Model(&s).Association("Class").Append(&c)
对应的 sql
语句
sql
复制代码
SELECT * FROM `class` WHERE `class`.`id` = 1 LIMIT 1 SELECT * FROM `student` WHERE `student`.`id` = 8 LIMIT 1 INSERT INTO `class` (`name`,`id`) VALUES ('一年级1班',1) ON DUPLICATE KEY UPDATE `id`=`id` UPDATE `student` SET `class_id`=1 WHERE `id` = 8
预加载
go
复制代码
s := Student{} DB.Debug().Preload("Class").Take(&s)
对应的 sql
语句
sql
复制代码
SELECT * FROM `class` WHERE `class`.`id` = 1 SELECT * FROM `student` LIMIT 1
带条件的预加载
go
复制代码
s := Student{} DB.Debug().Preload("Class", "id = ?", 2).Take(&s)
对应的 sql
语句
sql
复制代码
SELECT * FROM `class` WHERE `class`.`id` = 1 AND id = 2 SELECT * FROM `student` LIMIT 1
自定义预加载
go
复制代码
s := Student{} DB.Debug().Preload("Class", func(db *gorm.DB) *gorm.DB { return db.Where("id = ?", 2) }).Take(&s)
对应的 sql
语句
sql
复制代码
SELECT * FROM `class` WHERE `class`.`id` = 1 AND id = 2 SELECT * FROM `student` LIMIT 1
删除
go
复制代码
c := Class{} DB.Debug().Preload("Students").Take(&c, 1) DB.Debug().Model(&c).Association("Students").Delete(&c.Students)
对应的 sql
语句
sql
复制代码
SELECT * FROM `student` WHERE `student`.`class_id` = 1 SELECT * FROM `class` WHERE `class`.`id` = 1 LIMIT 1 UPDATE `student` SET `class_id`=NULL WHERE `student`.`class_id` = 1 AND `student`.`id` IN (1,2,3,4,7,8)
一对一
go
复制代码
type User struct { ID uint Name string Age int Gender bool UserInfo UserInfo } type UserInfo struct { UserID uint User *User ID uint Addr string Like string } db.AutoMigrate(&UserInfo{}, &User{})
多对多
go
复制代码
type Tag struct { ID uint Name string Articles []Article `gorm:"many2many:article_tags"` } type Article struct { ID uint Title string Tags []Tag `gorm:"many2many:article_tags"` } db.AutoMigrate(&Article{}, &Tag{})
更新(先删除再添加)
go
复制代码
// 先删除 a := Article{} DB.Debug().Preload("Tags").Take(&a, 2) DB.Model(&a).Association("Tags").Delete(a.Tags) // 再更新 t := Tag{} DB.Debug().Take(&t, 1) DB.Debug().Model(&a).Association("Tags").Append(&t)
对应的 sql
语句
sql
复制代码
-- 先删除 SELECT * FROM `article_tags` WHERE `article_tags`.`article_id` = 2 SELECT * FROM `tag` WHERE `tag`.`id` IN (3,4) SELECT * FROM `article` WHERE `article`.`id` = 2 LIMIT 1 DELETE FROM `article_tags` WHERE `article_tags`.`article_id` = 2 AND `article_tags`.`tag_id` = 1 -- 再更新 SELECT * FROM `tag` WHERE `tag`.`id` = 1 LIMIT 1 INSERT INTO `tag` (`name`,`id`) VALUES ('python',1) ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `article_tags` (`article_id`,`tag_id`) VALUES (2,1) ON DUPLICATE KEY UPDATE `article_id`=`article_id`
更新(替换)
go
复制代码
a := Article{} DB.Debug().Preload("Tags").Take(&a, 2) t := Tag{} DB.Debug().Take(&t, 6) DB.Debug().Model(&a).Association("Tags").Replace(&t)
对应的 sql
语句
sql
复制代码
SELECT * FROM `article_tags` WHERE `article_tags`.`article_id` = 2 SELECT * FROM `tag` WHERE `tag`.`id` = 1 SELECT * FROM `article` WHERE `article`.`id` = 2 LIMIT 1 SELECT * FROM `tag` WHERE `tag`.`id` = 6 LIMIT 1 INSERT INTO `tag` (`name`,`id`) VALUES ('后端',6) ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `article_tags` (`article_id`,`tag_id`) VALUES (2,6) ON DUPLICATE KEY UPDATE `article_id`=`article_id` DELETE FROM `article_tags` WHERE `article_tags`.`article_id` = 2 AND `article_tags`.`tag_id` <> 6
自定义连接表
go
复制代码
type Tag struct { ID uint Name string Articles []Article `gorm:"many2many:article_tags"` // 反向引用 } type Article struct { ID uint Title string Tags []Tag `gorm:"many2many:article_tags"` } type ArticleTag struct { ArticleID uint `gorm:"primaryKey"` TagID uint `gorm:"primaryKey"` CreatedAt time.Time `json:"created_at"` }
这里存在反向引用,所以第三张表中 create_at
没法创建
go
复制代码
DB.Debug().SetupJoinTable(&Article{}, "Tags", &ArticleTag{}) DB.AutoMigrate(&Article{}, &Tag{}, &ArticleTag{})
所以有两种方法:
- 删除反向引用
go
复制代码
type Tag struct { ID uint Name string // Articles []Article `gorm:"many2many:article_tags"` // 反向引用 }
Tag
表也进行自定义连接go
复制代码
DB.Debug().SetupJoinTable(&Tag{}, "Articles", &ArticleTags{})
添加文章并添加标签,自动关联
ArticleTag
表中 CreatedAt
字段在数据创建时,会自动填充上时间,如果是其他字段,需要用 hook
函数 BeforeCreate
来填充
go
复制代码
DB.Debug().Create(&Article{ Title: "flask基础", Tags: []Tag{{Name: "python"}, {Name: "flask"}}, })
对应的 sql
语句
sql
复制代码
INSERT INTO `tag` (`name`) VALUES ('python'),('flask') ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `article_tag` (`article_id`,`tag_id`,`created_at`) VALUES (1,1,'2024-05-29 20:11:20.973'),(1,2,'2024-05-29 20:11:20.973') ON DUPLICATE KEY UPDATE `article_id`=`article_id` INSERT INTO `article` (`title`) VALUES ('flask基础')
添加文章,关联已有标签
go
复制代码
t := []Tag{} DB.Debug().Find(&t, "name in ?", []string{"python"}) DB.Debug().Create(&Article{ Title: "Diango基础", Tags: t, })
对应的 sql
语句
sql
复制代码
SELECT * FROM `tag` WHERE name in ('python') INSERT INTO `tag` (`name`,`id`) VALUES ('python',1) ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `article_tag` (`article_id`,`tag_id`,`created_at`) VALUES (2,1,'2024-05-29 20:19:29.048') ON DUPLICATE KEY UPDATE `article_id`=`article_id` INSERT INTO `article` (`title`) VALUES ('Diango基础')
给已有的文章关联标签
go
复制代码
t := []Tag{} DB.Debug().Find(&t, "name in ?", []string{"golang"}) a := Article{} DB.Debug().Take(&a, "title = ?", "golang基础") DB.Debug().Model(&a).Association("Tags").Append(t)
对应的 sql
语句
sql
复制代码
SELECT * FROM `tag` WHERE name in ('golang') SELECT * FROM `article` WHERE title = 'golang基础' LIMIT 1 INSERT INTO `tag` (`name`,`id`) VALUES ('golang',3) ON DUPLICATE KEY UPDATE `id`=`id` INSERT INTO `article_tag` (`article_id`,`tag_id`,`created_at`) VALUES (3,3,'2024-05-29 20:24:25.459') ON DUPLICATE KEY UPDATE `article_id`=`article_id`
存储 json 数据
go
复制代码
type Auth struct { ID uint Info Info `gorm:"type:string"` // 不指定 type 默认是 blob } type Info struct { Status string `json:"status"` Addr string `json:"addr"` Age uint `json:"age"` } func (i *Info) Scan(value interface{}) error { bytes, ok := value.([]byte) if !ok { return errors.New(fmt.Sprintf("failed to unmarshal json value: ", value)) } return json.Unmarshal(bytes, i) } func (i Info) Value() (driver.Value, error) { return json.Marshal(i) } DB.Debug().Create(&Auth{ Info: Info{ Status: "success", Addr: "上海", Age: 18, }, }) auth := Auth{} DB.Take(&auth, 1)
对应的 sql
语句
sql
复制代码
INSERT INTO `auth` (`info`) VALUES ('{"status":"success","addr":"上海","age":18}') SELECT * FROM `auth` WHERE `auth`.`id` = 1 LIMIT 1
枚举
go
复制代码
func (s Status) MarshalJSON() ([]byte, error) { return json.Marshal(string(s)) } func (s Status) String() string { var str string switch s { case Running: str = "Running" case Offline: str = "Offline" case Except: str = "Except" } return str } type Host struct { ID uint IP string Status Status `gorm:"size:8"` } DB.Debug().Create(&Host{ IP: "192.168.1.1", Status: Running, }) h := Host{} DB.Take(&h) d, _ := json.Marshal(&h) fmt.Println(string(d))
hook
Create
,CreateInBatches
,Save
是会运行
go
复制代码
func (u *Student) BeforeCreate(tx *gorm.DB) error { u.Age = 18 return nil }
错误处理
go
复制代码
err := DB.Task(&Student{}).Error // 错误类型判断 err == Gorm.ErrRecordNotFound