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

news2024/12/24 8:12:24

整理下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))
	}
}()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法刷题记录2

4.图 4.1.被围绕的区域 思路:图中只有与边界上联通的O才不算是被X包围。因此本题就是从边界上的O开始递归,找与边界O联通的O,并标记为#(代表已遍历),最后图中剩下的O就是:被X包围的O。图中所有…

使用LVGL提升交互效率:基于启明智显Model3A方案的7寸智能屏用户界面(UI)设计介绍

项目概述: 【启明智显】,作为一家专注于HMI和AIoT解决方案的公司,我们致力于为用户提供创新、可靠且高效的产品和解决方案。近日我们推出了高性能及高性价比的HMI芯片——Model3A。芯片搭载了强大的2D图形加速引擎,能够提供高达7…

RNN知识体系构筑:详尽阐述其理论基础、技术架构及其在处理序列数据挑战中的创新应用

一、为什么需要RNN 尽管神经网络被视为一种强大且理论上能够近似任何连续函数的模型,尤其当训练数据充足时,它们能够在输入空间中的某个点( x )映射到输出空间的特定值( y ),然而,这并不能完全解释为何在众多应用场景中&#xff…

基于Spring Boot的新生宿舍管理系统设计与开发

基于Spring Boot的新生宿舍管理系统设计与开发 开发语言:Java框架:springbootJDK版本:JDK1.8数据库工具:Navicat11开发软件:eclipse/myeclipse/idea 系统部分展示 报修处理管理界面图,在报修处理管理页面…

QA测试开发工程师面试题满分问答16: 微信输入框如何设计测试用例?

可以涵盖基础功能、用户交互、编辑撤回、权限相关、网络信号、异常、并发性能和安全性等方面的测试用例: 基础功能: 验证输入框是否能够接收用户输入,并显示输入的文字。检查输入框是否支持常见的字符输入,如字母、数字、标点符号…

Unity Shader 流光 边缘光

前言 Unity2021.3.23 一、实现原理 Time控制UV的变化,再采样一张流光贴图.即可实现流光效果。 二、效果及源码展示 1.流光效果 效果描述: 1.边缘光(菲尼尔), 2.从上到下扫描光. 效果图如下: 代码如下: Shader "Unlit/ScanCode" {Properties{_MainTe…

【数据结构】树与二叉树、树与森林部分习题与算法设计例题

目录 【数据结构】树与二叉树部分习题与算法设计例题一、单选题二、算法设计题判断二叉树是否为完全二叉树求二叉树的最小深度 以及 二叉树树高 树与二叉树知识点文章: 【数据结构】树与二叉树(递归法先序、中序、后序、层次遍历二叉树、二叉树的建立以及求树高的方…

必看——通配符SSL证书在线免费申请方法!

申请通配符SSL证书,就相当于给你的网站及所有子域名都戴上同一顶加密“帽子”,保护它们的安全通信。以下是三步搞定的方法: 第一步:找权威机构 就像你要找个官方认证的地方办证明一样,先选一家靠谱的证书颁发机构&…

软考中级网络工程师-2024上岸宝典

1.软考是什么 简单说就是计算机技术 相关的国家级证书考试,想听专业点给大家截一张官网的图,不想听废话直接往下。 同为国家级证书的:注册会计师、法律职业资格证、一级建筑师,证书的价值是比较高的。 很多人都是在求职前或者大…

产废端实时音视频监控系统在运输车辆驾驶室中的应用

实时音视频监控系统可通过在运输车辆驾驶室安装音视频摄录设备,实现将运输车辆内部及周围环境音视频数据通过移动网络实时回传指挥中心的功能。 前端摄录设备主要负责采集车内外的视音频信息,为了保障车辆及运输人员 的安全,应合理选择摄录设…

探索半导体测试领域:哲讯TCC智能化管理系统的应用与优势

在半导体行业中,封装和测试环节是至关重要的一环。半导体封装测试是指将通过测试的晶圆按照产品型号及功能需求加工得到独立芯片的过程。半导体封测包括封装和测试两个环节,封装是保护芯片免受物理、化学等环境因素造成的损伤,增强芯片的散热…

node.js-模块化

定义:CommonJS模块是为Node.js打包Javascript代码的原始方式。Node.js还支持浏览器和其他Javascript运行时使用的ECMAScript模块标准。 在Node.js中,每个文件都被视为一个单独的模块。 概念:项目是由很多个模块文件组成的 好处&#xff1a…

一文读懂电阻并联电路和串联电路的特性

电阻并联电路是最基本的并联电路,所有的电路都可以转化为电阻串联电路和电阻并联电路来了解其工作原理。并联电路和串联电路具有完全不同的特性。它们是完全不同的电路,不能相互等效(电阻并联电路图)。 串联电路 特点与特性&…

教你三招,玩转AI通用大模型ChatGPT

工欲善其事必先利其器,想要高效的用好ChatGPT,首先,让我们从如何与它进行有效的对话开始。要知道,ChatGPT并非简单的问答机器,而是一个可以通过交互学习和适应的智能体。那么,如何让ChatGPT来更好地理解我们…

用云手机运营TikTok有什么好处?

在数字化浪潮的推动下,社交媒体平台正重塑商业推广与品牌建设的面貌。TikTok,这款全球热门的短视频应用,已经吸引了亿万用户的瞩目。对于出海电商和品牌推广而言,借助云手机运营TikTok,能够解锁更多潜在可能&#xff0…

短视频素材哪个网站好?8个视频素材下载免费网站

在视频制作的宏大舞台上,寻找恰到好处的素材是每位创作者的日常挑战。高清、无水印的视频素材不仅能够让你的作品焕发光彩,还能在讲述故事时增添无限动力。除了蛙学府,这里汇集了世界各地的视频素材网站,每个都带有独特的魅力&…

数据结构(七)——散列表

7.5.1 散列表的基本概念 散列表(哈希表,Hash Table)︰是一种数据结构。特点是∶可以根据数据元素的关键字计算出它在散列表中的存储地址 散列函数(哈希函数)︰AddrH(key)建立了“关键字”→“存储地址”的映射关系 冲突(碰撞)︰在…

【Linux】地址空间虚拟地址

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 虚拟地址1.1 虚拟地址引入1.2 虚拟地址理解1.3 虚拟地址细节问题 2. 地址空间2.1 理解地址空间2.2 页表和写时拷贝 3. 进程调度 1. 虚拟地址 1.1 虚拟地址引入 先先来一个测试代码&#xff1a; 1 #include<st…

JMM与内存屏障

一、cpu多核并发缓存架构解析 JMM内存模型&#xff1a;java多线程内存模型跟cpu缓存模型类似&#xff0c;是基于cpu缓存模型来建立的&#xff0c;java线程内存模型是标准化的&#xff0c;屏蔽掉了底层不同计算机的区别 JMM数据原子操作 read(读取)&#xff1a;从主内存读取数据…

asp.net core 依赖注入后的服务生命周期

ASP.NET Core 依赖注入&#xff08;DI&#xff09;容器支持三种服务的生命周期选项&#xff0c;它们定义了服务实例的创建和销毁的时机。理解这三种生命周期对于设计健壯且高效的应用程序非常重要&#xff1a; 瞬时&#xff08;Transient&#xff09;&#xff1a; 瞬时服务每次…