Golang:重复键值违反唯一约束

Posted

技术标签:

【中文标题】Golang:重复键值违反唯一约束【英文标题】:Golang: duplicate key value violates unique constraint 【发布时间】:2022-01-13 21:26:17 【问题描述】:

当我尝试将两个或多个寄存器插入 for 循环时出现此错误,第一个工作正常但随后出现错误,数据库是新的,我已经重新创建了很多次

pq: duplicate key value violates unique constraint \"movements_pkey\

我有我的动作模型

type Movement struct 
    ID        int        `gorm:"primary_key" json:"id"`
    Amount    float32    `json:"amount"`
    FkType    *int       `gorm:"column:fk_type" json:"fk_type"`
    Type      Type       `gorm:"foreignkey:FkType"`
    FkIncome  int        `gorm:"column:fk_income" json:"fk_income"`
    Income    Incoming   `gorm:"foreignkey:FkIncome"`
    FkOrder   *int       `gorm:"column:fk_order" json:"fk_order,omitempty"`
    CreatedAt *time.Time `json:"created_at"`

这里是我在 for 循环中调用函数以保存在数据库 (MovementCreate) 中的地方,第一个总是成功创建,但第二个给我这个错误,尝试创建新记录但显然 postgresql 采取相同的最后一个ID

func (w *WebServices) CreateMovementMoneyOut(dataMovement *validators.MovementValidator) ([]models.IncomingResult, error) 
    nameFile := "Movement Services"
    dataWallet, err := w.wallet.FindWalletByCustomerID(dataMovement.FKCustomer)
    if err != nil 
        utils.LogService(nameFile, "Wallet Find", err.Error(), "error")
        return nil, err
    
    if (dataWallet.Amount <= 0) || !(*dataMovement.FkType == 2) 
        utils.LogService(nameFile, "Wallet Update", "Insufficients funds", "error")
        return nil, fmt.Errorf("Insufficients funds")
    
    incomingUpdate, err := w.incoming.UpdateIncomingsByFkWallet(&dataWallet.ID, *dataMovement.Amount)
    var movement models.Movement
    for _, val := range incomingUpdate 
        movement.Amount = val.Amount
        movement.FkIncome = val.ID
        movement.FkType = dataMovement.FkType
        movement.FkOrder = dataMovement.FkOrder
        _, err := w.movement.MovementCreate(&movement)

        if err != nil 
            utils.LogService(nameFile, "CreateMovementMoneyOut", err.Error(), "error")
            return nil, err
        
    
    newAmount, err := w.incoming.CalculateWalletAmount(&dataWallet.ID)
    if err != nil 
        utils.LogService(nameFile, "Wallet Update", err.Error(), "error")
    
    _, err = w.wallet.WalletUpdateAmount(dataWallet, newAmount)
    if err != nil 
        utils.LogService(nameFile, "Wallet Update", err.Error(), "error")
        return nil, err
    
    return incomingUpdate, nil

保存到数据库时在此处

func (s *WalletService) MovementCreate(data *Movement) (*Movement, error) 
  result := s.Create(data)
  return data, result.Error

控制台报错的完整响应是这样的

2021/12/08 20:57:18 /usr/src/app/pkg/datalayers/models/movement.go:45 pq: duplicate key value violates unique constraint "movements_pkey"
[1.249ms] [rows:0] INSERT INTO "movements" ("amount","fk_type","fk_income","fk_order","created_at","id") VALUES (1000.000000,2,7,1,'2021-12-08 20:57:16.173',16) RETURNING "id"

16是循环中第一次迭代成功创建的最后一个id,下一次迭代尝试使用相同的id但新记录的数据正确

【问题讨论】:

上面代码中的motion.ID分配在哪里。请确保每次在循环内都将其覆盖,或者您可以在循环内声明移动变量以确保不会继续前一个循环的数据。 WalletService.Create 中发生了什么。此外,由于您的 id 是一个 int - 我猜您可能还需要结构中的 autoIncrement 字段标记 - 不过请先检查您的表详细信息(\d tablename)。如果您自己处理,请忽略。 【参考方案1】:

如果您使用的是gorm V2,请将primary_key 替换为primaryKey,并添加autoIncrement,默认情况下为true。如果您愿意,您可以添加 Unique 约束,这取决于您。

type Movement struct 
    ID        uint64     `gorm:"primaryKey;autoIncrement" json:"id"` //Change it
    Amount    float32    `json:"amount"`
    FkType    *int       `gorm:"column:fk_type" json:"fk_type"`
    Type      Type       `gorm:"foreignkey:FkType"`
    FkIncome  int        `gorm:"column:fk_income" json:"fk_income"`
    Income    Incoming   `gorm:"foreignkey:FkIncome"`
    FkOrder   *int       `gorm:"column:fk_order" json:"fk_order,omitempty"`
    CreatedAt *time.Time `json:"created_at"`

您需要为每组数据创建一个对象。在 for 循环中添加 var movement models.Movement

而且你不需要提及movement.ID,默认情况下它会被gorm很好地处理。

    for _, val := range incomingUpdate 
    var movement models.Movement             //Update it
    movement.Amount = val.Amount
    movement.FkIncome = val.ID
    movement.FkType = dataMovement.FkType
    movement.FkOrder = dataMovement.FkOrder
    _, err := w.movement.MovementCreate(&movement)

    if err != nil 
        utils.LogService(nameFile, "CreateMovementMoneyOut", err.Error(), "error")
        return nil, err
    

【讨论】:

谢谢!我将运动移入循环并开始工作。我还在用 gorm 1.9

以上是关于Golang:重复键值违反唯一约束的主要内容,如果未能解决你的问题,请参考以下文章

重复的键值违反了唯一约束,CakePHP

django:IntegrityError:重复键值违反唯一约束

TypeORM - 重复键值违反唯一约束(可延迟 fk)

Django 重复键值违反唯一约束错误模型表单

勺子插入 postgres 会产生“重复键值违反唯一约束”

Django Rest Framework:重复键值违反唯一约束