特点
- 全功能 ORM
- 关联 (拥有一个,拥有多个,属于,多对多,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持 Preload、Joins 的预加载
- 事务,嵌套事务,Save Point,Rollback To to Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- 自动迁移
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus...
- 每个特性都经过了测试的重重考验
- 开发者友好
约定(重点)
主键
GORM 使用一个名为ID
每个模型的默认主键的字段。
type User struct {
ID string // 默认情况下,名为 `ID` 的字段会作为表的主键
Name string
}
可以通过标签 primaryKey
将其它字段设为主键
// 将 `UUID` 设为主键
type Animal struct {
ID int64
UUID string `gorm:"primaryKey"`
Name string
Age int64
}
表名称
默认情况下,GORM 将结构名称转换为snake_case
表名称并将其复数化。例如,一个User
结构体出现users
在数据库中。
您可以实现 Tabler
接口来更改默认表名,例如:
type Tabler interface {
TableName() string
}
// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
return "profiles"
}
注意: TableName
不支持动态变化,它会被缓存下来以便后续使用。想要使用动态表名,你可以使用 Scopes
,例如:
func UserTable(user User) func (tx *gorm.DB) *gorm.DB {
return func (tx *gorm.DB) *gorm.DB {
if user.Admin {
return tx.Table("admin_users")
}
return tx.Table("users")
}
}
db.Scopes(UserTable(user)).Create(&user)
临时指定表名
您可以使用 Table
方法临时指定表名,例如:
// 根据 User 的字段创建 `deleted_users` 表
db.Table("deleted_users").AutoMigrate(&User{})
// 从另一张表查询数据
var deletedUsers []User
db.Table("deleted_users").Find(&deletedUsers)
// SELECT * FROM deleted_users;
db.Table("deleted_users").Where("name = ?", "jinzhu").Delete(&User{})
// DELETE FROM deleted_users WHERE name = 'jinzhu';
列名
GORM 自动将结构体字段名转换为snake_case
数据库中的列名。
type User struct {
ID uint // 列名是 `id`
Name string // 列名是 `name`
Birthday time.Time // 列名是 `birthday`
CreatedAt time.Time // 列名是 `created_at`
}
您可以使用 column
标签或 命名策略 来覆盖列名
type Animal struct {
AnimalID int64 `gorm:"column:beast_id"` // 将列名设为 `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设为 `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // 将列名设为 `age_of_the_beast`
}
你可以通过将 autoCreateTime
标签置为 false
来禁用时间戳追踪,例如:
type User struct {
CreatedAt time.Time `gorm:"autoCreateTime:false"`
}
时间戳字段
GORM 使用名为CreatedAt
和的字段UpdatedAt
来自动跟踪记录的创建和更新时间。
对于有 CreatedAt
字段的模型,创建记录时,如果该字段值为零值,则将该字段的值设为当前时间
db.Create(&user) // 将 `CreatedAt` 设为当前时间
user2 := User{Name: "jinzhu", CreatedAt: time.Now()}
db.Create(&user2) // user2 的 `CreatedAt` 不会被修改
// 想要修改该值,您可以使用 `Update`
db.Model(&user).Update("CreatedAt", time.Now())
对于有 UpdatedAt
字段的模型,更新记录时,将该字段的值设为当前时间。创建记录时,如果该字段值为零值,则将该字段的值设为当前时间
db.Save(&user) // 将 `UpdatedAt` 设为当前时间
db.Model(&user).Update("name", "jinzhu") // 会将 `UpdatedAt` 设为当前时间
db.Model(&user).UpdateColumn("name", "jinzhu") // `UpdatedAt` 不会被修改
user2 := User{Name: "jinzhu", UpdatedAt: time.Now()}
db.Create(&user2) // 创建记录时,user2 的 `UpdatedAt` 不会被修改
user3 := User{Name: "jinzhu", UpdatedAt: time.Now()}
db.Save(&user3) // 更新时,user3 的 `UpdatedAt` 会修改为当前时间
你可以通过将 autoUpdateTime
标签置为 false
来禁用时间戳追踪,例如:
type User struct {
UpdatedAt time.Time `gorm:"autoUpdateTime:false"`
}
安装
要在有mod文件的文件夹下面执行下面的命令
go get -u gorm.io/gorm
连接到数据库(mysql)
安装mysql驱动
go get -u gorm.io/driver/mysql
编写测试连接
基础版
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 root:123456@tcp(192.168.31.131:3306)/gotest
dsn := "root:123456@tcp(192.168.31.131:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接失败")
return
}
fmt.Println("连接成功", db)
}
高级版
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 root:123456@tcp(192.168.31.131:3306)/gotest
dsn := "root:123456@tcp(192.168.31.131:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: dsn, // 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{})
if err != nil {
fmt.Println("连接失败")
return
}
fmt.Println("连接成功", db)
}
crud
准备数据库和结构体
package main
import (
"errors"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Stu struct {
Id int `gorm:"primaryKey"`
Name string
Age int
Address string
}
func getDb() *gorm.DB {
dsn := "root:123456@tcp(192.168.31.131:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接失败")
panic(errors.New("连接失败"))
}
fmt.Println("连接成功", db)
return db
}
注意结构体中的属性首字母要大写,否则就不可见,还要指定主键
查询
需要带条件的可以自己查看api
查询一个
first
获取第一条记录(主键升序)
func main() {
db := getDb()
testFirst(db)
}
func testFirst(db *gorm.DB) {
var stu Stu
res := db.Table("stu").First(&stu)
if res.Error != nil {
fmt.Println("查询数据失败")
return
}
fmt.Println(stu)
}
Take
获取一条记录,没有指定排序字段
func testTake(db *gorm.DB) {
var stu Stu
res := db.Table("stu").Take(&stu)
if res.Error != nil {
fmt.Println("查询数据失败")
return
}
fmt.Println(stu)
}
Last
获取最后一条记录(主键降序)
func testLast(db *gorm.DB) {
var stu Stu
res := db.Table("stu").Last(&stu)
if res.Error != nil {
fmt.Println("查询数据失败")
return
}
fmt.Println(stu)
}
批量查询
func testMany(db *gorm.DB) {
var stus = make([]Stu, 0)
//相当于条件是id为10
_ = db.Table("stu").Find(&stus)
fmt.Println(stus)
}
新增
如果表不存在,会创建表
新增一个
func testAddOne(db *gorm.DB) {
stu := Stu{Name: "新增名称", Age: 11, Address: "新增地址"}
tx := db.Table("stu").Create(&stu)
if tx.Error != nil {
fmt.Println("新增失败")
return
}
fmt.Println(stu.Id)
}
批量新增
func testAddMany(db *gorm.DB) {
stus := []Stu{{Name: "批量新增名称1", Age: 11, Address: "批量新增地址1"}, {Name: "批量新增名称2", Age: 11, Address: "批量新增地址2"}}
tx := db.Table("stu").Create(&stus)
if tx.Error != nil {
fmt.Println("新增失败")
return
}
fmt.Println(stus)
}
更新
更新一个
func testUpdateOne(db *gorm.DB) {
var stu Stu
_ = db.Table("stu").First(&stu)
stu.Name = "更新后名字"
db.Table("stu").Save(&stu)
}
func testUpdateOne1(db *gorm.DB) {
db.Table("stu").Where("id=?", 72).Update("name", "更新").Update("address", "跟新地址")
}
批量更新
func testUpdateMany(db *gorm.DB) {
db.Table("stu").Where("id in (?)", []int{1,2,3,43}).Update("name", "更新").Update("address", "跟新地址")
}
删除
func testDeleteMany(db *gorm.DB) {
db.Table("stu").Where("id in (?)", []int{1, 2, 3, 43}).Delete(&Stu{})
}