防止 GORM 在一个重复的主键错误后放弃 INSERT

我从 API 中提取数据。拉取结果生成结构数组,每个数组包含 100 个元素。我用来将它们插入数据库的代码是:


   func (ACUpdater) InsertToTable(rawData *models.Contacts, table *gorm.DB) {

    for i := 0; i < len(rawData.Results); i++ {

        temp := models.ACQ_report{

            ID :                           "",  //to be created later by hashing below data fields

            RecordID:                      rawData.Results[i].ID,

            FirstName:                     rawData.Results[i].Properties.Firstname,

            LastName:                      rawData.Results[i].Properties.Lastname,

            Email:                         rawData.Results[i].Properties.Email,

            PhoneNumber:                   rawData.Results[i].Properties.Phone,

            ContactOwner:                  rawData.Results[i].Properties.HubspotOwnerID,

            CompanyName:                   rawData.Results[i].Properties.Company,

        }

        temp.ID = hashContactRecord(&temp)

        table.Create(&temp)

        fmt.Println(&temp)

        fmt.Println(i)

    }

}

我使用数据字段的散列作为表的主键,因此如果这些数据字段中的任何一个发生变化,散列也会发生变化。这样,我就可以将已更改的记录附加到现有表中,而不必担心重复的主键。


INSERTING问题是,在 GORM 出现 1 个重复主键错误后,上述功能完全“放弃” 。如果要插入数据库的第一条记录是重复的,那么tx.Created(&temp)仍然会运行,但不会插入更改的记录。好像tx.Create()在第一个重复的主键错误之后放弃了。


如何修复此行为?


一只甜甜圈
浏览 413回答 1
1回答

鸿蒙传说

这里的第一个问题可能掩盖了实际的根本原因,即您没有检查是否INSERT返回错误。你会这样做:result := tx.Create(&temp)if result.Error != nil {&nbsp; // handle it somehow}如果你这样做并检查错误,你可能会看到一些东西:错误:重复键值违反唯一约束即使您还看到以下错误,您也可能会看到这一点。在这种情况下,您的 INSERT 正在执行,但失败了。如果您没有看到任何其他错误并且只打印一次,那么您可能传入了一个 gorm.DB 句柄,该句柄已链接到数据库会话,并且会在第一个错误时失败。例如,如评论中所述,如果将结果传递db.Table("my_table")给此方法,则将出现上述情况。要修复它,只需传递db或db.NewSession(),并更新您的方法以指定表(或模型,更像 Gorm):result := db.Table("my_table_name").Create(&temp)if result.Error != nil {&nbsp; // ...}选项 2:错误:当前事务被中止,命令被忽略,直到事务块结束如果您看到这一点,则意味着您的方法正在事务中运行它的插入。这对你来说并非如此,但由于这是一个通用论坛,我将在此处留下这个和下面的解释:在 Postgres 中,如果任何语句在事务中失败,你不能执行任何进一步的语句,除了 ROLLBACK .要解决此问题,您有几种选择:在尝试插入之前进行更多的数据验证,直到您可以可靠地预期每次插入都会成功。您还可以使用 Gorm 的批量插入功能通过这种方法优化插入。不要使用事务。如果您可以接受跳过的行,并且不担心重复,那么这是一个合理的选择。使用保存点。在 Postgres 中,SAVEPOINT 类似于事务中的检查点,您可以回滚到它而不是回滚整个事务,这正是您想要的:tx.SavePoint("sp1")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // SAVEPOINT sp1;result := tx.Create(&temp)if result.Error != nil {&nbsp; tx.RollbackTo("sp1")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// ROLLBACK TO sp1;}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go