IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Go-GORM操作mysql -> 正文阅读

[大数据]Go-GORM操作mysql

Go-GORM操作mysql



请添加图片描述

前言

记录gorm的一些基本用法,使用原生的话操作比较繁琐,使用gorm的话可以让自己减少反复的写一些sql语句

gorm的一些特性

  • 全功能的ORM
  • 支持事务、嵌套事务
  • 支持批量的删除
  • 自定义 Logger
  • 比较稳定
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • ……

提示:以下是本篇文章正文内容,下面案例可供参考

一、数据库连接?

package main

import (
	"fmt"
	"sync"

	"github.com/sirupsen/logrus"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var (
	db       *gorm.DB
	linkOnce sync.Once
)

// 用于连接数据库
func linkDb() {
	linkOnce.Do(func() {
		dsn := "root:root@tcp(localhost:3306)/my_sql?charset=utf8mb4&parseTime=True&loc=Local"
		var err error
		db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
		if err != nil {
			panic(err)
		}
	})
}

func init() {
	linkDb()
}
  • 连接mysql,并且用全局变量进行保存因为后面大量的操作需要用到db
  • sync.Once 保证全局唯一连接

二、为数据库操作添加日志记录

配置logrus,并设定文件的创建的函数,这个与另一篇gin日志中间件的几乎一样就不再详细讲解。

package main

import (
	"fmt"
	"os"
	"path"

	"github.com/sirupsen/logrus"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

type MyWriter struct {
	mlog *logrus.Logger
}

// 初始化一个 MyWriter
func NewMyWriter() *MyWriter {
	logger := logrus.New()
	logFilePath := ""
	if dir, err := os.Getwd(); err == nil {
		logFilePath = dir + "/logs/"
	}
	// 创建日志文件夹
	createFolder(logFilePath)
	filenameFormat := time.Now().Format("2006-01-02")
	src := createLogFile(logFilePath, filenameFormat)

	logger.Out = src
	logger.SetLevel(logrus.DebugLevel)

	logger.SetFormatter(&logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})

	return &MyWriter{mlog: logger}
}

//实现gorm/logger.Writer接口
func (m *MyWriter) Printf(format string, v ...interface{}) {
	logstr := fmt.Sprintf(format, v...)
	//利用loggus记录日志
	m.mlog.Info(logstr)
}

// 检查并且创建日志文件夹
func createFolder(logFilePath string) {
	if err := os.MkdirAll(logFilePath, 0777); err != nil {
		fmt.Println("文件夹创建失败")
		panic(err)
	}
}

// |创建| 打开 日志文件
func createLogFile(logFilePath, filenameFormat string) *os.File {
	logFileName := filenameFormat + ".log"
	fileName := path.Join(logFilePath, logFileName)

	// 检查是否能够成功创建日志文件
	checkFile := func(filename string) {
		// 以时间去命名日志文件
		// 先去判断文件名字是否合法
		if _, err := os.Stat(filename); err != nil {
			if _, err := os.Create(filename); err != nil {
				fmt.Println("打开文件失败")
				panic(err)
			}
		}
	}
	checkFile(fileName)
	src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
	if err != nil {
		panic(err)
	}
	return src
}

修改上述数据库的链接,添加一个类似于中间件的东西

// 用于连接数据库
func linkDb() {
	linkOnce.Do(func() {
		newLogger := logger.New(
			NewMyWriter(), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
			logger.Config{
				SlowThreshold:             time.Second, // 慢 SQL 阈值
				LogLevel:                  logger.Info, // 日志级别
				IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
				Colorful:                  false,       // 禁用彩色打印
			},
		)

		dsn := "root:ywh@tcp(121.41.27.5:3306)/my_sql?charset=utf8mb4&parseTime=True&loc=Local"
		var err error
		db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
			Logger: newLogger,
		})
		if err != nil {
			panic(err)
		}
	})
}

**日志记录的结果
在这里插入图片描述


3. 数据库的基本操作

有了上述的数据库连接以及日志记录的基础,下面当然是一些增删改查以及事务的基本操作啦。

首先我们声明一个结构体Product, 后面的操作都是基于这个结构体的

type Product struct {
	// gorm.Model    // 结构体的内嵌,使用的话会默认给你增加几个字段 有
	Code  string `gorm:"primarykey"`
	Price int    `gorm:"price"`
}

// 对结构体添加 TableName 方法来设置对应结构体对应相连接的数据表
// 若没有设置默认会将结构体的名称 lower 然后 + s 得到的字符串作为默认的表名
func (t Product) TableName() string {
	return "product"
}

gorm.Model会额外增加idcreated_atupdated_atdeleted_at,然后id的话是默认作为一个主键的,这些去查看源代码都可以看到。

源代码截图
在这里插入图片描述

表的创建

// 数据表的创建
func useGorm() {
	// Migrate the schema
	// 可创建对应的表
	// 若已存在同名且字段一样的表则不会操作
	if err := db.AutoMigrate(&Product{}); err != nil {
		panic(err)
	}
}

成功创建出的表
在这里插入图片描述

要注意的是创建的表名即为db.AutoMigrate(&Product{})中结构体Product上的methodTableName如果结构体没有这个method那么就是这个结构体对应的名字的的小写然后加上s。例如这里Product上没有前面的TableNamemethod那么创建的表名就是products

数据的插入

单条数据的插入

// 数据的单条插入
func insertData() {
	rand.Seed(time.Now().UnixNano())
	code := strconv.Itoa(rand.Intn(1000))

	product := Product{
		Code:  "dasd" + code,
		Price: rand.Intn(100),
	}

	// Omit 用于忽略字段
	if err := db.Model(&Product{}).Omit("Price").Create(&product).Error; err != nil {
		fmt.Println("数据插入失败")
	}

	// 指定对应的字段插入
	// db.Select("Price").Create(&product)

	// if err := db.Select("Price").Create(&product).Error; err != nil {
	// 	fmt.Println("数据插入失败")
	// }

	// 将结构体中的数据全部插入
	// if err := db.Model(&Product{}).Create(&product).Error; err != nil {
	// 	fmt.Println("数据插入失败")
	// }

}

成功执行表对应的内容
在这里插入图片描述

  • 因为在gorm中很好的支持了链式的操作,其实通过db.Create(&product)就可以把指定的数据插入到表中,而Model的作用是可以指定输入插入数据对应的表,如果删除掉Model而直接执行db..Create(&product)的话插入表的信息以product这个数据对应的结构体来指定,所以Model可加可不加,加上去就以Model优先。
  • 字段的筛选,Omit可指定插入数据时要忽略的字段
  • 字段的选择,Select可指定插入数据时要保留的字段
method作用
Model指定后续要操作的数据表的基本信息(表名字段名),不使用该方法则以插入数据的结构体来指定
Omit指定要忽略对的字段名
Select指定要保留的字段名
Create执行插入操作

多数据一次性插入

// 数据的批量插入
func insertDatas() {
	rand.Seed(time.Now().UnixMilli())
	var products []Product
	for i := 0; i < 10; i++ {
		products = append(products, Product{
			Code:  "dasd" + strconv.Itoa(rand.Intn(1000)),
			Price: rand.Intn(100),
		})
	}
	
	//这样会将所有的数据生成一条sql语句然后执行插入,这个可以观察日志的输出可以看出
	if err := db.Model(&Product{}).Create(&products).Error; err != nil {
		fmt.Println("数据插入失败")
		panic(err)
	}
}

生成一次性执行的sql语句的日志
在这里插入图片描述

有时候可能要插入的数据太多,生成单句sql插入的方式可能性能较差,所以也支持数据的分批的插入

多数据的分批插入

// 数据的批量插入
func insertData() {
	rand.Seed(time.Now().UnixMilli())
	var products []Product
	for i := 0; i < 10; i++ {
		products = append(products, Product{
			Code:  "dasd" + strconv.Itoa(rand.Intn(1000)),
			Price: rand.Intn(100),
		})
	}

	// 指定一批最多执行 CreateBatchSize 个数据的插入
	db := db.Session(&gorm.Session{CreateBatchSize: 2})

	if err := db.Model(&Product{}).Create(&products).Error; err != nil {
		fmt.Println("数据插入失败")
		panic(err)
	}
}

分批插入的日志输出
在这里插入图片描述


数据的删除

单条数据的删除


// 数据的单条删除
func delData() {
	var data Product
	if err := db.Where("code = ?", "dasd104").Delete(&data).Error; err != nil {
		panic(err)
	}

	// 根据主键进行删除
	db.Delete(&Product{}, "10")
	// DELETE FROM users WHERE code = 10;

	// 保存删除的数据  仅适用于支持 Returning 的数据库
	db.Clauses(clause.Returning{}).Where("code = ?", "dasd945").Delete(&data)
}

与上面的数据插入不同的是这里通过Delete方法来指定要对应操作的数据库

数据的多条删除

// 数据的单条删除
func delDatas() {
	if err := db.Where("code LIKE ?", "%1%").Delete(&Product{}).Error; err != nil {
		panic(err)
	}
	// 也可以
	// db.Delete(&Product{}, "code LIKE ?", "%1%")
}

数据的更新

单列数据的更新

// 对数据进行更新
func updateData() {
		product := Product{"dasd706", 10}
	// 条件更新
	db.Model(&Product{}).Where("code = ?", "dasd706").Update("price", 10)
	// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;

	// 根据 product 中的值进行更新
	//db.Model(&product).Update("price", 10)
	// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

	// 根据条件和 model 的值进行更新
	//db.Model(&product).Where("code = ?", "dasd706").Update("price", 10)
}

多列数据的更新

// 对数据进行更新
func updateDatas() {
	// Update - update multiple fields
	// 是按照对应的主键进行更新的 若没有设置主键则按照第一个字段进行更新
	// db.Model(&Product{}).Updates(Product{Price: 2000, Code: "F42"}) // non-zero fields
	// 使用多字段的更新
	// db.Model(&Product{}).Updates(map[string]interface{}{"Price": 0, "Code": "F42"})

	if err := db.Model(&Product{}).Where("Price = ?", 16).Update("Price", 120).Error; err != nil {
		panic(err)
	}
}

上述没有用到Model方法的话,操作的数据表由Update方法中的结构体指示

数据的查询

单条数据的查询

// 用于数据查询的
func searchData() {
	aimData := Product{}
	// 无条件查询
	// 按照主键升序
	if result := db.First(&aimData); result.Error != nil {
		panic(result.Error.Error())
	} else {
		fmt.Println("找到的记录数", result.RowsAffected)
		fmt.Println("查找到的数据", aimData)
	}
	// 获取一条记录,不指定排序字段
	// db.Take(&aimData)
	// 获取最后一条记录 按照主键的降序
	// db.Last(&aimData)
	 
	// 条件查询
	// 根据主键进行查询
	//db.First(&user, "10")
	// SELECT * FROM product WHERE code = "10";
}

多条数据的查询

// 用于数据查询的
func searchDatas() {
	aimData := []Product{}
	if err := db.Model(&Product{}).Where("Price = ?", 29).Find(&aimData).Error; err != nil {
		fmt.Println(err.Error())
	}

	fmt.Println(aimData)

	// IN
	if err := db.Where("Price IN ?", []int{29, 50, 120}).Find(&aimData).Error; err != nil {
		panic(err)
	}
	fmt.Println(aimData)

	// 用map进行查询
	// SELECT * FROM product WHERE code = "dasd671" AND price = 5;
	if err := db.Where(map[string]interface{}{"code": "dasd671", "price": 5}).Find(&aimData).Error; err != nil {
		panic(err)
	}

	fmt.Println(aimData)
}

上述没有用到Model方法的话,操作的数据表由Find方法中的结构体指示

事务

// 使用事务
func useAffairs() error {
	rand.Seed(time.Now().UnixNano())
	tx := db.Begin()
	defer func() {
		// 若发生 panic可恢复
		if r := recover(); r != nil {
			tx.Rollback()
			fmt.Println("回滚")
		}
	}()

	if err := tx.Error; err != nil {
		return err
	}

	if err := tx.Create(&Product{Price: rand.Intn(100), Code: "测试事务" + strconv.Itoa(rand.Intn(1000))}).Error; err != nil {
		fmt.Println("发生回滚")
		tx.Rollback()
		return err
	}

	if err := tx.Create(&Product{Price: rand.Intn(100), Code: "测试事务" + strconv.Itoa(rand.Intn(1000))}).Error; err == nil {
		fmt.Println("发生回滚")
		tx.Rollback()
		return err
	}

	return tx.Commit().Error
}
  • 在开启事务之后,在上述的函数中若任意一条语句创建失败或者是运行时panic的话都会触发事务的回滚
  • 需要注意的是tx.Commit()被执行的时候sql语句才真正被执行

参考

  • 官方文档 里面有更多的示例和高级用法
    https://gorm.io/zh_CN/docs/query.html
  • github地址
    https://github.com/go-gorm/gorm
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 19:02:54  更:2022-05-21 19:05:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:25:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码