批量插入或者更新某些数据
场景描述:当我们遇到需要批量插入或者更新的数据时候 解决方案:
- 建立复合唯一索引,gorm是使用关键字uniqueIndex,这一步是为了使用数据库的ON DUPLICATE KEY UPDATE功能,有的数据库不支持这类功能
ON DUPLICATE KEY UPDATE 是 MySQL 中的一个特性,用于在插入数据时处理主键或唯一索引冲突。当你尝试插入一行数据,而这行数据的主键或唯一索引与已有的数据冲突时,MySQL 会返回一个错误。但是,如果你使用了 ON DUPLICATE KEY UPDATE 子句,MySQL 就会更新已有的数据,而不是返回一个错误。
go
type WordItem struct {
gorm.Model
RecordID uint `gorm:"primaryKey;uniqueIndex:idx_record_word"` // 外键,关联到Record的ID
Word string `gorm:"type:varchar(255);uniqueIndex:idx_record_word"`
Status int
Timestamp int64
}
-
开启事务,然后新建一个切片,类型为要批量保存的数据类型,然后新建worditem对象
go// 遍历 dataList 中的 RecordList for _, recordList := range dataList.RecordList { // 对于每个 recordList,创建一个新的 WordItem 对象 wordItem := scanpen.WordItem{ RecordID: record.ID, Word: recordList.Word, Status: recordList.Status, Timestamp: recordList.Timestamp, } // 使用原生 SQL 语句插入或更新 WordItem sql := `INSERT INTO scanpan_record_item (RecordID, Word, Status, Timestamp, CreatedAt, UpdatedAt) VALUES (?, ?, ?, ?, NOW(), NOW()) ON DUPLICATE KEY UPDATE Status = VALUES(Status), Timestamp = VALUES(Timestamp), UpdatedAt = NOW()` if err := tx.Exec(sql, wordItem.RecordID, wordItem.Word, wordItem.Status, wordItem.Timestamp).Error; err != nil { tx.Rollback() return err } // 将 WordItem 添加到 Record 的 WordItems 列表中 record.WordItems = append(record.WordItems, wordItem) } // 保存 Record 到数据库 if err := tx.Save(&record).Error; err != nil { tx.Rollback() return err }
这段代码的主要目的是将 `dataList.RecordList
` 中的每个 recordList
项作为一个新的 WordItem
对象插入到 scanpan_record_item
数据库表中。如果 scanpan_record_item
表中已经存在相同 RecordID
和 Word
的项,那么这个项的 Status
、Timestamp
和 UpdatedAt
字段会被更新。
以下是这段代码的详细解释:
-
for _, recordList := range dataList.RecordList { ... }
:这是一个 for-range 循环,用于遍历dataList.RecordList
列表中的每个元素。每次循环,recordList
都会被设置为列表中的一个元素。 -
wordItem := scanpen.WordItem{ ... }
:这行代码创建了一个新的WordItem
对象,其字段值从recordList
中获取。 -
sql :=
INSERT INTO scanpan_record_item ...``:这行代码定义了一个 SQL 语句,用于插入或更新WordItem
。这个 SQL 语句使用了 MySQL 的ON DUPLICATE KEY UPDATE
特性,如果尝试插入的RecordID
和Word
与已有的数据冲突,这个 SQL 语句会更新这个RecordID
和Word
对应的Status
和Timestamp
。 -
if err := tx.Exec(sql, ...).Error; err != nil { ... }
:这行代码执行了上面定义的 SQL 语句。如果 SQL 语句执行失败,这行代码会回滚事务并返回错误。 -
record.WordItems = append(record.WordItems, wordItem)
:这行代码将新创建的WordItem
添加到Record
的WordItems
列表中。
这段代码的写法主要是为了处理 RecordList
中可能存在的重复项。通过使用 ON DUPLICATE KEY UPDATE
,这段代码可以在插入新的 WordItem
时自动更新已有的 WordItem
,而不需要先查询数据库来检查 WordItem
是否已存在。这样可以减少数据库查询,提高代码的效率。