Go语言实战案例-数据库事务处理

在实际业务中,很多操作需要保证 要么全部成功,要么全部失败,否则可能造成数据不一致。比如:

  • 用户转账(A 账户扣款,B 账户加款)
  • 下单支付(生成订单、扣减库存、记录支付)

这种场景就需要用到 事务(Transaction)

本文将带你使用 Go 语言 + GORM 实现数据库事务处理。


一、事务的基本概念

事务(Transaction)是数据库中一组不可分割的操作单元,具有四大特性(ACID):

  • 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败。
  • 一致性(Consistency):事务执行前后,数据必须保持一致。
  • 隔离性(Isolation):多个事务之间相互独立,互不干扰。
  • 持久性(Durability):事务一旦提交,结果会永久保存。

二、GORM 事务处理方式

GORM 提供了三种事务使用方式:

  1. 手动开启、提交、回滚
  2. db.Transaction() 包裹函数(推荐)
  3. 嵌套事务(SavePoint / RollbackTo)

三、实战案例:用户转账

我们以一个"用户转账"的例子来演示事务操作。

1. 定义模型

go 复制代码
package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
)

type User struct {
	ID      uint   `gorm:"primaryKey"`
	Name    string `gorm:"size:100"`
	Balance int    // 账户余额
}

var db *gorm.DB

func initDB() {
	dsn := "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
	var err error
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal("数据库连接失败:", err)
	}
	_ = db.AutoMigrate(&User{})
}

2. 使用事务完成转账

go 复制代码
// Transfer 转账函数:fromID -> toID,金额 amount
func Transfer(fromID, toID uint, amount int) error {
	return db.Transaction(func(tx *gorm.DB) error {
		var from, to User

		// 查询转出账户
		if err := tx.First(&from, fromID).Error; err != nil {
			return err
		}
		if from.Balance < amount {
			return fmt.Errorf("余额不足")
		}

		// 扣减余额
		if err := tx.Model(&from).Update("balance", from.Balance-amount).Error; err != nil {
			return err
		}

		// 查询转入账户
		if err := tx.First(&to, toID).Error; err != nil {
			return err
		}

		// 增加余额
		if err := tx.Model(&to).Update("balance", to.Balance+amount).Error; err != nil {
			return err
		}

		// 所有操作成功 -> 提交事务
		return nil
	})
}

3. 测试转账

go 复制代码
func main() {
	initDB()

	// 初始化两位用户
	db.Create(&User{Name: "Alice", Balance: 100})
	db.Create(&User{Name: "Bob", Balance: 50})

	// 转账:Alice -> Bob 30元
	err := Transfer(1, 2, 30)
	if err != nil {
		log.Println("转账失败:", err)
	} else {
		log.Println("转账成功")
	}

	var users []User
	db.Find(&users)
	log.Println("当前用户余额:", users)
}

四、运行效果

  1. 初始状态:
text 复制代码
Alice: 100
Bob:   50
  1. 转账成功后:
text 复制代码
Alice: 70
Bob:   80
  1. 如果 Alice 余额不足,事务会回滚:
text 复制代码
转账失败: 余额不足
Alice: 100
Bob:   50

五、事务处理注意事项

  1. 事务函数返回 error

    • 返回 nil → 提交事务
    • 返回 error → 回滚事务
  2. 不要在事务中使用全局 db

    • 必须用传入的 tx 对象,确保操作在同一事务内。
  3. 事务适合小而快的操作

    • 长时间事务会占用锁,可能导致性能下降。

六、总结

通过本案例,我们学习了:

  • 事务的基本概念(ACID)
  • GORM 的事务 API(推荐 db.Transaction()
  • 用事务实现 用户转账 功能

事务在业务中非常常见,掌握它能让你的数据库操作更加安全可靠。

相关推荐
前端Hardy30 分钟前
一个时代结束了:npm 终于对 install 脚本下手了
前端·javascript·后端
damaoyou31 分钟前
Cog3DRangeImagePlaneEstimatorTool完全指南
后端
Nturmoils1 小时前
分页别写太顺手,LIMIT 背后还有排序和边界
数据库·后端
神奇小汤圆1 小时前
国产版“Codex”初体验,智谱ZCode很强啊!
后端
站大爷IP1 小时前
Python里的“赋值”到底是什么意思?
后端
鹅城剑仙2 小时前
Spring Boot 微服务架构设计与最佳实践
spring boot·后端·微服务
Full Stack Developme2 小时前
Spring Integration 教程
java·后端·spring
爱勇宝2 小时前
AI 时代,前端工程师的话语权正在下降?
前端·后端
kymjs张涛3 小时前
一个月,纯VibeCoding,全平台云笔记APP
前端·javascript·后端
星辰_mya3 小时前
autowired和resource区别
java·后端·spring·架构·原理