GORM教程

  • Post author:
  • Post category:其他




GORM

gorm是一个使用Go语言编写的ORM框架。它文档齐全,对开发者友好,支持主流数据库。



安装

go get -u gorm.io/gorm



连接数据库



引入驱动

import "gorm.io/driver/mysql"
import "gorm.io/driver/postgres"
import "gorm.io/driver/sqlite"
import "gorm.io/driver/mssql"

可以根据自己的需要选择对应的包



连接数据库(mysql)

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

func main() {
    
  dsn:="root:123456@(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

第一个包是gorm框架包,第二个是对应的mysql驱动。

连接mysql数据库需要按照上面的格式,输入用户名和密码,以及数据库对应IP地址以及端口号,dbname是数据库的名字,

charset=utf8mb4

支持完整的utf-8编码,想要正确的处理

time.Time

,需要带上

parseTime

参数



GORM Model

模型是标准的 struct,由 Go 的基本数据类型、实现了

Scanner



Valuer

接口的自定义类型及其指针或别名组成

例如:

type User struct {
  ID           uint
  Name         string
  Email        *string
  Age          uint8
  Birthday     *time.Time
  MemberNumber sql.NullString
  ActivatedAt  sql.NullTime
  CreatedAt    time.Time
  UpdatedAt    time.Time
}

GORM 倾向于约定,而不是配置。默认情况下,GORM 使用

ID

作为主键,使用结构体名的

蛇形复数

作为表名,字段名的

蛇形

作为列名,并使用

CreatedAt



UpdatedAt

字段追踪创建、更新时间



嵌入结构体

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

type User struct {
  gorm.Model
  Name string
}
// 等效于
type User struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
  Name string
}



字段标签

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

camelCase

风格

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:

not null



size

,

autoIncrement

… 像

varbinary(8)

这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:

MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT
size 指定列大小,例如:

size:256
primaryKey 指定列为主键
unique 指定列为唯一
default 指定列的默认值
precision 指定列的精度
scale 指定列大小
not null 指定列为 NOT NULL
autoIncrement 指定列为自动增长
autoIncrementIncrement 自动步长,控制连续记录之间的间隔
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

无读权限
忽略该字段,

-

无读写权限
comment 迁移时为字段添加注释

示例

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"` // 设置会员号(member number)唯一并且不为空
  Num          int     `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
  Address      string  `gorm:"index:addr"` // 给address字段创建名为addr的索引
  IgnoreMe     int     `gorm:"-"` // 忽略本字段
}



GORM操作MySQL

package main

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

// UserInfo 用户信息
type UserInfo struct {
	ID uint
	Name string
	Gender string
	Hobby string
}


func main() {
	dsn:="root:123456@(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err!= nil{
		panic(err)
	}
	

	// 自动迁移
	db.AutoMigrate(&UserInfo{})

	u1 := UserInfo{1, "李华", "男", "篮球"}
	u2 := UserInfo{2, "刘丽", "女", "足球"}
	// 创建记录
	db.Create(&u1)
	db.Create(&u2)
	// 查询
	var u = new(UserInfo)
	db.First(u)
	fmt.Printf("%#v\n", u)

	var uu UserInfo
	db.Find(&uu, "hobby=?", "足球")
	fmt.Printf("%#v\n", uu)

	// 更新
	db.Model(&u).Update("hobby", "双色球")
	// 删除
	db.Delete(&u)
}



GRUD接口



创建



创建记录

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
db.Create(&user) 



用指定的字段创建记录

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

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

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

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)

for _, user := range users {
  user.ID // 1,2,3
}

使用

CreateInBatches

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

var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}

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



根据 Map 创建

GORM 支持根据

map[string]interface{}



[]map[string]interface{}{}

创建记录,例如:

db.Model(&User{}).Create(map[string]interface{}{
  "Name": "jinzhu", "Age": 18,
})

// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{
  {"Name": "jinzhu_1", "Age": 18},
  {"Name": "jinzhu_2", "Age": 20},
})

根据 map 创建记录时,association 不会被调用,且主键也不会自动填充



默认值

您可以通过标签

default

为字段定义默认值,如:

type User struct {
  ID   int64
  Name string `gorm:"default:galeone"`
  Age  int64  `gorm:"default:18"`
}

插入记录到数据库时,默认值会被用于 填充值为 零值的字段



0



''



false

等零值,不会将这些字段定义的默认值保存到数据库。您需要使用指针类型或 Scanner/Valuer 来避免这个问题,例如:

type User struct {
  gorm.Model
  Name string
  Age  *int           `gorm:"default:18"`
  Active sql.NullBool `gorm:"default:true"`
}



查询



检索单个对象

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

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


First

,

Last

方法将按主键排序查找第一/最后一条记录,只有在用struct查询或提供model value时才有效,如果当前model没有定义主键,将按第一个字段排序

var user User

// 可以
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 可以
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)

// 根据第一个字段排序
type Language struct {
  Code string
  Name string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1



用主键检索

如果主键是数值类型,也可以通过 内联条件传入主键来检索对象。如果主键是 string 类型,要小心避免 SQL 注入。

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);



检索全部对象

// 获取全部记录
result := db.Find(&users)
// SELECT * FROM users;

result.RowsAffected // 返回找到的记录数,相当于 `len(users)`
result.Error        // returns error



条件

这里只取一小部分的。

// 获取第一条匹配的记录
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';



更新



保存所有字段


Save

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

db.First(&user)

user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;



更新单个列

当使用

Update

更新单个列时,你需要指定条件,否则会返回

ErrMissingWhereClause

。当使用了

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(User{Name: "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

来完成更新操作



删除



删除一条记录

删除一条记录时,删除对象需要指定主键,否则会触发 批量 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 允许通过内联条件指定主键来检索对象,但只支持整型数值,因为 string 可能导致 SQL 注入。

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

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

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



批量删除

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

db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
// DELETE from emails where email LIKE "%jinzhu%";

db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";



软删除

如果您的模型包含了一个

gorm.DeletedAt

字段(

gorm.Model

已经包含了该字段),它将自动获得软删除的能力!

拥有软删除能力的模型调用

Delete

时,记录不会被从数据库中真正删除。但 GORM 会将

DeletedAt

置为当前时间, 并且你不能再通过正常的查询方法找到该记录。

// user 的 ID 是 `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

如果您不想引入

gorm.Model

,您也可以这样启用软删除特性:

type User struct {
  ID      int
  Deleted gorm.DeletedAt
  Name    string
}



查找被软删除的记录

您可以使用

Unscoped

找到被软删除的记录

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



永久删除

您也可以使用

Unscoped

永久删除匹配的记录

db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;



版权声明:本文为weixin_48101150原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。