匿名函数与gorm中的Transaction事务方法

整理下go中的匿名函数,项目中很多地方都在用。

1、函数类型的变量

Go中,函数也是一种数据类型。定义一个函数,把这个函数赋值给一个变量,这个变量就是函数类型的变量,用这个变量等价于直接调函数

package main

import "fmt"

//定义一个函数:
func test(num int){
        fmt.Println(num)
}

func main(){
        //函数也是一种数据类型,可以赋值给一个变量	
        a := test	//a变量就是一个函数类型的变量
        fmt.Printf("a的类型是:%T,test函数的类型是:%T \n",a,test)	//a的类型是:func(int),test函数的类型是:func(int)
        //通过该变量可以对函数调用
        a(10) //等价于  test(10)
}

在这里插入图片描述

2、函数类型的形参

有函数类型的变量,那函数类型的形参也可以。对于函数类型的形参,可以选择传入类型相同的函数名称或者变量,也可以用匿名函数直接写逻辑

package main

import "fmt"

//定义一个函数
func test(num int){
        fmt.Println(num)
}

//定义一个函数,把另一个函数作为形参
func test02 (num1 int ,num2 float32, fc func(int)){
        fc(num1)	//调用形参对应的函数
}


func main(){
        //a变量就是一个函数类型的变量
        a := test
        //传入类型相同的函数名称
        test02(1, 3.14, test)
        //传入类型相同的函数类型的变量
        test02(2, 3.14, a)
        //传入一个匿名函数
        test02(3, 3.14, func(num int) {
        	fmt.Println(num)
        })
        
}

在这里插入图片描述

3、gorm中的Transaction方法

gorm框架中的Transaction方法,形参是一个函数类型func(tx *gorm.DB) error,下面直接用匿名函数给Transaction方法传参,匿名函数中自然是拿gorm.DB对象去做一系列的insert操作

// 使用事务执行数据库操作
err := db.Transaction(func(tx *gorm.DB) error {
    // 在事务中执行一系列数据库操作
    if err := tx.Model(&User{}).Create(&User{Name: "John"}).Error; err != nil {
        // 如果其中一个操作失败,则回滚事务
        return err
    }

    if err := tx.Model(&Order{}).Create(&Order{UserID: 1, Product: "Phone"}).Error; err != nil {
        // 如果其中一个操作失败,则回滚事务
        return err
    }

    // 如果所有操作都成功,则提交事务
    return nil
})

if err != nil {
    // 处理事务执行过程中的错误
    log.Println("Transaction failed:", err)
}

db.Transaction 方法开启了一个数据库事务,在事务中,依次执行了两个数据库操作:创建一个新用户和创建一个新订单。如果其中一个操作失败,则事务会回滚,所有之前的操作都会被撤销。如果所有操作都成功,则事务会被提交。

以上写法基本固定,有一步Create失败,然会error,事务回滚,执行到最后,说明前面的一个个Create都成功,return nil,提交事务。

4、Transaction方法源码

// Transaction start a transaction as a block, return error will rollback, otherwise to commit. Transaction executes an
// arbitrary number of commands in fc within a transaction. On success the changes are committed; if an error occurs
// they are rolled back.
func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error) {

	//声明一个变量 panicked,用于标记是否发生了 panic
	panicked := true
	
	//检查当前数据库连接池是否实现了 TxCommitter 接口,并且该接口不为 nil。
	//如果满足条件,说明支持嵌套事务,可以使用保存点进行处理
	if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {
		// nested transaction(嵌套事务)
		//检查是否禁用了嵌套事务。如果未禁用,将创建保存点来实现嵌套事务
		if !db.DisableNestedTransaction {
			//创建一个保存点,命名为函数指针的字符串表示形式,并将错误赋给变量 err
			err = db.SavePoint(fmt.Sprintf("sp%p", fc)).Error
			if err != nil {
				return
			}
			defer func() {
				// 善后处理,如果发生了 panic 或者函数执行过程中出现了错误,将回滚到之前创建的保存点
				if panicked || err != nil {
					db.RollbackTo(fmt.Sprintf("sp%p", fc))
				}
			}()
		}
		//调用传入的函数 fc,并将一个新的 Session 对象作为参数传递给它
		//Session 对象会创建一个新的数据库连接,如果 db.clone 的值为 1,则表示创建一个新的数据库连接
		//数据库连接对象传入给函数类型的变量fc
		//我上面写的匿名函数就被调用
		//开始执行我的一些列Create方法
		//执行完成后,把错误赋给err变量
		//err不为空,自会被defer中的匿名函数回滚
		err = fc(db.Session(&Session{NewDB: db.clone == 1}))
	//当前数据库连接池不支持嵌套事务,或者嵌套事务被禁用
	} else {
		//开始一个新的数据库事务,并将返回的事务赋给变量 tx
		tx := db.Begin(opts...)
		if tx.Error != nil {
			return tx.Error
		}
		
		defer func() {
			// 同样的defer搭配匿名函数,有一步err就回滚
			if panicked || err != nil {
				tx.Rollback()
			}
		}()

		//函数类型的变量fx,调用我传入的匿名函数,进行一系列Create
		if err = fc(tx); err == nil {
			//没发生错误,改标志位为false
			panicked = false
			//事务提交
			return tx.Commit().Error
		}
	}

	panicked = false
	return
}

上面源码中也体现了匿名函数的另一个用途:搭配defer

defer func() {
	// Make sure to rollback when panic, Block error or Commit error
	if panicked || err != nil {
		db.RollbackTo(fmt.Sprintf("sp%p", fc))
	}
}()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/563693.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AD高速板设计--HDMI(笔记)

HDMI的布线要求: 差分线对内误差为5以内,所有的差分线误差在10以内: 进行阻抗匹配需要调整线宽,间距和板层。 四对差分线,控制阻抗为100欧姆;四对单端信号线,控制阻抗为50欧姆。 \] HDMI识别过…

Redis底层数据结构之quicklist

目录 一、概述二、quicklist结构三、quicklistNode结构四、优缺点 上一篇 redis底层数据结构之ziplist 一、概述 QuickList是由多个 ziplist 组成的双向链表,每个 ziplist 存储一定数量的元素。优点:结合了 ziplist 和双向链表的优点,既节省空间&#x…

【数据结构】栈和队列(链表模拟队列)

学习本章节必须具备 单链表的前置知识, 建议提前学习:点击链接学习:单链表各种功能函数 细节 详解 本章节是学习用 单链表模拟队列 1. 单链表实现队列 思路如下 队列:只允许在一端进行插入数据操作,在另一端进行删除数…

查询服务器上所有SQL SERVER数据库中是否包含某个字段,且该字段是否包含某个值

公司有一堆相同类别的客户,每个客户都部署了相同的一套系统,每套系统对应一个相同结构的数据库,昨天老板让查一下手机号码177xxxxx248是属于哪个客户的客户。 我要查的这个号码来自于oa_member表中的phone字段,我需要对所有的数据…

2024年51cto视频如何提取

2024年,对于如何提取51cto网站上的视频,许多人都选择在该平台购买自己所需的学习视频。然而,在51cto网页上观看视频将消耗用户的流量。为了解决这一问题,我开发了名为小白51cto工具的软件,使用户能够轻松将视频下载到本…

【图解计算机网络】网络协议分层解析

网络协议分层解析 网络协议分层应用层传输层网络层数据链路层 TCP/IP分层模型通讯示例 网络协议分层 网络协议分层一共有OSI七层网络协议,TCP/IP四层网络网络协议,还有五层网络协议。 七层由于分层太多过于复杂,实际应用中并没有使用&#x…

解析deb与rpm文件的操作技巧

欢迎来到我的博客,代码的世界里,每一行都是一个故事 解析deb与rpm文件的操作技巧 前言deb文件介绍与操作deb 文件介绍特点和用途在 Debian、Ubuntu 系统中使用 deb 文件进行软件安装和管理安装 deb 文件处理依赖问题更新和卸载使用 APT 进行管理 deb文件…

学习笔记:Vue3(图片明天处理)

文章目录 1.概述1.1定义1.2特性1.3组合式API 2.基本用例-项目搭建3.项目目录介绍3.1概述3.2查看文件 4.组合式API4.1概述4.2新的API风格4.2.1概述4.2.2写法4.2.3基本用例-Setup选项使用4.2.4基本用例-语法糖写法(重点)4.2.5执行时机4.2.6代码特点 4.3响应…

C++从入门到精通——模板

模板 前言一、泛型编程二、函数模板函数模板的概念函数模板格式示例 函数模板的原理函数模板的实例化隐式实例化显式实例化示例 auto做模板函数的返回值模板参数的匹配原则总结 三、类模板类模板的定义格式类模板的实例化 前言 C模板是C语言中的一种泛型编程技术,可…

《星尘传说》游戏完整源码(源码+引擎+客户端+服务端+教程+工具),云盘下载

《星尘传说》是一款奇幻类大型多人在线角色扮演电脑客户端游戏,该游戏设置有两大阵营,六个国家以及22个职业,采用3D卡通风格, 有兴趣的,可以架设个外网,让大家一起玩。 《星尘传说》游戏完整源码&#xff0…

采用分治法求含n个实数序列中的最大元素和次大元素(C语言)

目录 实验内容: 实验过程: 1.算法设计 2.程序清单 3.复杂度分析 4.运行结果 实验内容: 设计一个程序,采用分治法求含n个实数序列中的最大元素和次大元素,并分析算法的时间复杂度。 实验过程: 1.算法…

如何增强Java GCExcel API 的导入和导出性能

前言 GrapeCity Documents for Excel (以下简称GcExcel) 是葡萄城公司的一款服务端表格组件,它提供了一组全面的 API 以编程方式生成 Excel (XLSX) 电子表格文档的功能,支持为多个平台创建、操作、转换和共享与 Microsoft Excel 兼容的电子表格&#xf…

[计算机效率] 网站推荐:图片编辑类

4.4 图片编辑类 在数字化时代,图片编辑已成为我们生活和工作中不可或缺的一部分。为了帮助大家更高效、更专业地进行图片编辑,这里推荐一系列优质的在线图片编辑网站。 这些网站不仅拥有直观易用的操作界面,更提供了丰富的编辑功能和素材资源…

jenkins 部署 vue 项目

jenkins 部署 vue 项目 环境 系统:CentOS7.9 Jenkins:最新LTS版本 nginx: 1.24.x gitLab: 打包机:jenkins所在服务器 目标机器:nginx所在服务器 jenkins部署配置 关键脚本 #node -v #已经安装node_module就无需执行install安…

快排非递归与计数排序

感谢大佬的光临各位,希望和大家一起进步,望得到你的三连,互三支持,一起进步 个人主页:LaNzikinh-CSDN博客 收入专栏:初阶数据结构_LaNzikinh篮子的博客-CSDN博客 文章目录 前言一.快速排序非递归二.数据结构栈与内存栈…

【埋点探针】微信小程序SDK安装

一、下载微信小程序SDK埋点代码 选择Wechat,复制sdk代码 在项目根目录下,创建sdk文件,webfunny.event.js 二、在app.js文件中,引入埋点SDK代码 首先引入sdk代码 require("./webfunny.event.js")引入兼容代码&#x…

职业技能鉴定服务中心(新闻系统+证书查询系统)

后端采用ThinkPHP8,最新tp框架 前端采用divcss布局 数据库采用MySQL 采用三种技术实现新闻系统和证书查询系统 源码:git clone https://gitee.com/3539949703/certificate-website.git 效果图如下:

一套在线画图工具(突突图 Procviz)

突突图(Procviz)是一款面向跨平台作图平台。支持流程图、思维导图、框架图、组织架构图、ER图、网络拓扑图等。实现了多团体同时协作,实时同步,解决跨地域合作作图的问题。平台提供了丰富的模板和素材库,轻松完成作图,效率翻倍。 …

docker pull速度慢解决办法

在使用 Docker 时遇到拉取镜像速度慢的问题,可以使用国内的镜像源可以提高下载速度。 使用阿里镜像加速器 Docker 配置文件位于 /etc/docker/daemon.json。如果文件不存在,可以手动创建它。将以下内容添加到配置文件中: 整体复制执行命令&…

【设计模式】单例模式|最常用的设计模式

写在前面 单例模式是最常用的设计模式之一,虽然简单,但是还是有一些小坑点需要注意。本文介绍单例模式并使用go语言实现一遍单例模式。 单例模式介绍 简介 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。 使用场景&#…