回滚不适用于 Go 语言事务包装器

我最近开始学习围棋。


我发现了以下用于数据库事务处理的包装器的 Github 实现,并决定尝试一下。


(来源)https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/transaction/wrapper/main.go


我使用 PostgreSQL 作为数据库。


最初,它包含以下数据。


testdb=> select * from products;

 product_id | price

------------+-------

 0001       |   200

 0002       |   100

 0003       |   150

 0004       |   300

(4 rows)

进程A成功后,故意让进程B失败,期望事务A回滚。然而,当我们运行它时,回滚并没有发生,我们最终得到以下结果


实际上,由于 B 失败,进程 A 应该回滚,数据库值应该没有变化。


我已经在一些地方插入了日志来确认这一点,但我不确定。为什么回滚没有执行?


慕勒3428872
浏览 67回答 1
1回答

红糖糍粑

正如@Richard Huxton 所说,传递tx给一个函数f以下是步骤:添加一个字段struct txAdmin以容纳*sql.Tx,所以txAdmin有DB和Tx字段里面Transaction设置tx为*txAdmin.Tx每个查询的内部UpdateProduct使用*Service.tx.Tx所以最后的代码是这样的:package mainimport (    "context"    "database/sql"    "fmt"    "log"    _ "github.com/jackc/pgx/v4/stdlib")// transaction-wrapper-starttype txAdmin struct {    *sql.DB    *sql.Tx}type Service struct {    tx txAdmin}func (t *txAdmin) Transaction(ctx context.Context, f func(ctx context.Context) (err error)) error {    log.Printf("transaction")    tx, err := t.DB.BeginTx(ctx, nil)    if err != nil {        return err    }    // set tx to Tx    t.Tx = tx    defer tx.Rollback()    if err := f(ctx); err != nil {        log.Printf("transaction err")        return fmt.Errorf("transaction query failed: %w", err)    }    log.Printf("commit")    return tx.Commit()}func (s *Service) UpdateProduct(ctx context.Context, productID string) error {    updateFunc := func(ctx context.Context) error {        log.Printf("first process")        // Process A        if _, err := s.tx.Tx.ExecContext(ctx, "UPDATE products SET price = 200 WHERE product_id = $1", productID); err != nil {            log.Printf("first err")            return err        }        log.Printf("second process")        // Process B(They are intentionally failing.)        if _, err := s.tx.Tx.ExecContext(ctx, "...", productID); err != nil {            log.Printf("second err")            return err        }        return nil    }    log.Printf("update")    return s.tx.Transaction(ctx, updateFunc)}// transaction-wrapper-endfunc main() {    data, err := sql.Open("pgx", "host=localhost port=5432 user=testuser dbname=testdb password=password sslmode=disable")    if nil != err {        log.Fatal(err)    }    database := Service{tx: txAdmin{DB: data}}    ctx := context.Background()    database.UpdateProduct(ctx, "0004")}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go