go语言web开发系列之十六:gin框架中通过gorm使用事务

一,演示项目的相关信息

1,地址:

https://github.com/liuhongdi/digv16

2,功能:以下订单为例,演示了在gorm中启用事务

3, 项目结构:如图:

说明:刘宏缔的go森林是一个专注golang的博客,
          地址:https://blog.csdn.net/weixin_43881017

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,数据库及sql

1,数据表:

2,建表sql:

  1.  
    CREATE TABLE `m_goods` (
  2.  
    `goodsId` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '商品id',
  3.  
    `goodsName` varchar(200) NOT NULL DEFAULT '' COMMENT '商品名称',
  4.  
    `stock` int unsigned NOT NULL DEFAULT '0' COMMENT '库存数量',
  5.  
    PRIMARY KEY (`goodsId`)
  6.  
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'
  1.  
    CREATE TABLE `m_order` (
  2.  
    `orderId` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id',
  3.  
    `userId` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户id',
  4.  
    `salePrice` decimal(10,0) NOT NULL DEFAULT '0' COMMENT '订单金额',
  5.  
    PRIMARY KEY (`orderId`),
  6.  
    KEY `userId` (`userId`)
  7.  
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单表'
  1.  
    CREATE TABLE `m_order_goods` (
  2.  
    `ogId` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
  3.  
    `orderId` bigint unsigned NOT NULL COMMENT '订单id',
  4.  
    `goodsId` bigint unsigned NOT NULL COMMENT '商品id',
  5.  
    `buyNum` int unsigned NOT NULL COMMENT '购买数量',
  6.  
    PRIMARY KEY (`ogId`),
  7.  
    UNIQUE KEY `orderId_2` (`orderId`,`goodsId`)
  8.  
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单商品表'

3,插入一条演示数据:

  1.  
    INSERT INTO `m_goods` (`goodsId`, `goodsName`, `stock`) VALUES
  2.  
    (1, '施丹兰蜂蜜牛奶手工皂', 10);

三,go代码说明

1,controller/orderController.go

  1.  
    package controller
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "github.com/gin-gonic/gin"
  6.  
    "github.com/liuhongdi/digv16/global"
  7.  
    "github.com/liuhongdi/digv16/service"
  8.  
    )
  9.  
     
  10.  
    type OrderController struct{}
  11.  
     
  12.  
    //result:= global.NewResult(c)
  13.  
     
  14.  
    func NewOrderController() OrderController {
  15.  
    return OrderController{}
  16.  
    }
  17.  
     
  18.  
    //新加一个订单
  19.  
    func (o *OrderController) AddOne(c *gin.Context) {
  20.  
    result := global.NewResult(c)
  21.  
     
  22.  
    goodsId:=1;
  23.  
    buyNum:=11;
  24.  
     
  25.  
    orderOne,err := service.AddOneOrder(int64(goodsId),buyNum);
  26.  
    if err != nil {
  27.  
    result.Error(404,"数据处理错误")
  28.  
    } else {
  29.  
    result.Success(&orderOne);
  30.  
    }
  31.  
    return
  32.  
    }
  33.  
     
  34.  
     
  35.  
    //新加一个订单,使用tx
  36.  
    func (o *OrderController) AddOneTx(c *gin.Context) {
  37.  
    result := global.NewResult(c)
  38.  
    goodsId:=1;
  39.  
    buyNum:=11;
  40.  
     
  41.  
    orderOne,err := service.AddOneOrderTx(int64(goodsId),buyNum);
  42.  
     
  43.  
    fmt.Println("orderOne:");
  44.  
    fmt.Println(orderOne);
  45.  
    fmt.Println(":end:");
  46.  
    //if ()
  47.  
    if (orderOne == nil || err != nil) {
  48.  
    result.Error(20001,"数据处理错误")
  49.  
    } else {
  50.  
    result.Success(&orderOne);
  51.  
    }
  52.  
    return
  53.  
    }

2,service/order.go

  1.  
    package service
  2.  
     
  3.  
    import (
  4.  
    "github.com/liuhongdi/digv16/dao"
  5.  
    "github.com/liuhongdi/digv16/model"
  6.  
    )
  7.  
     
  8.  
    //新加一个订单
  9.  
    func AddOneOrder(goodsId int64,buyNum int) (*model.Order, error) {
  10.  
    return dao.AddOneOrder(goodsId,buyNum)
  11.  
    }
  12.  
     
  13.  
    //新加一个订单,使用tx
  14.  
    func AddOneOrderTx(goodsId int64,buyNum int) (*model.Order, error) {
  15.  
    return dao.AddOneOrderTx(goodsId,buyNum)
  16.  
    }

3,dao/order.go

  1.  
    package dao
  2.  
     
  3.  
    import (
  4.  
    "errors"
  5.  
    "fmt"
  6.  
    "github.com/liuhongdi/digv16/global"
  7.  
    "github.com/liuhongdi/digv16/model"
  8.  
    "gorm.io/gorm"
  9.  
    )
  10.  
     
  11.  
    //添加一个订单
  12.  
    func AddOneOrder(goodsId int64,buyNum int) (*model.Order, error) {
  13.  
    //添加order
  14.  
    order := model.Order{UserId: 1, SalePrice: "20.00"}
  15.  
    resultCO := global.DBLink.Create(&order) // 通过数据的指针来创建
  16.  
    if (resultCO.Error != nil) {
  17.  
    return nil,resultCO.Error
  18.  
    }
  19.  
    //减库存
  20.  
    result := global.DBLink.Debug().Table("m_goods").Where("goodsId = ? and stock >= ?", goodsId,buyNum).Update("stock", gorm.Expr("stock - ?", buyNum))
  21.  
    if (result.Error != nil) {
  22.  
    return nil,result.Error
  23.  
    }
  24.  
    if (result.RowsAffected <= 0){
  25.  
    return nil,errors.New("减库存失败")
  26.  
    }
  27.  
    //添加订单商品
  28.  
    orderId := order.OrderId;
  29.  
    orderGoods := model.OrderGoods{OrderId:orderId,GoodsId:goodsId,BuyNum:buyNum}
  30.  
    resultCOG := global.DBLink.Create(&orderGoods) // 通过数据的指针来创建
  31.  
    if (resultCOG.Error != nil) {
  32.  
    return nil,resultCOG.Error
  33.  
    }
  34.  
    //return
  35.  
    return &order,nil
  36.  
    }
  37.  
     
  38.  
     
  39.  
    //添加一个订单
  40.  
    func AddOneOrderTx(goodsId int64,buyNum int) (*model.Order, error) {
  41.  
    // 事务开始后,需要使用 tx 处理数据
  42.  
    tx := global.DBLink.Begin()
  43.  
     
  44.  
    defer func() {
  45.  
    if r := recover(); r != nil {
  46.  
    fmt.Println("this is in defer recover")
  47.  
    tx.Rollback()
  48.  
    }
  49.  
    }()
  50.  
     
  51.  
    if err := tx.Error; err != nil {
  52.  
    return nil,err
  53.  
    }
  54.  
     
  55.  
    //添加order
  56.  
    order := model.Order{UserId: 1, SalePrice: "20.00"}
  57.  
    resultCO := tx.Debug().Create(&order) // 通过数据的指针来创建
  58.  
    if (resultCO.Error != nil) {
  59.  
    tx.Rollback()
  60.  
    return nil,resultCO.Error
  61.  
    }
  62.  
     
  63.  
    /*
  64.  
    var z int = 0
  65.  
    var i int = 100 / z
  66.  
    fmt.Println("i:%i",i)
  67.  
    */
  68.  
    //减库存
  69.  
    result := tx.Debug().Table("m_goods").Where("goodsId = ? and stock >= ?", goodsId,buyNum).Update("stock", gorm.Expr("stock - ?", buyNum))
  70.  
    if (result.Error != nil) {
  71.  
    tx.Rollback()
  72.  
    return nil,result.Error
  73.  
    }
  74.  
    if (result.RowsAffected <= 0){
  75.  
    tx.Rollback()
  76.  
    fmt.Println("减库存失败")
  77.  
    return nil,errors.New("减库存失败")
  78.  
    }
  79.  
    //添加订单商品
  80.  
    orderId := order.OrderId;
  81.  
    orderGoods := model.OrderGoods{OrderId:orderId,GoodsId:goodsId,BuyNum:buyNum}
  82.  
    resultCOG := tx.Debug().Create(&orderGoods) // 通过数据的指针来创建
  83.  
    if (resultCOG.Error != nil) {
  84.  
    tx.Rollback()
  85.  
    return nil,resultCOG.Error
  86.  
    }
  87.  
    //commit
  88.  
    fmt.Println("begin commit")
  89.  
    errCM := tx.Commit().Error
  90.  
    if (errCM != nil) {
  91.  
    fmt.Println("begin return1")
  92.  
    tx.Rollback()
  93.  
    return nil,errCM
  94.  
    }else {
  95.  
    fmt.Println("begin return2")
  96.  
    return &order,nil
  97.  
    }
  98.  
    }

4,model/goods.go

  1.  
    package model
  2.  
     
  3.  
    type Goods struct {
  4.  
    GoodsId int64 `gorm:"column:goodsId",json:"goodsId"` // 自增
  5.  
    GoodsName string `gorm:"column:goodsName",json:"goodsName"` // 用户id
  6.  
    Stock int `gorm:"column:stock",json:"stock"` // 售价
  7.  
    }
  8.  
     
  9.  
    func (Goods) TableName() string {
  10.  
    return "m_goods"
  11.  
    }

5,model/order.go

  1.  
    package model
  2.  
     
  3.  
    type Order struct {
  4.  
    OrderId int64 `gorm:"primaryKey;autoIncrement;column:orderId",json:"orderid"` // 自增
  5.  
    UserId int64 `gorm:"column:userId",json:"userid"` // 用户id
  6.  
    SalePrice string `gorm:"column:salePrice",json:"saleprice"` // 售价
  7.  
    }
  8.  
     
  9.  
    func (Order) TableName() string {
  10.  
    return "m_order"
  11.  
    }

6,router/router.go

  1.  
    package router
  2.  
     
  3.  
    import (
  4.  
    "github.com/gin-gonic/gin"
  5.  
    "github.com/liuhongdi/digv16/controller"
  6.  
    "github.com/liuhongdi/digv16/global"
  7.  
    "log"
  8.  
    "runtime/debug"
  9.  
    )
  10.  
     
  11.  
    func Router() *gin.Engine {
  12.  
    router := gin.Default()
  13.  
    //处理异常
  14.  
    router.NoRoute(HandleNotFound)
  15.  
    router.NoMethod(HandleNotFound)
  16.  
    router.Use(Recover)
  17.  
     
  18.  
    // 路径映射
  19.  
    goodsc:=controller.NewGoodsController()
  20.  
    router.GET("/goods/getone/:id", goodsc.GetOne);
  21.  
    //router.GET("/article/list", articlec.GetList);
  22.  
     
  23.  
    orderc:=controller.NewOrderController()
  24.  
    router.GET("/order/addone", orderc.AddOne);
  25.  
    router.GET("/order/addonetx", orderc.AddOneTx);
  26.  
     
  27.  
    return router
  28.  
    }
  29.  
     
  30.  
    func HandleNotFound(c *gin.Context) {
  31.  
    global.NewResult(c).Error(404,"资源未找到")
  32.  
    return
  33.  
    }
  34.  
     
  35.  
    func Recover(c *gin.Context) {
  36.  
    defer func() {
  37.  
    if r := recover(); r != nil {
  38.  
    //打印错误堆栈信息
  39.  
    log.Printf("panic: %v ", r)
  40.  
    debug.PrintStack()
  41.  
    global.NewResult(c).Error(500,"服务器内部错误")
  42.  
    }
  43.  
    }()
  44.  
    //加载完 defer recover,继续后续接口调用
  45.  
    c.Next()
  46.  
    }

7,其他相关代码可访问github查看

四,测试效果

1,测试除0错:

访问:

http://127.0.0.1:8080/order/addonetx

   返回:

2,测试扣减库存出错

把dao/order.go中,

AddOneOrderTx方法中的除0错代码注释掉

  1.  
    /*
  2.  
    var z int = 0
  3.  
    var i int = 100 / z
  4.  
    fmt.Println("i:%i",i)
  5.  
    */

再次执行:访问:

http://127.0.0.1:8080/order/addonetx

可以从控制台看到输出的提示:

  1.  
    2021/01/13 13:32:25 /data/liuhongdi/digv16/dao/order.go:69
  2.  
    [2.219ms] [rows:0] UPDATE `m_goods` SET `stock`=stock - 11 WHERE goodsId = 1 and stock >= 11
  3.  
    减库存失败

3,测试正常执行事务成功

把controller/orderController.go中,

AddOneTx方法,把buyNum的值改为9

buyNum:=9

再次执行:

http://127.0.0.1:8080/order/addonetx

返回:

事务执行已成功

五,使用的库的版本:

  1.  
    module github.com/liuhongdi/digv16
  2.  
     
  3.  
    go 1.15
  4.  
     
  5.  
    require (
  6.  
    github.com/gin-gonic/gin v1.6.3
  7.  
    github.com/jinzhu/gorm v1.9.16 // indirect
  8.  
    gorm.io/driver/mysql v1.0.1
  9.  
    gorm.io/gorm v1.20.6
  10.  
    )
原文地址:https://www.cnblogs.com/ExMan/p/14312276.html