Gorm 基础

Gorm 快速入门

GORM github地址: https://github.com/go-gorm/gorm GORM 官网地址: https://gorm.io/zh_CN/docs/

主操作: MYSQL

  • 安装

//安装MySQL驱动、gorm包
go get -u gorm.io/driver/mysql
go get -u gorm.io/gorm

增删改查

package main

import (
    "errors"
    "fmt"
    "time"

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

type User struct {
    gorm.Model   // gorm 自带模型继承
    Username string `gorm:"column:username;not null"` // 列名(字段名)为username,不为空
    Password string `gorm:"column:password;not null"`
}

// 设置表名,可以通过给struct类型定义 TableName函数,返回当前struct绑定的mysql表名是什么
func (u User) TableName() string {
    return "users"
}

func main() {
    //配置MySQL连接参数
    username := "root"   //账号
    password := "123456" //密码
    host := "127.0.0.1"  //数据库地址,可以是Ip或者域名
    port := 3306         //数据库端口
    Dbname := "tizi365"  //数据库名

    /*
      通过前面的数据库参数,拼接 MYSQL DSN, 其实就是数据库连接串(数据源名称)
      MYSQL dsn格式: {username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local
    */
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)

    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("连接数据库失败, error=" + err.Error())
    }

    // 自动创建表 users, 也就是上面模型 User
    db.AutoMigrate(&User{})

    /* 增 */
    u := User{
        Username:   "tizi365",
        Password:   "123456",
    }
    // 错误处理,默认gorm.DB的Error属性为 nil, 检测 Error 是否为nil
    if err := db.Create(&u).Error; err != nil {
        fmt.Println("插入失败", err)
        return
    }

    /* 查 */
    u = User{}
    result := db.Where("username = ?", "tizi365").First(&u)
    if errors.Is(result.Error, gorm.ErrRecordNotFound) {
        fmt.Println("找不到记录")
        return
    }
    fmt.Println(u.Username, u.Password)

    /* 改 */
    db.Model(&User{}).Where("username = ?", "tizi365").Update("password", "654321")

    /* 删 */
    db.Where("username = ?", "tizi365").Delete(&User{})
}

GORM 调试模式

会打印执行的 sql 语句

GORM 高并发

提高数据库连接的使用率,避免重复建立数据库连接带来的性能消耗,会经常使用数据库连接池技术来维护数据库连接。

  • 1、定义tools包,负责数据库初始化工作

  • 使用例子

GORM 插入数据

GORM 查询数据

  • Take

查询一条数据

  • First

查询一条记录,根据主键ID排序(正序),返回第一条记录

  • Last

查询一条记录, 根据主键ID排序(倒序),返回第一条记录

  • Find

查询多条记录,Find函数返回的是一个数组

  • Pluck

查询一列值

  • where

语法:db.Where(query interface{}, args ...interface{})

  • select

设置select子句, 指定返回的字段

  • order

设置排序语句,order by子句

  • limit & Offset

设置limit和Offset子句,分页的时候常用语句。

  • count

Count函数,直接返回查询匹配的行数。

  • group by

  • 直接执行sql语句

对于复杂的查询,例如多表连接查询,我们可以直接编写sql语句,然后执行sql语句。 gorm通过db.Raw设置sql语句,通过Scan执行查询。

GORM 更新数据

通过结构体变量更新字段值, gorm库会忽略零值字段。就是字段值等于0, nil, "", false这些值会被忽略掉,不会更新。如果想更新零值,可以使用map类型替代结构体。

  • Save

相当于根据主键id,更新所有模型字段值。

  • Update

更新单个字段值

自定义条件更新记录

更新某个 id 的多个记录

更新符合条件的记录

更新零值,使用 map 代替结构体去更新

计算更新表达式

UPDATE foods SET stock = stock + 1 WHERE id = '2' 这样的带计算表达式的更新语句gorm怎么写?

gorm提供了Expr函数用于设置表达式

GORM 删除数据

删除单条数据

删除符合条件数据

GORM事务处理

  • 自动事务

通过db.Transaction函数实现事务,如果闭包函数返回错误,则回滚事务。

  • 手动事务

在开发中经常需要数据库事务来保证多个数据库写操作的原子性。例如电商系统中的扣减库存和保存订单。

共用: 多表关联测试

一对多

继承共用代码

一对多测试模型

添加测试数据

查询某用户拥有几张信用卡

查询某个用户名下有几张信用卡

查询某ID的信用卡和归属用户

查询所有用户和所属信用卡

查询所有信用卡和归属用户

一对多,级联删除

删除用户,并会把此用户名下的信用卡所属 user_id 设置为空

一对一

一对一模型

用户查询自身份证

Joins 连表方式

身份证反向查询用户

多对多

举例账号和角色的关联关系; 一个账号可以有多个角色,一个角色也可以给多个账号的关联 如果查看数据库的话,会发现有三张表,多出的一张表是存储 角色和账户的关系: account_role

  • many2many:account_role 定义中间表名为:account_role

  • foreignKey:Id 使用当前表的id作为外键

  • joinForeignKey:accountId 当前数据模型外键关联到中间件表的字段名叫accountId

  • joinReferences:roleId 反向引用字段,如果是账号表就要写中间表的roleId

添加测试数据

通过用户查询角色

通过角色反查用户

参考地址

Beforce

GORM 模型定义

内置模型约定

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

例如:

自定义匿名字段模型约定

默认是有一个 gorm.Model, 但是我不想用,我想自定义,让所有表中都携带我自定义的字段

自定义正常字段模型约定

  • 比如某些公共的字段,可以被其他结构体调用

标签名
说明
示例

column

指定 db 列名

gorm:"column:username"

type

数据类型 bool、int、uint、float、string、time、bytes

gorm:"type:int" // 设置类型为int gorm:"type:varchar(20);not null "

size

指定列大小

gorm:"size:255" // 设置字段大小为255

primaryKey

指定列为主键

gorm:"primaryKey"

unique

指定列为唯一

gorm:"unique" // 唯一

default

指定列的默认值

gorm:"default:0" // 默认值为0

precision

指定列的精度

scale

指定列大小

not null

指定列为 NOT NULL

gorm:"not null" // 不为空

autoIncrement

指定列为自动增长

gorm:"AUTO_INCREMENT"

autoIncrementIncrement

自动步长,控制连续记录之间的间隔

embedded

嵌套字段

gorm:"embedded"

embeddedPrefix

嵌入字段的列名前缀

autoCreateTime

创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,使用 nano/milli 来追踪纳秒、毫秒时间戳

gorm:"embedded;embeddedPrefix:xxxx_"

autoUpdateTime

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

index

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

gorm:"index" gorm:"index:addr"// 给address字段创建名为addr的索引

uniqueIndex

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

check

创建检查约束

check:age > 13

<-

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

`gorm:"<-:create"

->

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

-

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

gorm:"-" // 忽略本字段

comment

迁移时为字段添加注释

gorm:"column:beast_id" // 设置列名为 beast_id

表字段标签

表关联标签

标签
描述

foreignKey

指定当前模型的列作为连接表的外键

references

指定引用表的列名,其将被映射为连接表外键

polymorphic

指定多态类型,比如模型名

polymorphicValue

指定多态值、默认表名

many2many

指定连接表表名

joinForeignKey

指定连接表的外键列名,其将被映射到当前表

joinReferences

指定连接表的外键列名,其将被映射到引用表

constraint

关系约束,例如:OnUpdateOnDelete

复数表名(表名)

表名是结构体名称的复数形式

GORM 数据库连接

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

官方通往: https://gorm.io/zh_CN/docs/connecting_to_the_database.html

或者参考: 惯例 - 人人学go惯例 - 人人学go

MYSQL 官方指南配置

mysql.Config的源码

gorm调试模式

MYSQL 单机测试使用

  • 示例一

MYSQL 封装测试调用

  • main

  • tools/tools.go 封装 mysql (测试使用)

viper

安装 Viper

Viper 作用

  • 设置默认值

  • 读取 JSON、TOML、YAML(YML)、HCL、envfile 和 Java properties 属性配置文件

  • 实时查看和重读配置文件(可选)

  • 从环境变量中读取

  • 从远程配置系统(etcd 或 Consor)读取数据,并观察变化

  • 从命令行标志读取

  • 从缓冲区读取

  • 设置显式值

Viper获取值

每一个Get方法在找不到值的时候都会返回零值。为了检查给定的键是否存在,提供了IsSet()方法。

读取配置文件

写入配置文件

  • WriteConfig - 将当前的viper配置写入预定义的路径并覆盖(如果存在的话)。如果没有预定义的路径,则报错。

  • SafeWriteConfig - 将当前的viper配置写入预定义的路径。如果没有预定义的路径,则报错。如果存在,将不会覆盖当前的配置文件。

  • WriteConfigAs - 将当前的viper配置写入给定的文件路径。将覆盖给定的文件(如果它存在的话)。

  • SafeWriteConfigAs - 将当前的viper配置写入给定的文件路径。不会覆盖给定的文件(如果它存在的话)。

监控配置文件热加载

只需告诉viper实例watchConfig。可选地,你可以为Viper提供一个回调函数,以便在每次发生更改时运行。

从io.Reader读取配置

Viper预先定义了许多配置源,如文件、环境变量、标志和远程K/V存储,但你不受其约束。你还可以实现自己所需的配置源并将其提供给viper。

远程Key/Value存储示例

更多其他方法参考

https://www.liwenzhou.com/posts/Go/viper_tutorial/#autoid-1-4-0

示例读取(ini,yaml)

当前路径下有两个文件

Last updated