go语言学习之旅--gorm(代码片段)

赵jc 赵jc     2022-12-02     197

关键词:

Go语言学习之旅--gorm

gorm概述

ORM简介

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库(如mysql数据库)存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

安装

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

gorm声明模型

模型定义

模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成

约定

GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间
遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们

gorm.Model

GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt

// gorm.Model 的定义
type Model struct 
 ID        uint           `gorm:"primaryKey"`
 CreatedAt time.Time
 UpdatedAt time.Time
 DeletedAt gorm.DeletedAt `gorm:"index"`

您可以将它嵌入到您的结构体中,以包含这几个字段,详情请参考 嵌入结构体

gorm连接到数据库

GORM 官方支持的数据库类型有:MySQL, PostgreSQL, SQlite, SQL Server
gorm连接MySQL

import (
 "gorm.io/driver/mysql"
 "gorm.io/gorm"
)

func main() 
 // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)

注意:想要正确的处理 time.Time ,您需要带上 parseTime 参数, (更多参数) 要支持完整的 UTF-8 编码,您需要将 charset=utf8 更改为 charset=utf8mb4 查看 此文章 获取详情

MySQl 驱动程序提供了 一些高级配置 可以在初始化过程中使用,例如:

db, err := gorm.Open(mysql.New(mysql.Config
 DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
 DefaultStringSize: 256, // string 类型字段的默认长度
 DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
 DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
 DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
 SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
), &gorm.Config)

快速入门

package main

import (
 "gorm.io/gorm"
 "gorm.io/driver/mysql"
)

type Product struct 
 gorm.Model
 Code  string
 Price uint


func main() 
 dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local"
 if err != nil 
   panic("failed to connect database")


 // 迁移 schema
 db.AutoMigrate(&Product)

 // Create
 db.Create(&ProductCode: "D42", Price: 100)

 // Read
 var product Product
 db.First(&product, 1) // 根据整形主键查找
 db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

 // Update - 将 product 的 price 更新为 200
 db.Model(&product).Update("Price", 200)
 // Update - 更新多个字段
 db.Model(&product).Updates(ProductPrice: 200, Code: "F42") // 仅更新非零值字段
 db.Model(&product).Updates(map[string]interface"Price": 200, "Code": "F42")

 // Delete - 删除 product
 db.Delete(&product, 1)

gorm的增删查改

gorm创建记录

user := UserName: "Jinzhu", Age: 18, Birthday: time.Now()

result := db.Create(&user) // 通过数据的指针来创建

user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数

用指定的字段创建记录

创建记录并更新给出的字段。

db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")

创建一个记录且一同忽略传递给略去的字段值。

db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")

批量插入

要有效地插入大量记录,请将一个 slice 传递给 Create 方法。GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。

var users = []UserName: "jinzhu1", Name: "jinzhu2", Name: "jinzhu3"
db.Create(&users)
for _, user := range users 
 user.ID // 1,2,3

使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:

var users = []Username: "jinzhu_1", ...., Name: "jinzhu_10000"

// 数量为 100
db.CreateInBatches(users, 100)

Upsert 和 Create With Associations 也支持批量插入

注意 使用CreateBatchSize 选项初始化 GORM 时,所有的创建& 关联 INSERT 都将遵循该选项

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config
 CreateBatchSize: 1000,
)

db := db.Session(&gorm.SessionCreateBatchSize: 1000)

users = [5000]UserName: "jinzhu", Pets: []Petpet1, pet2, pet3...

db.Create(&users)
// INSERT INTO users xxx (5 batches)
// INSERT INTO pets xxx (15 batches)

检索单个对象

GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error        // returns error or nil

// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

如果你想避免ErrRecordNotFound错误,你可以使用Find,比如db.Limit(1).Find(&user),Find方法可以接受struct和slice的数据。

First 和 Last 会根据主键排序,分别查询第一条和最后一条记录。只有在目标 struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。例如:

var user User
var users []User  

// 有效,因为目标 struct 是指针
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 有效,因为通过 `db.Model()` 指定了 model
result := map[string]interface
db.Model(&User).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 无效
result := map[string]interface
db.Table("users").First(&result)

// 配合 Take 有效
result := map[string]interface
db.Table("users").Take(&result)

// 未指定主键,会根据第一个字段排序(即:`Code`)
type Language struct 
 Code string
 Name string

db.First(&Language)
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1

用主键检索

如果主键是数字类型,您可以使用 内联条件 来检索对象。传入字符串参数时,需要特别注意 SQL 注入问题,查看 安全 获取详情.

db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;

db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;

db.Find(&users, []int1,2,3)
// SELECT * FROM users WHERE id IN (1,2,3);

如果主键是字符串(例如像 uuid),查询将被写成这样:

db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";

检索全部对象

// 获取全部记录
result := db.Find(&users)
// SELECT * FROM users;
result.RowsAffected // 返回找到的记录数,相当于 `len(users)`
result.Error        // returns error

String 条件

// 获取第一条匹配的记录

db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// 获取全部匹配的记录
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where("name IN ?", []string"jinzhu", "jinzhu 2").Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

Struct & Map 条件

// Struct
db.Where(&UserName: "jinzhu", Age: 20).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;

// Map
db.Where(map[string]interface"name": "jinzhu", "age": 20).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;

// 主键切片条件
db.Where([]int6420, 21, 22).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);

注意 当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果您的字段值为 0、‘’、false 或其他 零值,该字段不会被用于构建查询条件,例如:

db.Where(&UserName: "jinzhu", Age: 0).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";

如果想要包含零值查询条件,你可以使用 map,其会包含所有 key-value 的查询条件,例如:

db.Where(map[string]interface"Name": "jinzhu", "Age": 0).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;

查看 指定结构体查询字段 获取详情.

更新

更新单个列

当使用 Update 更新单个列时,你需要指定条件,否则会返回 ErrMissingWhereClause 错误,查看 Block Global Updates 获取详情。当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件,例如:

// 条件更新
db.Model(&User).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;

// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

更新多列

Updates 方法支持 struct 和 map[string]interface 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段

// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(UserName: "hello", Age: 18, Active: false)
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface"name": "hello", "age": 18, "active": false)
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

注意 当通过 struct 更新时,GORM 只会更新非零字段。如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作

更新选定字段

如果您想要在更新时选定、忽略某些字段,您可以使用 Select、Omit

// 使用 Map 进行 Select
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface"name": "hello", "age": 18, "active": false)
// UPDATE users SET name='hello' WHERE id=111;

db.Model(&user).Omit("name").Updates(map[string]interface"name": "hello", "age": 18, "active": false)
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// 使用 Struct 进行 Select(会 select 零值的字段)
db.Model(&user).Select("Name", "Age").Updates(UserName: "new_name", Age: 0)
// UPDATE users SET name='new_name', age=0 WHERE id=111;

// Select 所有字段(查询包括零值字段的所有字段)
db.Model(&user).Select("*").Update(UserName: "jinzhu", Role: "admin", Age: 0)

// Select 除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Update(UserName: "jinzhu", Role: "admin", Age: 0)

批量更新

如果您尚未通过 Model 指定记录的主键,则 GORM 会执行批量更新

// 根据 struct 更新
db.Model(User).Where("role = ?", "admin").Updates(UserName: "hello", Age: 18)
// UPDATE users SET name='hello', age=18 WHERE role = 'admin';

// 根据 map 更新
db.Table("users").Where("id IN ?", []int10, 11).Updates(map[string]interface"name": "hello", "age": 18)
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);

删除一条记录

删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete,例如:

// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;

// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";

根据主键删除

GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字(如以下例子。也可以使用字符串——译者注)。查看 查询-内联条件(Query Inline Conditions) 了解详情。

db.Delete(&User, 10)
// DELETE FROM users WHERE id = 10;

db.Delete(&User, "10")
// DELETE FROM users WHERE id = 10;

db.Delete(&users, []int1,2,3)
// DELETE FROM users WHERE id IN (1,2,3);

批量删除

如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录

db.Where("email LIKE ?", "%jinzhu%").Delete(Emailgo语言学习之旅--gorm(代码片段)

Go语言学习之旅--gormgorm概述ORM简介安装gorm声明模型模型定义约定gorm.Modelgorm连接到数据库快速入门gorm的增删查改增gorm创建记录用指定的字段创建记录批量插入查检索单个对象用主键检索检索全部对象String条件Struct&Map条件更... 查看详情

go语言学习之旅--go语言环境安装(代码片段)

前言笔者已经自学python大概已经几个月了,但是工作繁忙,而且工作中未用到python的知识。于是就渐渐生疏了,这是下定决心学习go,是因为公司有一台go编写的报表系统,而且公司逐渐在想docker转,学习go更又助于未来的工作。... 查看详情

go语言学习之旅--结构体(代码片段)

结构体go语言类型定义go语言类型别名go语言类型定义和类型别名的区别结构体go语言结构体的定义声明一个结构体变量结构体的初始化访问结构体成员匿名结构体结构体指针举例使用new关键字创建结构体指针访问结构体指针成员... 查看详情

go语言学习之旅--文件os(代码片段)

Go语言学习之旅--文件os库OS文件、目录操作File文件读操作File文件写操作os包进程相关操作os包和环境相关的方法博客根据B站《golang入门到项目实战[2021最新Go语言教程,没有废话,纯干货》视频整理的,如有错误请大... 查看详情

go语言学习之旅--文件os(代码片段)

Go语言学习之旅--文件os库OS文件、目录操作File文件读操作File文件写操作os包进程相关操作os包和环境相关的方法博客根据B站《golang入门到项目实战[2021最新Go语言教程,没有废话,纯干货》视频整理的,如有错误请大... 查看详情

go语言学习之旅--初识golang(代码片段)

Go语言简介特点编码风格Go安装测试Go(又称Golang)是Google的RobertGriesemer,RobPike及KenThompson开发的一种静态强类型、编译型语言。Go语言语法与C相近,但功能上有:内存安全,GC(垃圾回收),结... 查看详情

go语言学习之旅--函数(代码片段)

函数GoLang函数介绍Go语言中函数特性函数的定义匿名函数闭包deferinit函数GoLang函数介绍函数的go语言中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型... 查看详情

go学习之旅go语言安装及环境配置教程(代码片段)

一、前言Go(又称Golang)是Google开发的一种静态强类型、编译型的编程语言。Go语言的主要特点包括:自动垃圾回收、更丰富的内置类型、函数多返回值、错误处理、匿名函数和闭包、类型和接口、并发编程、反射、语言交互性... 查看详情

go语言学习之旅--接口(代码片段)

接口接口实现接口必须实现接口中的所有方法golang接口值类型接收者和指针类型接收者golang接口和类型的关系一个类型实现多个接口多个类型实现同一个接口golang接口嵌套接口go语言的接口,是一种新的类型定义,它把所... 查看详情

go的100天之旅-03变量(代码片段)

变量变量介绍变量这个词来源于数学,类似方程中的x、y,代表的是存储在计算机中的值。这里主要介绍Go和其它编程语言不一样的地方,在前面我们提到过,Go是一门静态语言。静态语言区别动态语言一个重要的特性就是变量的... 查看详情

go语言学习之旅--基础语法(代码片段)

变量变量有三种声明方式第一种:var变量名类型=值variint=10第二种:var变量名=值varj=10第三种:变量名:=值,自动推导类型name:="zjc"一些特殊的情况:一次声明多个变量,变量名和值... 查看详情

go之数据库gorm(代码片段)

参考的链接https://gorm.io/docs/migration.htmlhttps://blog.csdn.net/qq_23179075/article/details/88066241  查看详情

gorm框架学习--入门(代码片段)

Gorm框架学习--入门引言快速入门模型定义约定gorm.Model高级选项字段级权限控制创建/更新时间追踪(纳秒、毫秒、秒、Time)嵌入结构体字段标签关联标签连接到数据库MySQL自定义驱动现有的数据库连接其他连接池参考引言... 查看详情

go的100天之旅-06数组和slice(代码片段)

目录数组Slice数组Go的数组和其它语言基本上一样,是长度固定的特定类型元素组成的序列,这基本上是所有语言数组的特性。和其它语言相比差异主要在声明和初始化的写法上,下面是简单声明一个数组:vara[5]intfmt.Println(a[0])fm... 查看详情

go开源宝藏gorm专场(含思维导图)|持续更新(代码片段)

写在前面本人只是一个Go语言的初学者,这篇文只是把我平常经常用到的都总结起来而已。具体详细的内容建议到去GORM的中文文档查看。当然这篇文章也会持续更新,记录我的CURD打磨过程这篇文章也会持续更新哒思维导... 查看详情

学习笔记golang之gorm学习笔记(代码片段)

一、模型定义1.模型定义模型是标准的struct,由Go的基本数据类型、实现了Scanner和Valuer接口的自定义类型及其指针或别名组成,如:typeUserstructIDuintNamestringEmail*stringAgeuint8Birthday*time.TimeMemberNumbersql.NullStringActivedAtsql 查看详情

从java到go搭建go的orm框架gorm(代码片段)

【提问】如何使用Goland软件,搭建一个ORM框架GORM?【解答】具体步骤如下:1、检查Go的安装在任意目录执行如下命令:goversion若有如下返回,则安装成功;如果报异常,则重新安装golanggoversiongo1.19.1darw... 查看详情

从java到go搭建go的orm框架gorm(代码片段)

【提问】如何使用Goland软件,搭建一个ORM框架GORM?【解答】具体步骤如下:1、检查Go的安装在任意目录执行如下命令:goversion若有如下返回,则安装成功;如果报异常,则重新安装golanggoversiongo1.19.1darw... 查看详情