Gorm 操作mysql
gorm操作mysql
安装:
go get -u gorm.io/gorm // gorm
要连接数据库首先要导入驱动程序:
import _ "github.com/go-sql-driver/mysql"
为了方便,grom包装一些驱动:
"gorm.io/driver/mysql" // mysql,可以不使用上面那个了
"gorm.io/driver/postgres" // postgres
"gorm.io/driver/sqlite" // sqlite
go get -u gorm.io/driver/mysql
简单示例:
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type UserInfo struct {
ID int // 默认使用作为主键
Name string
Gender int
Address string
}
func main() {
dsn := "root:123@(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=true"
d := mysql.Open(dsn)
db, err := gorm.Open(d)
if err != nil {
panic(err)
}
// 自动迁移
db.AutoMigrate(&UserInfo{})
// 插入数据
db.Create(&UserInfo{
1, "zs", 0, "beijing",
})
var u UserInfo
// 查询第一条数据
db.First(&u)
fmt.Printf("u: %v\n", u)
// 更新数据
db.Model(&u).Update("address", "上海")
// 删除
db.Delete(&u)
}
主键、表名、列名约定
默认情况下,GORM 使用 ID
作为主键。
type User struct{
ID string // 默认ID作为主键
Name string
}
type Animal struct{
AnimalID int64 `gorm:"PRIMARY_KEY"` // 使用AnimalID作为主键
Name string
}
使用结构体名的 蛇形复数
作为表名,例如:对于结构体 User
,根据约定,其表名为 users
。
type Animal struct {
ID int64
AnimalID int64 `gorm:"PRIMARY_KEY"`
Name string
}
func (Animal) TableName() string { // 修改表名为new_table_name_animal
return "new_table_name_animal"
}
func (u User) TabelName() string { // 设置表名
if u.Role == "admin" {
return "admin_users"
} else {
return "users"
}
}
字段名的 蛇形
作为列名,并使用 CreatedAt
、UpdatedAt
、Deleted_At
字段追踪创建、更新时间。
type User struct{
ID int64
Name string
Age int64 `gorm:"column:age123"` // 指定字段名为:age123
MemberNumber string // 创建的字段名为:member_number
}
grom.Model结构体
可以将它嵌入到您的结构体中,以包含这几个字段。
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
// 示例2:继承gorm.Model的例子
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct{ // 不使用gorm.Model定义模型
ID int
Name string
}
type Product struct { // 使用gorm.Model定义模型,嵌入到我们结构体中。包含ID, Created_At, Updated_At, Deleted_At。
gorm.Model
Code string
Price uint
Status int8
}
func main() {
// 想要正确的处理 time.Time ,您需要带上 parseTime 参数
dsn := "root:123@tcp(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=True&loc=Local"
d := mysql.Open(dsn)
db, err := gorm.Open(d, &gorm.Config{})
if err != nil {
fmt.Printf("%v\n", err.Error())
return
}
// 迁移schema,执行建表之类操作
db.AutoMigrate(&Product{})
// 1. insert新记录
db.Create(&Product{
Code: "200",
Price: 101,
Status: 0,
})
// 2. 查询记录
var product Product
db.First(&product) // 默认查找条件,查找id第一个。
db.First(&product, 1) // id为1记录存在时,同上,查找id为1的记录。
fmt.Printf("product: %+v\n", product)
// 根据条件查询记录
db.Find(&product, "code=?", "100") // 如果有多条记录,查询第一条记录
fmt.Printf("product: %+v\n", product)
// 3. 更新记录
db.Model(&product).Update("status", 1)
fmt.Printf("product: %+v\n", product)
db.Model(&product).Updates(Product{Price: 200, Code: "code200"}) // 更新多个字段
db.Model(&product).Updates(map[string]interface{}{"Price": 300, "Code": "Code300"}) // 同上
// // 4. 删除操作
db.Delete(&product, 2) // 删除id=2的记录,只是更新deleted_at字段(逻辑删除)
fmt.Printf("product: %+v\n", product)
}
结构体字段标签
tag名对大小写不敏感,推荐使用驼峰命名
结构体标记(tag) | 说明 |
---|---|
column | 指定列名 |
type | 指定列数据类型 |
size | 指定列长度,默认255 |
primaryKey | 设置为主键 |
UNIQUE | 指定列唯一 |
DEFAULT | 指定列默认值 |
PRECISION | 指定列精度 |
NOT NULL | 列非空 |
autoIncrement | 自增 |
INDEX | 创建索引 |
UNIQUE_INDEX | 创建唯一索引 |
EMBEDDED | 将结构体设置为嵌入 |
EMBEDDED_PREFIX | 设置嵌入结构的前缀 |
- |
忽略这个字段,不会在数据库中创建 |
foreignKey | 指定外键,以【自己表】的哪个key去关联 |
references | 指定引用表的列名,被关联结构的那个Key |
示例:
package main
import (
"database/sql"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Age sql.NullInt64 // 可以为null的int64类型, 因为sql的null不能转为int
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"` // 类型varchar, 唯一索引
Role string `gorm:"size:255;comment:'角色'"` // 设置字段长度为255, 添加注释
MemberNumber *string `gorm:"unique;not null"` // 设置字段唯一,不能为空
Num int `gorm:"AUTO_INCREMENT"` // 设置字段自增
Address string `gorm:"index:addr"` // 添加字段索引,索引名addr
IgnoreMe int `gorm:"-"` // 忽略此字段,不会创建在数据库中
}
type Animal struct {
ID int64
AnimalID int64 `gorm:"PRIMARY_KEY"`
Name string
}
func main() {
dsn := "root:123@(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=true"
d := mysql.Open(dsn)
db, err := gorm.Open(d, &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&User{})
db.AutoMigrate(&Animal{})
}
插入操作
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 1. 定义模型
type User struct {
ID int64
Name string
Age int64
}
func main() {
dsn := "root:123@(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=true"
d := mysql.Open(dsn)
db, err := gorm.Open(d, &gorm.Config{})
if err != nil {
panic(err)
}
// 2. 模型与数据库中表对应
db.AutoMigrate(&User{})
// 3. 创建记录
var u = User{Name: "zs", Age: 19}
db.Create(&u) // 传&u。 否则报panic
}
// 示例2. 设置默认值
type User struct {
ID int64
Name string `gorm:"default:李四"`
Age int64
}
func main(){
var u2 = User{Age: 21}
db.Create(&u2)
}
// 示例3: 设置default的tag之后,零值不会插入到数据库。
type User struct {
ID int64
Name string `gorm:"default:李四"`
Age int64
}
var u2 = User{Age: 21, Name: ""} // 同User{Age: 21},数据库会插入【李四】,而不是被空字符覆盖。
db.Debug().Create(&u2) // 在语句之前调用Debug()打印sql语句。
// 示例4:使用指定字段插入数据
type User struct {
ID int64
Name *string `gorm:"default:李四"`
Age *int64 `gorm:"default:18"`
Address string
}
name := "zs"
var age int64 = 100
u := User{Name: &name, Age: &age, Address: "bj"}
db.Debug().Select("Name", "Address").Create(&u) // INSERT INTO `users` (`name`,`address`) VALUES ('zs','bj')
/** 数据库中:
3 | zs | 18 | bj |
**/
// 示例5: 批量插入
type User3 struct {
ID int64
Name string
Age int64
}
var users = []User3{User3{Name: "zs"}, User3{Name: "ls"}, User3{Name: "ww"}}
db.Debug().Create(&users) // 一条sql插入
var users = []User3{User3{Name: "zs_new"}, User3{Name: "ls_new"}, User3{Name: "ww_new"}, User3{Name: "zl"}, User3{Name: "heihei"}}
db.Debug().CreateInBatches(&users, 2) // 多条sql,一条sql插入2条记录
// 示例6: 使用map插入
user := map[string]interface{}{
"Name": "gaga", "Age": 19,
}
db.Model(&User3{}).Create(&user)
// map批量插入
users := []map[string]interface{}{
{"Name": "gaga_new1", "Age": 29},
{"Name": "gaga_new2", "Age": 18},
}
db.Model(&User3{}).Create(&users)
注意:
-
对于声明了默认值的字段,在插入新记录的时候,会忽略零值(0,"",false, nil等)的字段。需要使用指针类型或 实现Scanner/Valuer接口 来避免这个问题。
// 方法一: 声明指针类 type User struct{ ID int64 Name *string `gorm:"default:李四"` // 为*string类型 Age *int64 `gorm:"default:18"` } func main(){ var u User = User{Name:new(string), Age:new(int64)} // 插入零值 db.Create(&u) } /** | id | name | age | | 1 | | 0 | **/ // 方法二:使用实现了Scanner/Valuer接口的结构体 type User2 struct { ID int64 Name sql.NullString `gorm:"default:李四"` Age sql.NullInt64 `gorm:"default:18"` } func main(){ var u2 = User2{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{0, true}} db.Create(&u2) } // 如果不想再数据库中设置默认值 type User2 struct { ID int64 Name sql.NullString `gorm:"default:李四"` Age sql.NullInt64 `gorm:"default:18"` }
-
如果在迁移时候,跳过默认值定义,可以在tag中添加
default:(-)
查询操作
一般查询
var user User
// 1. 查询id排序的第一条记录
db.First(&user) // select * from users order by id limit 1;
fmt.Printf("user: %+v\n", user)
// 2. 查询第一条记录
db.Take(&user) // select * from users limit 1;
fmt.Printf("user: %+v\n", user)
// 3. 查询id逆序第一条记录
db.Last(&user)
fmt.Printf("user: %+v\n", user) // select * from users order by id desc limit 1;
result := db.First(&user)
fmt.Printf("result.RowsAffected: %v\n", result.RowsAffected) // 找到的记录数
fmt.Printf("result.Error: %v\n", result.Error) // return errors or nil
}
// 4. 查询所有记录
var users = make([]User, 0, 10)
db.Find(&users) // select * from users;
fmt.Printf("users: %v\n", users)
// 5. 查询制定id的记录
var u User
db.First(&u, 3) // select * from users where id = 3;
fmt.Printf("u: %v\n", u)
条件查询
// 1. 查询指定条件的第一条记录
var user User
db.Where("name=?", "zs").First(&user) // select * from users where name="zs" order by id limit 1;
fmt.Printf("user: %+v\n", user)
// 2. 查询制定条件的所有记录
var users = make([]User, 0, 10)
db.Where("name=?", "zs").Find(&users) // select * from users where name="zs"
fmt.Printf("users: %+v\n", users)
// 3. <> 不等于
users = make([]User, 0, 10)
db.Where("name<>?", "zs").Find(&users) // select * from users where name<>'zs'
fmt.Printf("users: %+v\n", users)
// 4. in
users = make([]User, 0, 10)
db.Where("name in (?)", []string{"zs", "ls"}).Find(&users) // select * from users where name in ("zs", "ls");
fmt.Printf("users: %+v\n", users)
// 5. like语句
users = make([]User, 0, 10)
db.Where("name like ?", "%zs%").Find(&users) // select * from users where name like %zs%;
fmt.Printf("users: %+v\n", users)
// 6. and语句
users = make([]User, 0, 10)
db.Where("name=? and age=?", "zs", 18).Find(&users) // select * from users where name=zs and age = 18
fmt.Printf("users: %+v\n", users)
// 7. >语句
lastWeek := time.Date(2022, 9, 13, 1, 1, 1, 1, time.Local).Format("2006-01-02 15:04:05")
fmt.Printf("lastWeek: %v\n", lastWeek) // 2022-09-13 01:01:01
users = make([]User, 0, 10)
db.Where("updated_at > ?", lastWeek).Find(&users) // select * from users where updated_at > "2022-09-13 01:01:01"
fmt.Printf("users: %+v\n", users)
// 8. between 语句
users = make([]User, 0, 10)
db.Where("age between ? and ?", 10, 20).Find(&users) // select * from users where age between 10 and 20;
fmt.Printf("users: %+v\n", users)
struct和map查询
// 1. 结构体作为查询条件
var user User
db.Where(&User{Name: "zs", Age: 18}).First(&user) // select * from users where name = zs and age = 18
fmt.Printf("user: %+v\n", user)
// 2. map作为查询条件
var users = make([]User, 0, 10)
db.Where(map[string]interface{}{"Name": "zs", "Age": 18}).Find(&users) // select * from users where name = zs and age = 18
fmt.Printf("users: %v\n", users)
// 3. 主键id作为查询条件
users = make([]User, 0)
db.Where([]int64{1, 3, 5}).Find(&users) // select * from users where id in (1, 3, 5)
fmt.Printf("users: %v\n", users)
注意:
-
在使用结构体查询的时候,GORM只会通过非零字段,一些字段的零值不会用于到查询条件中。 map不受影响。
var user User db.Debug().Where(&User{Name: "zs", Age: 0}).First(&user) // select * from users where name = zs, 零值的Age不会用于查询条件 fmt.Printf("user: %+v\n", user) // 解决办法: 使用指针 或者 实现Scanner/Valuer接口。 // 法一:使用指针 type User struct{ Name string Age *int64 } var age int64 = 0 db.Where(&User{Name: "zs", Age: &age}).First(&user) // select * from users where name = zs and age = 0 // 法二:使用scanner和Valuer接口的类型 type User struct{ Age sql.NullInt64 } db.Where(&User{Name: "zs", Age: sql.NullInt64{0, true}}).First(&user) // select * from users where name = zs and age = 0
Not条件
// 1. 基本使用
var users = make([]User, 0)
db.Not("name=?", "zs").Find(&users) // select * from users where not name = "zs"
fmt.Printf("users: %v\n", users)
// 2. struct。 在not中使用and
db.Not(User{Name: "zs", Age: 18}).Find(&users) // select * from users where name <> zs and age <> 18;
fmt.Printf("users: %v\n", users)
// 3. map。在not中使用and
db.Not(map[string]interface{}{"Name": "zs", "Age": 18}).Find(&users) // select * from users where name <> zs and age <> 18;
fmt.Printf("users: %v\n", users)
Or条件
// 1. 基本使用
var users = make([]User, 0)
db.Where("name=?", "zs").Or("age=?", 18).Find(&users) // SELECT * FROM `users` WHERE (name='zs' OR age=18)
fmt.Printf("users: %v\n", users)
// 2. struct。 在or中使用and
db.Where("name=?", "zs").Or(User{Name: "zs-new", Age: 18}).Find(&users) // SELECT * FROM users WHERE (name='zs' OR (name = 'zs-new' AND age = 18))
fmt.Printf("users: %v\n", users)
// 3. map
users = make([]User, 0)
db.Where("name=?", "zs").Or(map[string]interface{}{"Name": "zs-new", "Age": 18}).Find(&users) // SELECT * FROM users WHERE (name='zs' OR (name = 'zs-new' AND age = 18))
fmt.Printf("users: %v\n", users)
指定字段
// 1. 只查询指定的字段
users = make([]User, 0)
db.Select("name", "age").Find(&users) // select name,age from users。
fmt.Printf("users: %+v\n", users) // 注意: 此时name和age赋值给users的值,其余字段为零值。
// 2. 只查询指定的字段, 同上
db.Debug().Select([]string{"name", "age"}).Find(&users) // select name,age from users
fmt.Printf("users: %v\n", users)
Order
// 1. 基本使用
users = make([]User, 0)
db.Order("name desc, age").Find(&users) // select * from users order by name desc, age;
fmt.Printf("users: %v\n", users)
// 2. 同上
db.Order("name desc").Order("age").Find(&users) // select * from users order by name desc, age;
fmt.Printf("users: %v\n", users)
limit 和 offset
users = make([]User, 0)
db.Debug().Limit(1).Offset(2).Where("name=?", "zs").Find(&users) // select * from users where name = "zs" limit 1 offset 2;
fmt.Printf("users: %v\n", users)
// 示例2
var users = make([]User, 0)
db.Limit(5).Offset(-1).Find(&users) // Offset设置为-1或0,取消offset。 SELECT * FROM `users` LIMIT 5
fmt.Printf("users: %+v\n", users)
注意:使用count
不能用Offset
,或者是将Offset
值设为 -1
。
group和having
// 1. 定义Result的结构体
type Result struct {
Name string
TotalSum int64
}
func main(){
var result = make([]Result, 0, 10)
// 注意:1.需要指定表,db.Model() ; 2. Select中的字段名和结构体Result要对应。
db.Debug().Model(&User{}).Select("name, sum(age) as total_sum").Group("name").Find(&result)
fmt.Printf("result: %v\n", result)
}
Distinct
// 1. 返回结果赋值给User结构体
var users = make([]User, 0, 10)
db.Debug().Distinct("name", "age").Find(&users)
fmt.Printf("users: %v\n", users)
// 2. 返回结果赋值给新的结构体
type Result struct {
Name string
Age int64
}
func main(){
var results = make([]Result, 0, 10)
db.Debug().Model(&User{}).Distinct("name", "age").Find(&results) // 需要调用db.Model指定表名 ***
fmt.Printf("users: %v\n", users)
}
Count
会执行sql查询操作。类似Find()
var count int64
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count) // SELECT count(1) FROM users WHERE name = 'jinzhu';
db.Table("deleted_users").Count(&count) // SELECT count(1) FROM deleted_users;
db.Model(&User{}).Distinct("name").Count(&count) // SELECT COUNT(DISTINCT(`name`)) FROM `users`
db.Table("deleted_users").Select("count(distinct(name))").Count(&count) // SELECT count(distinct(name)) FROM deleted_users
// 注意:
db.Debug().Find(&users).Count(&count) // 会产生两条sql, 一条Find,一条Count
db.Model(&User{}).Count(&count) // 产生一条sql,查询count
db.Debug().Model(&User{}).Limit(2).Count(&count) // SELECT count(*) FROM `users` LIMIT 2。 查询出来的结果为count(*),再limit 2。
db.Debug().Model(&User{}).Limit(2).Offset(2).Count(&count) // SELECT count(*) FROM `users` LIMIT 2 OFFSET 2。查询出来的结果为count(*),就一行,在offset 2,就没有结果了。
Joins
type User struct {
ID uint
Name string
Age uint
CreditCards []CreditCard
}
type CreditCard struct {
ID uint
Number string
UserID uint
}
// 将结果保存到struct
type Result struct {
ID uint
Name string
Number string
}
func main(){
var results []Result
DB.Model(&User{}).Select("user.id,user.name,credit_cards.number").
Joins("join credit_cards on user.id = credit_cards.user_id").Find(&results)
DB.Table("user").Select("user.id,user.name,credit_cards.number"). // 同上
Joins("join credit_cards on user.id = credit_cards.user_id").Scan(&results)
}
更新操作
// 1. 更新所有字段
type User struct{
gorm.Model
Name string
Age int64
Active bool
}
func main(){
u := User{}
db.First(&u)
u.Name = "张三"
u.Age = 100
db.Save(&u) // 默认会将user的所有字段都进行update操作,如果新对象(没有插入)则进行insert操作。 UPDATE `users` SET `created_at`='2022-09-15 11:50:38.67',`updated_at`='2022-09-15 20:34:10.821',`deleted_at`=NULL,`name`='张三',`age`=50,`active`=true ;
}
// 2. 更新指定字段Update
db.Model(&u1).Update("name", "world") // 更新u1记录的name为world
db.Model(&u1).Where("active=?", true).Update("name", "world") // 更新u1记录【带条件】的name为world
// 3. 更新多个字段Updates。 使用struct和map
db.Model(&user).Updates(User{Name: "王五", Age: 18, Active: false}) // 注意当使用struct,【存在零值不会更新的问题】
db.Model(&user).Updates(map[string]interface{}{"name": "王五", "age": 18, "active": false}) // 使用map更新多个字段
// 4. 更新选定的字段。
db.Model(&user).Select("name", "age").Updates(User{Name: "王五1", Age: 118, Active: true}) // 传进来的是struct, 但只更新name和age
db.Model(&user).Select("name", "age").Updates(map[string]interface{}{"name": "王五2", "age": 128, "active": false}) // 传进来的是map, 但只更新name和age
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "王五3", "age": 10, "active": true}) // 【忽略name字段】。更新除了name字段之外的字段
// 5. 批量更新记录
db.Model(&User{}).Where("active=?", false).Updates(User{Name: "hello", Age: 11}) // 批量更新记录
db.Table("users").Where("active=?", false).Updates(User{Name: "world", Age: 18}) // 对表users批量更新记录
删除操作
// 1. 删除记录
u := User{}
u.ID = 1
db.Delete(&u) // 删除id为1的记录
u = User{}
u.Name = "zs"
db.Debug().Where("name=?", "zs").Delete(&u) // 批量删除
// 2. 根据主键删除
db.Delete(&User{}, 3) // 删除id=3记录
db.Delete(&User{}, "3") // 同上
db.Delete(&User{}, []int{1, 2, 3}) // 删除多个id的记录
// 3. 批量删除
db.Where("name = ?", "zs").Delete(&User{})
db.Delete(&User{}, "name=?", "ls")
// 4. 物理删除
db.Debug().Unscoped().Where("name is not null").Delete(&User{}) // 物理删除
注意:
- 删除记录时候,【确保主键字段(有值)】,gorm会通过主键删除记录,如果主键字段为空,会删除所有记录。
关联关系
一对一关系
// 第一种:belongs to
// Dog 属于 GirlGod
type Dog struct {
gorm.Model
Name string
GirlGodId uint // 在Dog表中添加GirlGodId外键
GirlGod GirlGod // 添加GirlGod
}
type GirlGod struct {
gorm.Model
Name string
}
func main() {
DB.AutoMigrate(&Dog{}, &GirlGod{})
// 1. 添加记录
girlGod := GirlGod{
Name: "女神1",
}
d := Dog{
Name: "狗1",
GirlGod: girlGod,
}
DB.Create(&d) // 对dog添加记录的同时,会自动创建GirlGod记录。
// 2. 查询
var dog Dog
DB.First(&dog, 1) // 此时在返回结果中,没有GirlGod记录
fmt.Printf("dog: %v\n", dog)
var dog2 Dog
DB.Preload("GirlGod").First(&dog2, 1) // 通过Preload()函数在加载Dog同时,加载GirlGod记录
fmt.Printf("dog2: %v\n", dog2)
}
// 第二种:Has one
type Dog struct {
gorm.Model
Name string
GirlGodId uint // 在Dog表中添加GirlGodId外键
}
type GirlGod struct {
gorm.Model
Name string
Dog Dog // 添加Dog变量
}
func main() {
DB.AutoMigrate(&GirlGod{}, &Dog{})
// 1. 添加记录
girlGod := GirlGod{
Name: "女神1",
}
d := Dog{
Name: "狗1",
}
girlGod.Dog = d // 不添加这句话,不会自动创建Dog记录,赋值之后,Create才自动创建Dog记录
DB.Create(&girlGod)
// 2. 查询
var girl GirlGod
DB.First(&girl, 2) // 此时在返回结果中,没有Dog记录
fmt.Printf("girl: %v\n", girl)
var girl2 GirlGod
DB.Preload("Dog").First(&girl2, 2) // 通过Preload()函数在加载GirlGod同时,加载Dog记录
fmt.Printf("girl2: %v\n", girl2)
}
一对多关系
type Dog struct {
gorm.Model
Name string
GirlGodId uint // 在Dog表中添加GirlGodId外键
}
type GirlGod struct {
gorm.Model
Name string
Dogs []Dog // 同一对多不同是,这里是切片
}
func main() {
DB.AutoMigrate(&GirlGod{}, &Dog{})
// 1. 添加记录
d1 := Dog{
Name: "dog1号",
}
d2 := Dog{
Name: "dog2号",
}
g := GirlGod{
Name: "女神1",
Dogs: []Dog{d1, d2},
}
DB.Create(&g) // 添加girlGod同时,添加两条dog记录
// 2. 一对多的查询
var girl GirlGod
DB.Debug().Preload("Dogs").First(&girl) // 将Dog两条记录都打印出来了
fmt.Printf("girl: %v\n", girl)
// 3. 带条件的预加载preload
var girl2 GirlGod
DB.Preload("Dogs", "name=?", "dog2号").First(&girl2)
fmt.Printf("girl2: %v\n", girl2)
// 4. 自定义预加载
var girl3 GirlGod
DB.Preload("Dogs", func(db *gorm.DB) *gorm.DB { // 定义匿名函数
return db.Where("name=?", "dog2号")
}).Find(&girl3)
fmt.Printf("girl3: %+v\n", girl3)
// 5. 嵌套加载
var girl4 GirlGod
DB.Preload("Dogs.Info").Preload("Dogs").First(&girl4) // 查询girls,带Dogs,带Info
fmt.Printf("girl4: %v\n", girl4)
}
// 对外键和引用重写
// 默认情况下,第二个表的外键,引用第一个表的主键。 例如:Dog表的外键GirlGodID引用的是GirlGod的主键ID。
// 可以对外键和引用重写。
package main
type User struct {
ID uint
Name string `gorm:"primaryKey;type:varchar(36);"`
// Cards []Card `gorm:"foreignKey:Number;"` // 此时Number作为外键,引用User的主键ID
Cards []Card `gorm:"foreignKey:Number;references:Name"` // 此时Number作为外键,引用User的Name,此时需要设置字段类型和主键
}
type Card struct {
ID uint
Number string `gorm:"type:varchar(36)"`
UserID uint
}
func main() {
DB.AutoMigrate(&User{}, &Card{})
c1 := Card{Number: "1000abc"}
c2 := Card{Number: "2000abc"}
user := User{Name: "zs", Cards: []Card{c1, c2}}
DB.Create(&user)
}
多对多关系
type Info struct {
gorm.Model
Money uint
DogId uint // dog的外键, 一对一
}
// 一个Dog 有多个女神GirlGod
type Dog struct {
gorm.Model
Name string
Info Info
GirlGods []GirlGod `gorm:"many2many:dog_girl_god"` // 添加GirlGod切片, 需要添加tag,其中dog_girl_god是连接表
}
// 一个女神有多个Dog
type GirlGod struct {
gorm.Model
Name string
Dogs []Dog `gorm:"many2many:dog_girl_god"` // 添加many2many 的tag,否则报错,其中dog_girl_god是连接表
}
func Many2Many() {
DB.AutoMigrate(&Dog{}, &GirlGod{}, &Info{})
// 1. 添加记录
i := Info{
Money: 3000,
}
g1 := GirlGod{
Name: "女神1号",
}
g2 := GirlGod{
Name: "女神2号",
}
d := Dog{
Name: "狗子1号",
Info: i,
GirlGods: []GirlGod{g1, g2},
}
DB.Create(&d) // 添加记录
// 2. 查询
var dogs []Dog
DB.Model(&Dog{}).Preload("GirlGods").Find(&dogs) // 查询所有Dogs记录
fmt.Printf("dogs: %+v\n", dogs)
var girlGods []GirlGod
DB.Model(&GirlGod{}).Preload("Dogs").Find(&girlGods) // 查询所有GirlGods记录
fmt.Printf("girlGods: %+v\n", girlGods)
}
事务操作
// 1. 禁用事务
dsn := "root:123@tcp(127.0.0.1:3306)/gorm_new?charset=utf8mb4&parseTime=true&loc=Local"
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
SkipDefaultTransaction: true,
})
// 2. Transaction() 返回nil提交事务,返回error,则rollback
DB.Transaction(func(tx *gorm.DB) error {
tx.Create(&TMG{Name: "NAME1"})
tx.Create(&TMG{Name: "NAME2"})
tx.Create(&TMG{Name: "NAME3"})
return nil
})
// 3. 手动事务
tx := DB.Begin()
tx.Create(&TMG{Name: "NAME1111"})
tx.Create(&TMG{Name: "NAME2222"})
tx.Create(&TMG{Name: "NAME3333"})
//tx.Rollback()
tx.Commit()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南