Golang中Sqlx事务处理指南

Golang中Sqlx事务处理指南 你好,

我正在使用 sqlx 向具有外键关系的 MySQL 数据库表中插入多行数据。能否请您审阅下面的代码?我不太确定应该在何处使用回滚。这是一个好的做法吗?

tx, err := db.Begin()
if err != nil {
	return
}

r, err := tx.Exec("INSERT INTO licenses ...")
if err != nil {
	tx.Rollback() // 我应该在这里使用回滚吗?
	return
}
licenseId, _ := r.LastInsertId()

r, err = tx.Exec("INSERT INTO companies ...", licenseId, ...)
if err != nil {
	tx.Rollback()
	return
}
companyId, _ := r.LastInsertId()

r, err = tx.Exec("INSERT INTO users ...", companyId, ...)
if err != nil {
	tx.Rollback()
	return
}

err = tx.Commit()
if err != nil {
	tx.Rollback() // 我应该在这里使用回滚吗?
	return
}

此致敬礼…


更多关于Golang中Sqlx事务处理指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

谢谢!

更多关于Golang中Sqlx事务处理指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


为提交和回滚编写defer函数

你好,欢迎,

我不知道这个问题的答案 😊

如果三条 INSERT 语句必须全部成功,或者都不产生任何效果,那么必须在每次出现错误时回滚事务,正如您所做的那样。

ermanimer:

LastInsertId

如果你使用的是UUID而不是ID会怎样?因为 r.LastInsertId() 返回的是 int64 类型。

func main() {
    fmt.Println("hello world")
}

这是一个正确的事务处理模式,但可以进一步优化。以下是改进后的代码示例:

tx, err := db.Begin()
if err != nil {
    return err
}
defer func() {
    if p := recover(); p != nil {
        tx.Rollback()
        panic(p)
    } else if err != nil {
        tx.Rollback()
    } else {
        err = tx.Commit()
        if err != nil {
            tx.Rollback()
        }
    }
}()

r, err := tx.Exec("INSERT INTO licenses (name) VALUES (?)", "license1")
if err != nil {
    return err
}
licenseId, err := r.LastInsertId()
if err != nil {
    return err
}

r, err = tx.Exec("INSERT INTO companies (license_id, name) VALUES (?, ?)", licenseId, "company1")
if err != nil {
    return err
}
companyId, err := r.LastInsertId()
if err != nil {
    return err
}

_, err = tx.Exec("INSERT INTO users (company_id, name) VALUES (?, ?)", companyId, "user1")
if err != nil {
    return err
}

return nil

关键改进:

  1. 使用 defer 确保事务始终被正确处理
  2. 处理 LastInsertId() 的错误
  3. 添加 panic 恢复机制
  4. 统一错误处理路径

对于 sqlx 的特定用法,可以使用命名参数:

type License struct {
    Name string `db:"name"`
}

type Company struct {
    LicenseID int64  `db:"license_id"`
    Name      string `db:"name"`
}

type User struct {
    CompanyID int64  `db:"company_id"`
    Name      string `db:"name"`
}

tx, err := db.Beginx()
if err != nil {
    return err
}
defer func() {
    if err != nil {
        tx.Rollback()
    } else {
        err = tx.Commit()
    }
}()

license := License{Name: "license1"}
result, err := tx.NamedExec("INSERT INTO licenses (name) VALUES (:name)", license)
if err != nil {
    return err
}

licenseId, err := result.LastInsertId()
if err != nil {
    return err
}

company := Company{LicenseID: licenseId, Name: "company1"}
result, err = tx.NamedExec("INSERT INTO companies (license_id, name) VALUES (:license_id, :name)", company)
if err != nil {
    return err
}

companyId, err := result.LastInsertId()
if err != nil {
    return err
}

user := User{CompanyID: companyId, Name: "user1"}
_, err = tx.NamedExec("INSERT INTO users (company_id, name) VALUES (:company_id, :name)", user)
if err != nil {
    return err
}

return nil

这种模式确保:

  • 任何错误都会触发回滚
  • Commit 失败也会触发回滚
  • 代码更简洁,错误处理更一致
  • 使用 sqlx 的特性提高可读性
回到顶部