foreign key options mysql外键_go-xorm使用外键报错(Cannot add or update a child row: a foreign key constraint...

在使用go-xorm操作包含外键约束的MySQL数据库时,可能会遇到'Cannot add or update a child row: a foreign key constraint fails'的错误。该错误通常是由于插入的数据中外键对应的ID在原表中不存在。本文通过一个楼房、邮箱和通信情况的业务场景,分析了导致错误的两种情况:更新操作后插入链接数据时的外键问题,以及内联查询结果映射时字段顺序不一致导致的ID错误。解决此类问题需要确保外键ID的有效性和查询结果映射的一致性。
摘要由CSDN通过智能技术生成

问题描述

使用mysql会经常遇到要使用外键的场景,go-xorm 作为一个orm的框架,在数据映射上使用非常方便,但在增添数据是经常碰到报错:

Cannot add or update a child row: a foreign key constraint fails

情不知所起

明明直接在数据库中插数据并没有报错,为什么用代码跑就出问题了?

报错原因

以上报错信息,英文的主要意思时外键约束未被满足,无法添加数据

具体原因插入的数据用外键的id在原表中不存在。

瞎编的案例

一个不严谨的业务场景

假设一个楼房有多个邮箱可以接受邮件,匿名邮件只有收件地址没有寄件地址,但每个楼房都有一个固定的邮递小哥负责将本楼房发出的邮件送到目的地,要统计楼房之间的通信情况需要三个表

mysql initail script

create table if no exists `buildings`(

`id` int(11) unsigned not null auto_inrement,

`addr` varchar(64) not null unique,

primary key(`id`)

)ENGINE=InnoDB

create table if no exists `mailboxes`(

`id` int(11) unsigned not null auto_inrement,

`number` int(11) not null,

`building_id` int(11) not null,

`username` varchar(16),

primary key(`id`),

unique key `building_mailbox` (`number`, `building_id`),

constraint `mail_address` foreign key (`building_id`) refernces `buildings`(`id`) on delete cascade

)ENGINE=InnoDB

create table if no exists `links`(

`id` int(11) unsigned not null auto_inrement,

`mialbox_id` int(11) not null,

`building_id` int(11) not null,

primary key(`id`),

unique key `mail_link` (`mialbox_id`, `building_id`),

constraint `mailbox_link` foreign key (`building_id`) refernces `buildings`(`id`) on delete cascade,

constraint `building_link` foreign key (`mialbox_id`) refernces `mailboxes`(`id`) on delete cascade

)ENGINE=InnoDB

go-xorm models

需要三个model,两个辅助model

主要model

type Building struct{

ID int64 `xorm:"pk autoincr notnull 'id'"`

Addr string `xorm:"varchar(64) nutnull unique"`

}

type Mailbox struct{

···

}

type Link struct{

···

}

ps:使用vscode时,编辑器会建议将字段‘Id’改为‘ID’,但字段‘ID’对应数据库的字段会变成‘i_d’。所以为了强迫症舒服,必须在后面的xorm里面标注在数据库对于的字段,即上文中的‘id’

辅助model

type BuildingMailbox struct{

Building `xorm:"extends"`

Mailbox `xorm:"extends"`

}

type BuildingMailboxLink struct{

}

Insert

func InsertLink(engine *xorm.Engine,box Mailbox,building Building) (l Link,err error){

l.MailboxID = box.ID

l.BuildingID = building.ID

_,err = engine.Table("links").Insert(&l)

if err != nil{

return fmt.Errorf("InsertLink:%v",err)

}

return nil

}

错误原因

我遇到,或者说我犯过的错误有两种

using update

场景描述

func UpdateMailboxUsername(engine *xorm.Engine,id int64,name string) (box Mailbox,err error){

box.Username = name

_,err = engine.Table("mailboxes").ID(id).Update(box)

return

}

使用上述代码更新数据库不会有任何问题。问题出在以下情况

box,err := UpdateMailboxUsername(e,id,"xorm")

if err !=nil{

···deal with the error···

}

link,has,err := GetLinkByBoxAndBuilding(e,box,building)

if err !=nil{

···deal with the error···

}

if !has{

link,err = InsertLink(e,box,building)

}

这个时候InsertLink就会报以上的错。额且错误并不是由于InsertLink。

错误解释

Update方法可以接受以下两种情况

_,err = engine.Table("mailboxes").ID(id).Update(box)

_,err = engine.Table("mailboxes").ID(id).Update(&box)

但两种情况都不会修改box的ID值。

ps:使用 Insert(&box) 会在box中注入保存后的id值

内联查询结果映射

使用内联查询

func GetMailboxByAddrAndNum(e *xorm.Engine,addr string,num int64) (b BuildingMailbox,has bool,err error){

has,err = engine.Table("mailboxes").Select("mailboxes.*, buildings.*").

Join("INNER","buildings","mailboxes.building_id = buildings.id").

Where("mailboxed.number = ?",num).And("buidings.addr = ?",addr).Get(&b)

if err !=nil{

err = fmt.Errorf("GetMailboxByAddrAndNum:%v",err)

}

return

}

以上函数能够得到结果,通过使用b.Mailbox和b.Building,能够直接使用两个对象,而且从数值上看好像没错,除了一样——id

进过和数据库仔细对比会发现,只有id字段会与数据库记录的不相同。

错误原因

BuildingMailbox解析出错

在上述的

···Select("mailboxes.*, buildings.*")···

和BuildingMailbox*定义中的顺序不一致。其他字段名称不同解析不会出现错误。但由于mailbox和building都有id字段,在解析式如果辅助结构体中定义的与数据就可能出现因为顺序不同而导致解析时出现id字段交换的情况。

小结

本文总结了笔者使用xorm的外键时犯过的错误。第一种错误是因为对go-xorm包不够熟悉,一段时间后自然就可以避免;第二种则要隐蔽得多,需要多多注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值