慎用gorm的preload加载大量关联数据

  1. 1. Prepared Statement contains too many placeholders
  2. 2. Why we should not abuse Gorm Preload?
    1. 2.1. Work Around

Prepared Statement contains too many placeholders

这是一个Mysql的Error。
当我们用类似 select * from users where id in (1,2,3,4,5)的时候,我们可能并没有在意这一串id到底能有多长。

实际上,这个in后面的list是有长度限制的,而且在Mysql中,这个限制无法更改,最大支持65535。

上面的query,当我们给的id太多超过这个限制的时候,就会出现
Prepared Statement contains too many placeholders
的报错了。

Why we should not abuse Gorm Preload?

1
2
3
4
5
6
7
8
9
10
11
type User Struct {
Id uint
Name string
Orders []Order `gorm:"many2many:user_orders"`
}

type Order Struct {
Id uint
Amount string
Users []User `gorm:"many2many:user_orders"`
}

上面的定义,如果我们需要找出用户id 1,2,3他们对应的所有order,可以简简单单用一个preload。

1
2
3
4
5
6
7
var users []User
var orders []Order
ids := []uint{1,2,3}
db.Preload("Orders").Find(&users, ids)
for _, user := range users {
orders = append(orders, user.orders)
}

这个乍看没啥问题呀。这个时候我们先看看gorm会去做哪几件事情。

1
2
3
// select * from users;
// select * from user_orders where user_id in (1,2,3);
// select * from orders where id in (xxx,xxx,xxx);

这个时候,如果1,2,3中间某个用户拥有大量的order,那最后的where语句就会抛出Prepared Statement contains too many placeholders的Error。

那咋办?

Work Around

直接用Raw写subquery。

1
2
3
var orders []Order
subquery := db.Raw("select order_id from user_orders where user_id in (1,3,4)")
db.Where("id in (?)", subquery).Find(&orders)
如果你觉得本文对你有帮助,请给我点赞助。