gorm基础字典

gorm的使用

gorm的安装

打开go运行的文件夹下的终端,创建main.go文件 执行下面的指令初始化mod

go mod init main.go

安装gorm
官方给的示例是

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
但是我们这里要使用mysql数据库 所以就对第二个依赖进行一部分修改

got get -u gorm.io/driver/mysql

gorm的使用 增删改查

package main

import (
	"fmt"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

func main() {
	dsn := "root:1296729980@tcp(127.0.0.1:3306)/go_db"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}
	// //创建表
	db.AutoMigrate(&Product{})
	p := Product{
		Price: 200,
		Code:  "2",
	}
	//插入数据
	db.Create(&p)
	//查看数据
	var product Product
	// db.First(&product, 2) // 按照整形链查找
	db.First(&product, 1)
	fmt.Printf("product: %v\n", product)

	//改
	//这里有坑,修改单个字段使用的是update 修改多个使用到的是updates
	db.Model(&product).Update("Price", 300)
	//更新多个字段
	db.Model(&product).Updates(Product{Price:300,Code:"3"})
	db.Model(&product).Updates(map[string]interface{}{"Price": 300, "Code": "3"})
	//删
	db.Delete(&product, 1)
}

gorm 模型 model

gorm的模型更多的是一种约定,而不是配置

gorm.Model是grom的一种结构体代表的是

type Model struct{
    ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

当我们生命gorm结构体时,如果不想直接写gorm.Model的话,将上面的这些字段加入到我们声明的结构体当中效果也是一样的。

字段权限控制

当我们对字段CRUD时,默认字段是具有全部的权限的

gorm允许我们使用使用标签来控制字段级的权限

type User struct {
  Name string `gorm:"<-:create"` // allow read and create
  Name string `gorm:"<-:update"` // allow read and update
  Name string `gorm:"<-"`        // allow read and write (create and update)
  Name string `gorm:"<-:false"`  // allow read, disable write permission
  Name string `gorm:"->"`        // readonly (disable write permission unless it configured)
  Name string `gorm:"->;<-:create"` // allow read and create
  Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
  Name string `gorm:"-"`            // ignore this field when write and read with struct
  Name string `gorm:"-:all"`        // ignore this field when write, read and migrate with struct
  Name string `gorm:"-:migration"`  // ignore this field when migrate with struct
}

当使用gorm Migrator创建表时,不会创建被忽略的字段名

创建、修改、删除时间

gorm约定默认使用

    CreatedAt 
    UpdatedAt 
    DeletedAt

这三个字段名来分别记载创建时间,修改时间,删除时间
如果不想使用这三个字段可以使用标签进行更改

type User struct {
  CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
  UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
  Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳填纳秒数充更新时间
  Updated   int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间
  Created   int64 `gorm:"autoCreateTime"`      // 使用时间戳秒数填充创建时间
}

gorm 连接数据库

gorm支持连接的数据库有以下几种MySQL, PostgreSQL, SQlite, SQL Server
我们主要介绍mysql
如果想要了解更多可以访问官方文档地址

官方链接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{})
}

这只是个实例 我们在使用时dsn会这样写

	dsn := "root:1296729980@tcp(127.0.0.1:3306)/go_db"

root为名称,root后跟的是数据库密码,go_db是我们在root下创建的数据库

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

如果想要使用更加精细的配置,官方也给了配置选项,以下表明的是默认配置选项

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 (
	"fmt"

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

func main() {
	dsn := "root:1296729980@tcp(localhost:3306)/go_db"
	d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(d)
}

&{0x140001da630 <nil> 0 0x140002aa000 1}

我们会得到一个地址,代表链接成功

创建记录

创建一个记录

package main

import (
	"fmt"
	"time"

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

var db *gorm.DB

type User struct {
	gorm.Model
	Name     string
	Age      uint
	Birthday time.Time
}

func init() {
	dsn := "root:1296729980@tcp(localhost:3306)/go_db"
	d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}
	db = d
}
func InsertValue() {
	user := User{Name: "小明", Age: 11, Birthday: time.Now()}
	result := db.Create(&user)
	fmt.Println("userID", user.ID) // 显示添加ID
	fmt.Println("Error", result.Error)	//错误信息
	fmt.Println("Rows", result.RowsAffected) //改变行数
}

func CreateTable() {
	db.AutoMigrate(&User{})
}

func main() {
	CreateTable()
	InsertValue()
}

⚠️注意,在命名结构体时,我们所要用到的所有字段必须首字母大写,这是一种约定,否则gorm将无法将字段名添加至数据库中

按照字段传值

func InsertValue() {
	user := User{Name: "小红", Age: 12, Birthday: time.Now()}
	db.Select("Name","Age").Create(&user)
	db.Omit("Name", "Age").Create(&user)
}

db.Select(column1,column).Create()指只添加我们查询到的字段名,其他的数据忽略
db.Omit(column1,column2).Create()指忽略我们传的字段名,其他字段名全部添加

|  4 | 2022-06-15 06:20:56.537 | 2022-06-15 06:20:56.537 | NULL       | 小红   |   12 | NULL                    |
|  5 | 2022-06-15 06:22:15.330 | 2022-06-15 06:22:15.330 | NULL       | NULL   | NULL | 2022-06-15 06:22:15.329 |

批量插入

批量插入就是传一个slice给Create(),grom会单独创建出一条sql语句,钩子在这时依然可以进行使用

user := []User{{Name: "小刚", Age: 11}, {Name: "小强", Age: 10}}

钩子

Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。

hook执行顺序

// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  u.UUID = uuid.New()

  if !u.IsValid() {
    err = errors.New("can't save invalid data")
  }
  return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  if u.ID == 1 {
    tx.Model(u).Update("role", "admin")
  }
  return
}

所有对数据库的操作和伴随的hook是一个事务,如果有一项报错,那么之前所有的操作都将被回滚

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
	if u.ID == 1 {
		tx.Model(u).Update("role", "admin")
	}
	fmt.Println("AfterCreate")
	return
}

我们会发现在上传多个数据的时候,会执行多次hook

查询

检索单个对象

// 获取第一条记录(主键升序)
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)

检索多条记录

检索多条记录时一定注意,传入的是一个切片的形式

	var user []User
	db.Find(&user)
	for _, u := range user {
		fmt.Printf("u: %v\n", u.ID)
	}

使用主键进行检索

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

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

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

根据结构体查字段

	var user []User
	db.Where(&User{Name: "小刚"}).Find(&user)
	for _, u := range user {
		fmt.Printf("u: %v\n", u.ID)
	}

根据结构体查询字段

var user User
	db.Select("name").First(&user)
	fmt.Printf("user: %v\n", user.Name)

通过这种方式我们可以检索到name

智能选择字段

新建一个结构体

type UserSelect struct {
	Name string
}

使用

	db.Model(&User{}).Limit(10).Find(&user)
    // SELECT `id`, `name` FROM `users` LIMIT 10
    fmt.Println(user)

我们可以得到表中每一行的name
同时gorm还支持子查询,form子查询以及group子查询

更新

保存所有字段

user.Name = "小小"
	user.Age = 10
	user.Birthday = time.Now()
	db.Save(&user)

db.Save()会保存所有的字段,即使是0值也会被保存
执行上面的语句我们在查询表的时候,可以发现表中多了一条名为小小的数据

更改一列

db.Select("name").Model(&user).Where("id=?", 15).Update("name", "小一")
	fmt.Printf("user.Name: %v\n", user.Name)

上述语句相当于sql语句

SELECT name from UPDATE users SET name=‘小一’, updated_at=‘2022-06-15 09:45:39.965’ WHERE id=15 AND active=true;

更改多列

使用struct更新属性,只能更新非零值

db.Select("name").Model(&user).Where("age=?", 10).Updates(&User{Name: "小三"})

使用map[string]更新属性

	db.Select("name").Model(&user).Where("age=?", 10).Updates(map[string]interface{}{"name":"小三"})

更新多列时,使用到的事updates而不再是update,并且需要传一个结构体或者是map类型的数据

更新选定的字段

我们先定义一个结构体

	var user User
	user.Name = "new_name"
	user.Age = 11
	user.Birthday = time.Now()

有时我们不想把所有的字段全部都进行更改,我们可以使用到select或者是omit

	db.Model(&user).Select("name").Where("age=?", 10).Updates(&user)
	db.Model(&user).Omit("name").Where("age=?", 10).Updates(&user)

select是对所有age为10的数据的name全部进行更改,而omit则是除了name以外的其他字段进行更改

阻止全局更新

db.Model(&user).Select("name").Updates(&user)

像是这种不加任何判断直接进行全局更新的情况,gorm一般是不会允许的,并抛出这样的错误

WHERE conditions required

对此我们必须加一些条件或者使用sql原生语句

db.Exec("UPDATE users SET name = ?", "jinzhu")
// UPDATE users SET name = "jinzhu"

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu"

删除

db.Where(“name = ?”, “小红”).Delete(&user)
// DELETE from emails where id = 10 AND name = “jinzhu”;

直接删除所有名字为小红的数据

根据主键删除

删除主键为10的数据

db.Delete(&user,10)

删除主键为1,2,

db.Delete(&users, []int{1,2,3})

阻止全局删除

db.Exec("DELETE FROM users")
// DELETE FROM users

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
// DELETE FROM users

软删除

我们引入了gorm.Model后,因为gorm.deletedat字段的存在,其实数据并没有被真正的删除,而是在deleted_at中加入了一个时间戳

查找被软删除的记录

db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

永久删除
直接将数据从数据表中删除

db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值