mysql

基本定义

Mysql操作,我们一律使用Gorm做为默认ORM,我们的依赖注入完全无缝兼容gorm。在Web案例中我们有访问过数据库

Gorm特性

最新api请参考,gorm官网http://gorm.book.jasperxu.com/

本教程只供参考

  • 全功能 ORM

  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)

  • Create,Save,Update,Delete,Find 中钩子方法

  • 支持 PreloadJoins 的预加载

  • 事务,嵌套事务,Save Point,Rollback To Saved Point

  • Context,预编译模式,DryRun 模式

  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD

  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询

  • 复合主键,索引,约束

  • Auto Migration

  • 自定义 Logger

  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…

  • 每个特性都经过了测试的重重考验

  • 开发者友好

模型定义

模型定义

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

例如:

type User struct {

gorm.Model

Name string

Age sql.NullInt64

Birthday time.Time

Email string

gorm:"type:varchar(100);unique_index"

Role string gorm:"size:255" //设置字段的大小为255个字节

MemberNumber string gorm:"unique;not null" // 设置 memberNumber 字段唯一且不为空 Num int gorm:"AUTO_INCREMENT" // 设置 Num字段自增

Address string gorm:"index:addr" // 给 Address 创建一个名字是 addr的索引

IgnoreMe int gorm:"-" //忽略这个字段 }

约定

GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间

遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们

gorm.Model

GORM 定义一个 gorm.Model 结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt

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

高级选项

字段级权限控制

可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

注意: 使用 GORM Migrator 创建表时,不会创建被忽略的字段

创建/更新时间追踪(纳秒、毫秒、秒、Time)

GORM 约定使用 CreatedAtUpdatedAt 追踪创建/更新时间。如果您定义了这种字段,GORM 在创建、更新时会自动填充 当前时间

要使用不同名称的字段,您可以配置 autoCreateTimautoUpdateTim 标签

如果您想要保存 UNIX(毫/纳)秒时间戳,而不是 time,您只需简单地将 time.Time 修改为 int 即可

嵌入结构体

对于匿名字段,GORM 会将其字段包含在父结构体中,例如:

对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:

并且,您可以使用标签 embeddedPrefix 来为 db 中的字段名添加前缀,例如:

字段标签

声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格

标签名

说明

column

指定 db 列名

type

列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT

size

指定列大小,例如:size:256

primaryKey

指定列为主键

unique

指定列为唯一

default

指定列的默认值

precision

指定列的精度

scale

指定列大小

not null

指定列为 NOT NULL

autoIncrement

指定列为自动增长

embedded

嵌套字段

embeddedPrefix

嵌入字段的列名前缀

autoCreateTime

创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano

autoUpdateTime

创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli

index

根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情

uniqueIndex

index 相同,但创建的是唯一索引

check

创建检查约束,例如 check:age > 13,查看 约束 获取详情

<-

设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限

->

设置字段读的权限,->:false 无读权限

-

忽略该字段,- 无读写权限、

创建

创建记录

用指定的字段创建记录

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

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

批量插入

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

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

UpsertCreate With Associations 也支持批量插入

说明 初始化GORM时使用CreateBatchSize选项,所有的INSERT在创建record & associations时都会尊重这个选项

创建钩子

GORM允许用户定义的钩子被实现为beforeave, beforereate, AfterSave, AfterCreate。这些钩子方法将在创建记录时被调用,有关生命周期的详细信息请参考钩子

如果你想跳过钩子方法,你可以使用SkipHooks会话模式,例如

根据 Map 创建

GORM支持创建 map[string]interface{}和[]map[string]interface{}{},例如:

注意当创建from map时,钩子不会被调用,关联不会被保存,主键值也不会被填回

使用 SQL 表达式、Context Valuer 创建记录

ORM允许使用SQL表达式插入数据,有两种方法可以实现这个目标,create from map[string]interface{}或自定义数据类型,例如:

高级选项

关联创建

当创建一些带有关联的数据时,如果它的关联值不是零值,那么这些关联将被插入,并且它的hook方法将被调用

你可以用Select跳过保存关联,省略,例如:

默认值

你可以为带有default标签的字段定义默认值,例如:

然后,当为零值字段插入数据库时,将使用默认值

注意任何像0,",false这样的零值不会被保存到数据库中,对于那些定义了默认值的字段,你可能想要使用指针类型或Scanner/Valuer来避免这种情况,例如:

注意:你必须为在数据库中有默认值或虚拟/生成值的字段设置默认标记,如果你想在迁移时跳过默认值定义,你可以使用default:(-),例如:

当使用虚拟/生成的值时,您可能需要禁用其创建/更新权限,检查字段级权限

Upsert 及冲突

GORM为不同的数据库提供兼容的Upsert支持

在高级查询中还检出firststorinit和firststorcreate

查看原始SQL和SQL Builder以获取更多细节

查询

检索单个对象

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

如果你想避免ErrRecordNotFound错误,你可以使用Find,比如db.Limit(1).Find(&user)

First, Last方法将根据主键找到第一个/最后一个记录顺序,它只在使用struct查询或提供模型值时有效,如果没有为当前模型定义主键,将根据第一个字段排序,例如:

根据主键检索

通过使用内联条件,可以使用主键检索对象。使用字符串时要格外小心,以避免SQL注入,详细信息请参阅安全部分

检索全部对象

条件

String 条件

Struct & Map 条件

注意:当使用struct查询时,GORM只会查询非零字段,这意味着如果你的字段的值是0,",false或其他零值,它不会被用于构建查询条件,例如

您可以使用map来构建查询条件,例如:

内联条件

工作原理类似于Where。

Not 条件

建造NOT条件 工作原理类似于Where。

Or 条件

还可以在高级查询中检查组条件,它可以用于编写复杂的SQL

选择特定字段

指定要从数据库检索的字段,默认情况下,选择所有字段

也可以查看智能选择字段

Order

从数据库中检索记录时指定顺序

Limit & Offset

Limit指定要检索的记录的最大数量Offset指定在开始返回记录之前要跳过的记录的数量

关于如何制作分页器的签出分页

Group & Having

Distinct

从模型中选择不同的值

Distinct 也可以使用Pluck Count

Joins

指定连接条件

Joins 预加载

你可以在单个SQL中使用连接急切加载关联,例如:

有关详细信息,请参阅预加载(即时加载)

Scan

扫描结果到一个结构的工作类似于查找

高级查询

智能选择字段

GORM 允许通过 Select 方法选择特定的字段,如果您在应用程序中经常使用此功能,你也可以定义一个较小的结构体,以实现调用 API 时自动选择特定的字段,例如:

说明QueryFields模式将按所有字段的名称选择当前模型

Locking (FOR UPDATE)

GORM支持不同类型的锁,例如:

更多细节请参考原始SQL和SQL Builder

子查询

子查询可以嵌套在查询中,当使用* GORM . db对象作为参数时,GORM可以生成子查询

From 子查询

GORM允许在方法表的FROM子句中使用子查询,例如:

Group 条件

更容易编写带有组条件的复杂SQL查询

命名参数

GORM支持sql命名参数。NamedArg或map[string]接口{}{},例如:

查看Raw SQL和SQL Builder了解更多细节

Find 至 map

GORM允许扫描结果映射[string]interface{}或[]map[string]interface{},不要忘记指定模型或表,例如:

FirstOrInit

获取第一个匹配的记录或用给定的条件初始化一个新的实例(仅适用于struct或map条件)

用更多的属性初始化struct如果没有找到记录,这些Attrs将不会被用于构建SQL查询

为struct分配属性,无论是否找到,这些属性都不会用于构建SQL查询,最终数据也不会保存到数据库中

FirstOrCreate

获取第一个匹配的记录或创建一个给定条件的新记录(仅适用于struct, map条件)

创建具有更多属性的结构,如果没有找到记录,这些属性将不会被用于构建SQL查询

为记录分配属性,不管是否找到记录,并将它们保存回数据库

优化器、索引提示

优化器提示用于控制查询优化器选择某个查询执行计划,GORM 通过 gorm.io/hints 提供支持,例如:

索引提示允许向数据库传递索引提示,以防查询规划器搞混。

请参考优化Optimizer Hints/Index/Comment了解更多细节

迭代

GORM支持遍历行

FindInBatches

批量查询和处理记录

查询钩子

GORM允许hook在查询后被调用,当查询一个记录时,它将被调用,参考hook获取详细信息

Pluck

从数据库查询单个列并扫描到一个切片,如果要查询多个列,则使用Select和scan代替

Scopes

作用域允许您指定可以作为方法调用引用的常用查询

查看Scopes详细信息

Count

获取匹配记录计数

更新

保存所有字段

Save 会保存所有的字段,即使字段是零值

更新单个列

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

更新多列

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

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

更新选定字段

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

更新 Hook

对于更新操作,GORM 支持 BeforeSaveBeforeUpdateAfterSaveAfterUpdate 钩子,这些方法将在更新记录时被调用,详情请参阅 钩子

批量更新

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

阻止全局更新

如果在没有任何条件的情况下执行批量更新,默认情况下,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误

对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:

更新的记录数

获取受更新影响的行数

高级选项

使用 SQL 表达式更新

GORM 允许使用 SQL 表达式更新列,例如:

并且 GORM 也允许使用 SQL 表达式、自定义数据类型的 Context Valuer 来更新,例如:

根据子查询进行更新

使用子查询更新表

不使用 Hook 和时间追踪

如果您想在更新时跳过 Hook 方法且不追踪更新时间,可以使用 UpdateColumnUpdateColumns,其用法类似于 UpdateUpdates

检查字段是否有变更?

GORM 提供了 Changed 方法,它可以被用在 Before Update Hook 里,它会返回字段是否有变更的布尔值

Changed 方法只能与 UpdateUpdates 方法一起使用,并且它只是检查 Model 对象字段的值与 UpdateUpdates 的值是否相等,如果值有变更,且字段没有被忽略,则返回 true

在更新时修改值

若要在 Before 钩子中改变要更新的值,如果它是一个完整的更新,可以使用 Save;否则,应该使用 SetColumn ,例如:

删除

删除一条记录

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

根据主键删除

GORM 允许通过内联条件指定主键来检索对象,但只支持整型数值,因为 string 可能导致 SQL 注入。查看 内联条件查询 获取详情

Delete Hook

对于删除操作,GORM 支持 BeforeDeleteAfterDelete Hook,在删除记录时会调用这些方法,查看 Hook 获取详情

批量删除

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

阻止全局删除

如果在没有任何条件的情况下执行批量删除,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误

对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:

软删除

如果您的模型包含了一个 gorm.DeletedAt 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录。

如果您不想引入 gorm.Model,您也可以这样启用软删除特性:

查找被软删除的记录

您可以使用 Unscoped 找到被软删除的记录

永久删除

您也可以使用 Unscoped 永久删除匹配的记录

SQL 构建器

原生 SQL

原生查询 SQL 和 Scan

Exec 原生 SQL

注意 GORM 允许缓存预编译 SQL 语句来提高性能,查看 性能 获取详情

命名参数

GORM 支持 sql.NamedArgmap[string]interface{}{} 或 struct 形式的命名参数,例如:

DryRun 模式

在不执行的情况下生成 SQL ,可以用于准备或测试生成的 SQL,详情请参考 Session

Row & Rows

获取 *sql.Row 结果

获取 *sql.Rows 结果

转到 FindInBatches 获取如何在批量中查询和处理记录的信息, 转到 Group 条件 获取如何构建复杂 SQL 查询的信息

sql.Rows 扫描至 model

使用 ScanRows 将一行记录扫描至 struct,例如:

高级

子句(Clause)

GORM 内部使用 SQL builder 生成 SQL。对于每个操作,GORM 都会创建一个 *gorm.Statement 对象,所有的 GORM API 都是在为 statement 添加/修改 Clause,最后,GORM 会根据这些 Clause 生成 SQL

例如,当通过 First 进行查询时,它会在 Statement 中添加以下 Clause

然后 GORM 在 Query callback 中构建最终的查询 SQL,像这样:

生成 SQL:

您可以自定义 Clause 并与 GORM 一起使用,这需要实现 Interface 接口

可以参考 示例

子句构造器

不同的数据库, Clause 可能会生成不同的 SQL,例如:

之所以支持 Clause,是因为 GORM 允许数据库驱动程序通过注册 Clause Builder 来取代默认值,这儿有一个 Limit 的示例

子句选项

GORM 定义了很多 Clause,其中一些 Clause 提供了你可能会用到的选项

尽管很少会用到它们,但如果你发现 GORM API 与你的预期不符合。这可能可以很好地检查它们,例如:

StatementModifier

GORM 提供了 StatementModifier 接口,允许您修改语句,使其符合您的要求,这儿有一个 Hint 示例

最后更新于

这有帮助吗?