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

huangapple go评论159阅读模式
英文:

Golang: duplicate key value violates unique constraint

问题

当我尝试将两个或更多的注册表插入到for循环中时,我遇到了这个错误。第一个注册表正常工作,但是之后就出现了错误。数据库是新的,我已经多次重新创建了它。

pq: duplicate key value violates unique constraint "movements_pkey"

这是我的movements模型:

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"`
}

这是我调用保存到数据库的函数(MovementCreate)的位置,在for循环中调用它。第一个总是成功创建,但第二个给我这个错误,它试图创建一个新的记录,但显然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,但是新记录的数据是正确的。

英文:

I'm getting this error when I try to insert two or more register into for loop, the first one works fine but then the error appears, the database is new and I have had recreated many times

pq: duplicate key value violates unique constraint \&quot;movements_pkey\

I have this model of my movements

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

And here is where I call the function to save in database (MovementCreate) into in a for loop, the first one always is created successfully but the second give me this error, where try to create a new record but apparently postgresql take the same last id

func (w *WebServices) CreateMovementMoneyOut(dataMovement *validators.MovementValidator) ([]models.IncomingResult, error) {
	nameFile := &quot;Movement Services&quot;
	dataWallet, err := w.wallet.FindWalletByCustomerID(dataMovement.FKCustomer)
	if err != nil {
		utils.LogService(nameFile, &quot;Wallet Find&quot;, err.Error(), &quot;error&quot;)
		return nil, err
	}
	if (dataWallet.Amount &lt;= 0) || !(*dataMovement.FkType == 2) {
		utils.LogService(nameFile, &quot;Wallet Update&quot;, &quot;Insufficients funds&quot;, &quot;error&quot;)
		return nil, fmt.Errorf(&quot;Insufficients funds&quot;)
	}
	incomingUpdate, err := w.incoming.UpdateIncomingsByFkWallet(&amp;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(&amp;movement)

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

Here when save on database

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

The complete response of the error in console is this

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

16 is the last id created successfully in the first iteration in the loop, and the next iteration try with the same id but the correct data of the new record

答案1

得分: 0

如果您正在使用gorm V2,请将primary_key替换为primaryKey,并且默认情况下添加autoIncrement,其值为true。如果需要,您可以添加Unique约束,这取决于您的需求。

type Movement struct {
    ID        uint64     `gorm:"primaryKey;autoIncrement" 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循环内部添加var movement models.Movement

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

for _, val := range incomingUpdate {
    var movement models.Movement             //更新此处
    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
    }
}
英文:

If you are using gorm V2 then replace primary_key to primaryKey and also add autoIncrement by default it will be true. If you want you can add Unique constraints it's up to you.

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

For each set of data you need to create an object. Add this var movement models.Movement inside the for loop.

And you don't need to mention movement.ID by default it will be handled by gorm very nicely.

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(&amp;movement)

    if err != nil {
        utils.LogService(nameFile, &quot;CreateMovementMoneyOut&quot;, err.Error(), &quot;error&quot;)
        return nil, err
    }
}

huangapple
  • 本文由 发表于 2021年12月9日 08:19:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/70283288.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定