GORM使用Preload加载含有复合主键表的空关系报错的问题

天锦 发表于 某的代码片段 分类,标签:

两个结构体定义两张表,一个Device表存储设备信息,一个Tag表存储设备的标签,一个设备可用打多个标签,多个设备也可以使用一个相同的标签,故使用了many2many关系。两个结构体如下:

type Device struct {
    ID        uint `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
    UserID uint `json:"userId" form:"userId" gorm:"column:user_id;comment:用户ID;"`
    Type string `json:"type" form:"type" gorm:"column:type;comment:设备类型;type:varchar(255);"`
    Hostname string `json:"hostname" form:"hostname" gorm:"column:hostname;comment:主机名;type:varchar(255);"`
    Manufacturer string `json:"manufacturer" form:"manufacturer" gorm:"column:manufacturer;comment:生产厂商;type:varchar(255);"`
    Model string `json:"model" form:"model" gorm:"column:model;comment:型号;type:varchar(255);"`
    OS string `json:"os" form:"os" gorm:"column:os;comment:系统;type:varchar(255);"`
    Remark string `json:"remark" form:"remark" gorm:"column:remark;comment:备注;type:varchar(255);"`
    Tags []Tag `json:"tags" form:"tags" gorm:"many2many:relation_device_tag"`
}
func (Device) TableName() string {
    return "devices"
}
type Tag struct {
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
    UserID uint `json:"userId" form:"userId" gorm:"primary_key;column:user_id;comment:用户ID;"`
    Label string `json:"label" form:"label" gorm:"primary_key;column:label;comment:Tag名称;type:varchar(255);"`
    Devices []Device `json:"devices" form:"devices" gorm:"many2many:relation_device_tag"`
}
func (Tag) TableName() string {
    return "tags"
}
// Preload联合查询
err = db.Model(&Device{}).Where("`user_id` = 1 AND `id` = 1").Preload("Tags").Find(&findLists).Error

目前的问题是,插入一条含有Tags信息的Device结构数据,联查正常,没有报错,如果插入一条没有Tags信息的数据,再联查这条没有Tags信息的Device记录时报错,查询失败。

打开gebug信息,看SQL语句,一下是正常的

SELECT * FROM `relation_device_tag` WHERE `relation_device_tag`.`device_id` = 1
SELECT * FROM `tags` WHERE (`tags`.`user_id`,`tags`.`label`) IN ((1,"Server")) AND `tags`.`deleted_at` IS NULL
SELECT * FROM `devices` WHERE `user_id` = 1 AND `id` = 1 AND `devices`.`deleted_at` IS NULL LIMIT 10

再看一下加了一条没有Tag后联查报错的:

SELECT * FROM `relation_device_tag` WHERE `relation_device_tag`.`device_id` = 2
SELECT * FROM `tags` WHERE (`tags`.`user_id`,`tags`.`label`) IN (NULL) AND `tags`.`deleted_at` IS NULL
SELECT * FROM `devices` WHERE `user_id` = 1 AND `id` = 2 AND `devices`.`deleted_at` IS NULL LIMIT 10

报错:

Error 1241: Operand should contain 2 column(s)

也就是在查了many2many的结构之后,由于第二条Device没有Tag信息,查了关联表relation_device_tag之后得到的是NULL,恰巧Tag表是复合主键,是需要两个主键的,一个NULL无法匹配完整条件,也就造成联查失败了。

目前原因找到了,解决方案暂时没找到。