创建/删除由于缓存ID失败?

时间:2020-02-23 08:52:49

标签: mysql go visual-studio-code go-gorm

我在Visual Studio代码(VSCode)中具有以下代码(为Go单元测试设置)。它基本上包括4个单元测试:

  1. TestGormCreateDB:重新创建MySQL数据库用户表
  2. TestCreateMoon:将用户“ Moon”记录添加到用户表
  3. TestCreateDaltrey:将用户“ Daltrey”添加到用户表
  4. TestDeleteDaltrey:从用户表中删除用户“ Daltrey”

如果您有MySQL,则可以在VSCode中运行它,方法是将mySQLHost,mySQLUser,mySQLPass和mySQLDBName替换为您的值(必须先在MySQL中创建一个空数据库)。然后在VSCode中,您可以手动运行TestGormCreateDB,TestCreateMoon,TestCreateDaltrey,TestDeleteDaltrey,TestCreateDaltrey,TestDeleteDaltrey等。

每次删除Daltrey条目然后重新创建时,MySQL自动递增的ID都会增加1。最终,TestCreateDaltrey或TestDeleteDaltrey不会报告错误,但不会更新数据库(有时TestCreateDaltrey不能添加新记录,而在其他情况下,TestDeleteDaltrey不会删除应删除的现有记录)。这是因为在某个时刻,GORM建模的User {}结构中的ID继承了先前的Create或Delete中的先前ID,就好像ID已被缓存,而User {}结构中的ID开始使用无效的ID(用户表中的记录具有ID 4,但是代码发现Daltrey具有ID 3)。我无法确定这是我的错误,还是VSCode单元测试错误(用户结构从一个单元测试的运行缓存到另一个),GORM缓存错误甚至MySQL缓存错误。

package main

import (
    "fmt"
    "log"
    "testing"

    // MySQL db driver. Aliased to blank identifier, because we need the init()
    //   function to run, but we are not using anything else in the driver.
    // _ "github.com/go-sql-driver/mysql"
    _ "github.com/jinzhu/gorm/dialects/mysql"

    "github.com/jinzhu/gorm"
)

const (
    mySQLHost    = "localhost" // Replace with your MySQL host.
    mySQLPort    = "3306"
    mySQLUser    = "mysqluser" // Enter MySQL user.
    mySQLPass    = "mysqlpass" // Enter MySQL password.
    mySQLDBName  = "mysqldb"   // Enter MySQL database name.
    mySQLCharset = "utf8mb4"   // See https://mathiasbynens.be/notes/mysql-utf8mb4
)

type User struct {
    gorm.Model
    Name  string `gorm:"not null"`
    Email string `gorm:"unique_index; not null"`
}

// Db provides global access to the database.
var db *gorm.DB

func init() {
    var err error
    db, err = gorm.Open("mysql", getDSN())
    if err != nil {
        fmt.Println("failed to connect to database", mySQLDBName)
        log.Fatal(err)
    }
    err = db.DB().Ping()
    if err != nil {
        fmt.Println("failed to ping database", mySQLDBName)
    }
    fmt.Println("successfully connected to database")
    return
}

func getDSN() string {
    dsn := mySQLUser + ":" + mySQLPass
    dsn += "@tcp(" + mySQLHost + ":" + mySQLPort + ")"
    dsn += "/" + mySQLDBName
    dsn += "?charset=" + mySQLCharset
    dsn += "&parseTime=True"
    dsn += "&loc=Local"
    return dsn
}

// String to satisfy the Stringer interface.
func (u User) String() string {
    return fmt.Sprintf("user: ID=%d Name=%s Email=%s", u.ID, u.Name, u.Email)
}

// NewUser creates a new user struct, not adding user to database.
func NewUser(name, email string) User {
    return User{Name: name, Email: email}
}

// NewDatabaseUser creates a new user in the database.
func NewDatabaseUser(name, email string) (user User, err error) {
    // Create a user.
    user = NewUser(name, email)
    fmt.Println("NewDatabaseUser (before Create):", user)
    // Create new user in the database.
    err = user.Create()
    fmt.Println("NewDatabaseUser (after Create):", user)
    if err != nil {
        return User{}, err
    }
    return user, nil
}

// Create a new user, save user info into the database
func (u *User) Create() (err error) {
    err = db.Debug().Create(&u).Error
    fmt.Println("Create:", u)
    if err != nil {
        return err
    }
    return nil
}

// UserByEmail gets a single user given the email
func UserByEmail(email string) (u User, err error) {
    err = db.Debug().Where("email = ?", email).First(&u).Error
    fmt.Println("UserByEmail:", u)
    if err != nil {
        return User{}, err
    }
    return u, nil
}

// Delete user from database
func (u *User) Delete() (err error) {
    delUser, err := UserByEmail(u.Email)
    fmt.Println("Delete delUser: ", delUser)
    if err != nil {
        return err
    }
    // Double check that user has primary key ID.  If primary key is 0,
    //   GORM will delete all users from the database!
    if delUser.ID <= 0 {
        return fmt.Errorf("attempt to delete user with invalid id %d", delUser.ID)
    }
    err = db.Debug().Unscoped().Delete(&delUser).Error
    if err != nil {
        return err
    }
    return nil
}

func TestGormCreateDB(t *testing.T) {
    // Cleanup old stuff.
    if err := db.Debug().DropTableIfExists("users").Error; err != nil {
        t.Fatal(err)
    }

    // Create new stuff.
    if err := db.Debug().CreateTable(&User{}).Error; err != nil {
        t.Fatal(err)
    }
}

func TestCreateMoon(t *testing.T) {
    // Create new user in the database.
    u, err := NewDatabaseUser("Keith Moon", "keith.moon@who.com")
    if err != nil {
        t.Error(err)
    } else {
        fmt.Println("created", u)
    }
}

func TestCreateDaltrey(t *testing.T) {
    // Create new user in the database.
    u, err := NewDatabaseUser("Roger Daltrey", "roger.daltrey@who.com")
    if err != nil {
        t.Error(err)
    } else {
        fmt.Println("created", u)
    }
}

func TestDeleteDaltrey(t *testing.T) {
    u := User{Email: "roger.daltrey@who.com"}
    fmt.Println("TestDeleteUser:", u)
    err := u.Delete()
    if err != nil {
        t.Fatalf("delete user with email %s failed: %v\n", u.Email, err)
    }
    fmt.Printf("user with email %s deleted\n", u.Email)
}

0 个答案:

没有答案