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()
  • 用事务实现 用户转账 功能

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

相关推荐
IT_陈寒3 分钟前
Vue的响应式让我加班到凌晨,问题竟出在这个不起眼的地方
前端·人工智能·后端
乱码三千15 分钟前
使用 Docker-compose 搭建SRS直播中转服务端
后端·架构·github
y1su15 分钟前
【Leetcode】1477. 找两个和为目标值且不重叠的子数组
数据结构·后端·算法·leetcode·职场和发展
愛芳芳31 分钟前
Swagger2 多环境动态配置实战指南
java·后端·maven·springboot·swagger
JaguarJack1 小时前
PHP 8.6 只是个小版本,三十项弃用却已在为 PHP 9.0 铺路
后端·php·服务端
BingoGo1 小时前
PHP 8.6 只是个小版本,三十项弃用却已在为 PHP 9.0 铺路
后端·php
Bs_MoneyMagnet5 小时前
基于springboot+vue的滑雪场票务与装备租赁系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
AlienZHOU9 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
狗头大军之江苏分军11 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术12 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端