事务

func Transaction(db *gorm.DB) error {
    // 为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。
    db = db.Session(&gorm.Session{SkipDefaultTransaction: true}) //临时禁用默认事务

    tx := db.Begin() // 开始事务

    defer func() {
        if err := recover(); err != nil {
            tx.Rollback() //手动回滚
        }
    }()

    if err := tx.Error; err != nil {
        return err
    }

    user := User{UserId: rand.IntN(100000), Degree: "本科", Gender: "男", City: "上海"}
    fmt.Printf("uid=%d\n", user.UserId)
    if err := tx.Create(&user).Error; err != nil {
        tx.Rollback() //手动回滚
        fmt.Println("第一次Create回滚")
        return err
    }

    user.Id = 0
    if err := tx.Create(&user).Error; err != nil { //第二次会失败,因为uid重复了
        tx.Rollback() //手动回滚
        fmt.Println("第二次Create回滚")
        return err
    }

    return tx.Commit().Error //提交事务。Commit和Rollback只能执行一个,且只能执行一次
}

执行原生sql

// 执行原生的select语句
func RawSelect(db *gorm.DB) {
    var users []User
    db.Raw("select id,uid,city from user where id>? and uid>? limit 3", 2, 4).Scan(&users)
    for _, user := range users {
        fmt.Printf("uid %d city %s\n", user.UserId, user.City)
    }

    fmt.Println()
    rows, err := db.Raw("select id,uid,city from user where id>? and uid>? limit 3", 2, 4).Rows()
    if err == nil {
        defer rows.Close()
        var id, uid int
        var city string
        for rows.Next() {
            rows.Scan(&id, &uid, &city)
            fmt.Printf("uid %d city %s\n", uid, city)
        }
    }
}

// 执行原生的update、insert、delete语句
func RawExec(db *gorm.DB) {
    tx := db.Exec("update user set degree=? where id=?", "大专", 30)
    fmt.Printf("更新了%d行\n", tx.RowsAffected)
}

错误处理

    var user User
    db.First(&user)

    tx := db.Create(&user)
    if tx.Error != nil {
        //获得MySQL错误码,每一个code都对应一种特定的错误
        if mysqlErr, ok := tx.Error.(*mysql.MySQLError); ok { //接口的类型断言
            switch mysqlErr.Number { //针对不同的错误码,采取不同的处理方案
            case 1:
                //...
            case 2:
                //...
            default:
                fmt.Println("mysql error", "code", mysqlErr.Number, "msg", mysqlErr.Message)
            }
        } else {
            fmt.Printf("err %#v\n", tx.Error)
        }
    }

或者使用errors.As,跟接口断言是等价的

    if tx.Error != nil {
        var mysqlErr *mysql.MySQLError //必须是指针,因为是指针实现了error接口
        if errors.As(tx.Error, &mysqlErr) {
            switch mysqlErr.Number { //针对不同的错误码,采取不同的处理方案
            case 1:
                //...
            case 2:
                //...
            default:
                fmt.Println("mysql error", "code", mysqlErr.Number, "msg", mysqlErr.Message)
            }
        } else {
            fmt.Printf("err %#v\n", tx.Error)
        }
    }