Golang Gorm 一对多 preload预加载

预加载示例  preload + find/take


 GORM允许使用 Preload通过多个SQL中来直接加载关系, 例如:

type User struct {
  gorm.Model
  Username string
  Orders   []Order
}

type Order struct {
  gorm.Model
  UserID uint
  Price  float64
}

// 查找 user 时预加载相关 Order
db.Preload("Orders").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4);



// 预加载Orders、Profile、Role多个关联属性  ps: 预加载字段,必须是User的属性
db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)

// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to
	var u []User
	db.Debug().Preload("Articles").Find(&u)
	fmt.Println(u)

[1.486ms] [rows:7] SELECT * FROM `article` WHERE `article`.`user_id` IN (1,2,3)

[2.644ms] [rows:3] SELECT * FROM `user`
[{1 lucas [{1 golang 1} {2 k8s 1}]} {2 yanzi [{3 golang 2} {4 k8s 2}]} {3 test [{5 redis 3} {6 cherry 3} {7 marry 3}]}]
	var u User
	db.Debug().Where("name = ?", "lucas").Find(&u)
	fmt.Printf("%#v\n", u)

	db.Debug().Preload("Articles").Find(&u)
	fmt.Printf("%#v\n", u)

	var a Article
	db.Debug().Take(&a, 8)
	fmt.Printf("%#v\n", a)

	db.Debug().Preload("User").Find(&a)
	fmt.Printf("%#v\n", a)

[1.306ms] [rows:4] SELECT * FROM `article` WHERE `article`.`user_id` = 1

[3.628ms] [rows:1] SELECT * FROM `user` WHERE `user`.`id` = 1
main.User{ID:1, Name:"lucas", Articles:[]main.Article{main.Article{ID:1, Title:"k8s", UserID:1, User:main.User{ID:0, Name:"", Arti
cles:[]main.Article(nil)}}, main.Article{ID:2, Title:"golang", UserID:1, User:main.User{ID:0, Name:"", Articles:[]main.Article(nil
)}}, main.Article{ID:9, Title:"c", UserID:1, User:main.User{ID:0, Name:"", Articles:[]main.Article(nil)}}, main.Article{ID:10, Tit
le:"ruby", UserID:1, User:main.User{ID:0, Name:"", Articles:[]main.Article(nil)}}}}


[1.667ms] [rows:1] SELECT * FROM `article` WHERE `article`.`id` = 8 LIMIT 1
main.Article{ID:8, Title:"c++", UserID:3, User:main.User{ID:0, Name:"", Articles:[]main.Article(nil)}}


[1.124ms] [rows:1] SELECT * FROM `user` WHERE `user`.`id` = 3


[3.090ms] [rows:1] SELECT * FROM `article` WHERE `article`.`id` = 8
main.Article{ID:8, Title:"c++", UserID:3, User:main.User{ID:3, Name:"jerry", Articles:[]main.Article(nil)}}

preload的原理


其实很简单,你要preload user表的数据,gorm就提前把这张表全部取出来,然后再执行FirstFind这样的方法去查询主数据,最后把两种数据通过外键关联一一对应起来。

其实preload也就是做了两次查询。

	var u User
	db.Debug().Preload("Articles").Take(&u, "name = ?", "test")
	fmt.Println(u)


[2.007ms] [rows:3] SELECT * FROM `article` WHERE `article`.`user_id` = 3

[3.056ms] [rows:1] SELECT * FROM `user` WHERE name = 'test' LIMIT 1
{3 test [{5 redis 3} {6 cherry 3} {7 marry 3}]}

带条件预加载  只想展示某些


 

	var u User
	db.Debug().Where("name = ?", "lucas").Find(&u)

	db.Debug().Preload("Articles", "id = ?", 2).Find(&u)
	fmt.Println(u)

{1 lucas [{2 golang 1 {0  []}}]}

嵌套预加载


	var a Article
	db.Debug().Take(&a, 8)
	db.Debug().Preload("User.Articles").Find(&a)
	fmt.Printf("%#v\n", a)

[4.840ms] [rows:1] SELECT * FROM `article` WHERE `article`.`id` = 8
main.Article{ID:8, Title:"c++", UserID:3, User:main.User{ID:3, Name:"jerry", Articles:[]main.Article{main.Article{ID:8, Title:"c++", UserID:3, User:m
ain.User{ID:0, Name:"", Articles:[]main.Article(nil)}}}}}

级联删除


	var u User
	
	db.Debug().Where("name = ?", "lisa").Find(&u)
	db.Debug().Select("Articles").Delete(&u)

[0.748ms] [rows:1] SELECT * FROM `user` WHERE name = 'lisa'
[2.106ms] [rows:1] DELETE FROM `article` WHERE `article`.`user_id` = 4
[6.887ms] [rows:1] DELETE FROM `user` WHERE `user`.`id` = 4

清除外键关系   Association + Delete、Clean


 有了preload,那么user就可以通过.点出来。

	db.Debug().Preload("Articles").Take(&u, 1
	)
	fmt.Println(u)


[4.167ms] [rows:1] SELECT * FROM `user` WHERE `user`.`id` = 1 LIMIT 1
{1 lucas [{1 k8s 1 {0  []}} {2 golang 1 {0  []}} {9 c 1 {0  []}} {10 ruby 1 {0  []}}]}
	var u User
	db.Take(&u, 2)
	db.Model(&u).Association("Articles").Find(&u.Articles)
	db.Debug().Model(&u).Association("Articles").Delete(&u.Articles)

[4.128ms] [rows:2] UPDATE `article` SET `user_id`=NULL WHERE `article`.`user_id` = 2 AND `article`.`id` IN (3,7)

mysql> select * from user;
+----+-------+
| id | name  |
+----+-------+
|  2 | yanzi |
|  3 | lucas |
|  4 | yanzi |
+----+-------+
3 rows in set (0.00 sec)

mysql> select * from article;
+----+--------+---------+
| id | title  | user_id |
+----+--------+---------+
|  1 | golang |    NULL |
|  2 | k8s    |    NULL |
|  3 | c#     |       2 |
|  4 | golang |       3 |
|  5 | k8s    |       3 |
|  6 | c#     |       4 |
|  7 | oracle |       2 |
+----+--------+---------+
7 rows in set (0.00 sec)

#修改之后

mysql> select * from user;
+----+-------+
| id | name  |
+----+-------+
|  2 | yanzi |
|  3 | lucas |
|  4 | yanzi |
+----+-------+

mysql> select * from article;
+----+--------+---------+
| id | title  | user_id |
+----+--------+---------+
|  1 | golang |    NULL |
|  2 | k8s    |    NULL |
|  3 | c#     |    NULL |
|  4 | golang |       3 |
|  5 | k8s    |       3 |
|  6 | c#     |       4 |
|  7 | oracle |    NULL |
+----+--------+---------+

  清空关联关系示例:先清空关联关系,再删除

	var u User
	db.Take(&u, 1)
	db.Debug().Model(&u).Association("Articles").Clear()

[11.177ms] [rows:2] UPDATE `article` SET `user_id`=NULL WHERE `article`.`user_id` = 1



mysql> select * from user;
+----+-------+
| id | name  |
+----+-------+
|  1 | lucas |
|  2 | yanzi |
|  3 | lucas |
|  4 | yanzi |
+----+-------+

mysql> select * from article;
+----+--------+---------+
| id | title  | user_id |
+----+--------+---------+
|  1 | golang |    NULL |
|  2 | k8s    |    NULL |
|  3 | c#     |       2 |
|  4 | golang |       3 |
|  5 | k8s    |       3 |
|  6 | c#     |       4 |
|  7 | oracle |       2 |
+----+--------+---------+


db.Debug().Delete(&u)
[9.960ms] [rows:1] DELETE FROM `user` WHERE `user`.`id` = 1


mysql> select * from user;
+----+-------+
| id | name  |
+----+-------+
|  2 | yanzi |
|  3 | lucas |
|  4 | yanzi |
+----+-------+

mysql> select * from article;
+----+--------+---------+
| id | title  | user_id |
+----+--------+---------+
|  1 | golang |    NULL |
|  2 | k8s    |    NULL |
|  3 | c#     |       2 |
|  4 | golang |       3 |
|  5 | k8s    |       3 |
|  6 | c#     |       4 |
|  7 | oracle |       2 |
+----+--------+---------+
<think>嗯,用户想了解如何在Golang中使用GORM。首先,我需要确定用户对GORM的了解程度。可能他们已经有Golang的基础,现在想学习ORM库来操作数据库。GORM是一个流行的ORM工具,支持种数据库,比如MySQL、PostgreSQL等。 首先,我应该从安装开始讲起,用户需要知道如何用go get安装GORM和对应的数据库驱动。比如,安装GORM和MySQL驱动的话,命令是go get gorm.io/gormgo get gorm.io/driver/mysql。 接下来是连接数据库的步骤。需要提供一个DSN,例如用户名、密码、地址、端口、数据库名等。这里要注意处理可能的错误,比如数据库连接失败的情况,所以需要检查错误并处理。 然后是定义模型结构体。GORM使用结构体来映射数据库表,默认情况下,表名是结构体名的复数形式,但也可以通过TableName()方法自定义。模型中的字段对应表的列,可以使用gorm标签来指定主键、自增等属性。比如定义一个User结构体,包含ID、Name、Email等字段,ID作为主键且自增。 接下来是基本的CRUD操作。创建记录使用Create方法,需要注意指针传递和错误处理。查询的话,可以使用First、Find等方法,First是获取第一条记录,按主键排序。更新操作使用Updates方法,可以部分更新或全量更新,这里要提醒用户注意Select和Omit的使用。删除操作使用Delete,可以是软删除或硬删除,取决于模型是否有DeletedAt字段。 然后是高级查询部分,比如条件查询、预加载关联、事务处理。条件查询可以用Where方法,预加载关联用Preload避免N+1查询问题。事务的话,使用Transaction方法,确保原子性。 关联关系处理可能比较复杂,比如一对一、一对。这里需要举例说明,比如用户有个文章,用HasMany关联,文章属于用户,用BelongsTo关联。的话,需要中间表,用Many2Many标签。 最后是注意事项,比如关闭调试模式、避免全表更新、使用连接池。这些是实际开发中容易遇到的问题,需要提醒用户注意。 总结一下,回答的结构应该是先介绍GORM,然后安装、连接数据库、定义模型,CRUD操作,高级查询,关联处理,注意事项。这样用户能逐步学习如何使用GORM,同时避免常见错误。需要确保示例代码正确,符合GORM的最新语法,比如使用gorm.Open的格式,以及正确的标签写法。另外,要提醒用户处理错误,避免程序崩溃。可能用户在使用过程中会遇到问题,比如表名不对,字段未正确映射,事务处理不当等,所以注意事项部分要强调这些点。</think>以下为Golang GORM的使用介绍,分为基础操作和进阶技巧两部分: 📌 基础篇 1️⃣ 安装与连接 ```go go get gorm.io/gorm go get gorm.io/driver/mysql // 以MySQL为例 // 连接数据库 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("数据库连接失败") } ``` 2️⃣ 模型定义 ```go type User struct { ID uint `gorm:"primaryKey;autoIncrement"` Name string `gorm:"type:varchar(100);not null"` Email string `gorm:"uniqueIndex"` CreatedAt time.Time } // 自定义表名 func (User) TableName() string { return "sys_user" } ``` 3️⃣ CRUD操作 ```go // 创建 user := User{Name: "张三", Email: "zhangsan@example.com"} result := db.Create(&user) // 查询 var findUser User db.First(&findUser, "name = ?", "张三") // 更新 db.Model(&user).Updates(User{Name: "李四"}) // 删除 db.Delete(&user) ``` 🚀 进阶技巧 1️⃣ 关联查询 ```go // 预加载关联数据 db.Preload("Orders").Find(&users) // 一对关联定义 type User struct { ID uint Orders []Order `gorm:"foreignKey:UserID"` } type Order struct { UserID uint // ...其他字段 } ``` 2️⃣ 事务处理 ```go db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&user).Error; err != nil { return err } if err := tx.Create(&order).Error; err != nil { return err } return nil }) ``` 3️⃣ 复杂查询 ```go // 条件组合 db.Where("age > ?", 18). Order("created_at desc"). Limit(10). Find(&users) // 原生SQL db.Raw("SELECT * FROM users WHERE age > ?", 18).Scan(&result) ``` ⚠️ 注意事项 1. 生产环境关闭Debug模式: ```go db, err := gorm.Open(..., &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent) }) ``` 2. 批量操作时建议分批次处理: ```go db.CreateInBatches(users, 100) ``` 3. 使用连接池配置: ```go sqlDB, _ := db.DB() sqlDB.SetMaxOpenConns(100) sqlDB.SetMaxIdleConns(10) ``` 建议结合官方文档(https://gorm.io/zh_CN/docs/)进行实践,实际开发中注意错误处理和日志记录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值