简体   繁体   English

GORM 的 FirstOrCreate() 方法(或 Django 的 get_or_create)如何确保只创建一行?

[英]How can GORM's FirstOrCreate() method (or Django's get_or_create) ensure that just one row is created?

I'm considering using GORM for an application and was looking into how FirstOrCreate works, and it seems that it uses two database operations.我正在考虑将 GORM 用于应用程序,并正在研究FirstOrCreate工作原理,它似乎使用了两个数据库操作。 Consider this example script:考虑这个示例脚本:

package main

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "github.com/sirupsen/logrus"
)

type User struct {
    gorm.Model
    Name string
    Age  uint
}

func main() {
    db, err := gorm.Open("sqlite3", "examplegorm.db")
    if err != nil {
        logrus.Fatalf("open db: %v", err)
    }
    defer db.Close()

    db.LogMode(true)
    db.AutoMigrate(&User{})

    var user User
    db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
}

Upon running this and inspecting the logs, I see that (aside from the auto-migration) it uses two queries, one SELECT and one INSERT :运行它并检查日志后,我看到(除了自动迁移)它使用两个查询,一个SELECT和一个INSERT

kurt@Kurts-MacBook-Pro-13 ~/D/Scratch> go run gorm_example.go

(/Users/kurt/Documents/Scratch/gorm_example.go:23) 
[2020-01-05 09:09:10]  [1.03ms]  CREATE TABLE "users" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime,"name" varchar(255),"age" integer )  
[0 rows affected or returned ] 

(/Users/kurt/Documents/Scratch/gorm_example.go:23) 
[2020-01-05 09:09:10]  [0.86ms]  CREATE INDEX idx_users_deleted_at ON "users"(deleted_at)   
[0 rows affected or returned ] 

(/Users/kurt/Documents/Scratch/gorm_example.go:26) 
[2020-01-05 09:09:10]  [0.28ms]  SELECT * FROM "users"  WHERE "users"."deleted_at" IS NULL AND (("users"."name" = 'non_existing')) ORDER BY "users"."id" ASC LIMIT 1  
[0 rows affected or returned ] 

(/Users/kurt/Documents/Scratch/gorm_example.go:26) 
[2020-01-05 09:09:10]  [0.31ms]  INSERT  INTO "users" ("created_at","updated_at","deleted_at","name","age") VALUES ('2020-01-05 09:09:10','2020-01-05 09:09:10',NULL,'non_existing',20)  
[1 rows affected or returned ] 

As I understand from https://stackoverflow.com/a/16128088/995862 , however,然而,据我了解https://stackoverflow.com/a/16128088/995862

In a SQL DBMS, the select-test-insert approach is a mistake: nothing prevents another process from inserting the "missing" row between your select and insert statements.在 SQL DBMS 中,select-test-insert 方法是一个错误:没有什么可以阻止另一个进程在您的selectinsert语句之间插入“缺失”的行。

It seems that Django's get_or_create() works in a similar fashion.似乎 Django 的get_or_create()以类似的方式工作。 Given this model,鉴于这个模型,

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=255)
    age = models.PositiveIntegerField() 

if I enable database logging and run a get_or_create() query I see如果我启用数据库日志记录并运行get_or_create()查询,我会看到

In [1]: from djangoapp.models import *                                                                                           

In [2]: User.objects.get_or_create(name="jinzhu", age=20)                                                                        
(0.000) SELECT "djangoapp_user"."id", "djangoapp_user"."name", "djangoapp_user"."age" FROM "djangoapp_user" WHERE ("djangoapp_user"."age" = 20 AND "djangoapp_user"."name" = 'jinzhu') LIMIT 21; args=(20, 'jinzhu')
(0.000) BEGIN; args=None
(0.000) INSERT INTO "djangoapp_user" ("name", "age") VALUES ('jinzhu', 20); args=['jinzhu', 20]
Out[2]: (<User: User object (1)>, True)

In short, if I want to be sure that only one record gets created, it seems that I should refrain from using an ORM such as GORM or the Django ORM and write my own query?简而言之,如果我想确保只创建一条记录,似乎我应该避免使用诸如 GORM 或 Django ORM 之类的 ORM 并编写自己的查询?

A second question I have is how to get the equivalent of Django's created Boolean in GORM.我的第二个问题是如何在 GORM 中获得 Django created Boolean 的等价物。 Should I determine whether the RowsAffected of the resulting gorm.DB is 1 to determine whether a row was actually created or not?我应确定是否RowsAffected所产生的gorm.DB1来确定行是否实际创建或不?

You should just add UNIQUE constraint on the query model fields and that would be enough to keep it consistent in db您应该只在查询模型字段上添加 UNIQUE 约束,这足以使其在 db 中保持一致

for Django that would be adding meta class to model对于将元类添加到模型的 Django

class Meta:
    unique_together = ['name', 'age']

for GORM对于 GORM

Name string `gorm:"unique_index:idx_name_age"`
Age  uint   `gorm:"unique_index:idx_name_age"`

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM