概述
gorm是go语言编写的开发者友好的orm库。
有如下特性:
- 全功能的ORM。
- 关系(一对一,一对多,多对多,多态,单表继承)。
- 钩子函数,在
Create/Save/Update/Delete/Find之前或者之后操作。 - 使用
Preload,Joins预加载 - 事务,嵌套事务,保存点,回滚保存点
- Context, Prepared Statement模式,DryRun模式
- 批量插入,批量查询,使用Map查找和创建,使用SQL表达式和context valuer进行CRUD。
- SQL构建器,UPSERT,加锁,优化器/索引/注释提示,命名参数,子查询
- 复合主键,索引,约束。
- 自动迁移,这里的迁移表示会根据struct类型创建数据库表。
- 日志记录器
- 可扩展,灵活的插件API:数据库解析器(多数据库,读写分离)
- 每个功能都有测试
- 开发者友好
安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite快速启动
package main
import (
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(&Product{})
// Create
db.Create(&Product{Code: "D42", Price: 100})
// Read
var product Product
db.First(&product, 1) // find product with integer primary key
db.First(&product, "code = ?", "D42") // find product with code D42
// Update - update product's price to 200
db.Model(&product).Update("Price", 200)
// Update - update multiple fields
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
// Delete - delete product
db.Delete(&product, 1)
}声明Model
模型是带有基本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,UpdateAt来跟踪创建和更新时间。
遵循约定可以少些很多代码,如果约定不符合需求,也可以自己配置。
gorm.Model
这是GORM定义的结构体,其中包含了约定中提到的字段,可以将其嵌入到自己的结构体中。
// gorm.Model definition
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}只要遵循约定,那么这个嵌入这个结构体可以帮助节省很多工作。
进阶
字段级别权限
使用GORM进行CRUD的时候导出字段有所有的权限,GORM允许使用标签来改变字段级别的权限。因此可以设置字段为只读,只写,只能创建,只能更新或者忽略。
当使用GORM Migrator创建表时,不会创建被忽略的字段。
type User struct {
Name string `gorm:"<-:create"` // 允许读和创建
Name string `gorm:"<-:update"` // 允许读和更新
Name string `gorm:"<-"` // 允许读和写
Name string `gorm:"<-:false"` // 允许读,不允许写
Name string `gorm:"->"` // 只读
Name string `gorm:"->;<-:create"` // 允许读和创建
Name string `gorm:"->:false;<-:create"` // 只能创建
Name string `gorm:"-"` // 读写时忽略此字段
Name string `gorm:"-:all"` // 读写,migrate忽略此字段
Name string `gorm:"-:migration"` // migrate时忽略此字段
}Creating/Updating时间
GORM约定使用CreateAt,UpdateAt来跟踪创建和更新时间,只要设置了这两个字段,那么就会在对应的create或者update操作中将数据库中对应字段设置为当前时间。
要使用不同名称的字段,可以用autoCreateTime、autoUpdateTime标签配置这些字段。如果希望保存UNIX(Milli/Nano)秒而不是时间,则可以简单地更改字段的数据类型为int。
type User struct {
CreatedAt time.Time // Set to current time if it is zero on creating
UpdatedAt int // Set to current unix seconds on updating or if it is zero on creating
Updated int64 `gorm:"autoUpdateTime:nano"` // Use unix nano seconds as updating time
Updated int64 `gorm:"autoUpdateTime:milli"`// Use unix milli seconds as updating time
Created int64 `gorm:"autoCreateTime"` // Use unix seconds as creating time
}嵌入结构体
对于匿名字段,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
}对于普通的结构体字段,可以使用embedded标签,例如
type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
等价于:
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}还可以使用embeddedPrefix标签来为嵌入的字段对应的db字段名加上前缀。例如
type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
}
// equals
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}字段标签
在声明model的时候标签时可选的。标签时大小写敏感的,使用驼峰式命令风格,GORM支持下面的标签。
| 标签名 | 描述 |
|---|---|
| column | 数据库中列的字段名 |
| type | 列的类型 |
| serializer | 如何序列化和反序列化到数据库,例如serializer:json/gob/unixtime |
| size | 指定列的数据大小或者长度 |
| primaryKey | 指定列为主键 |
| unique | 指定列为unique约束 |
| default | 指定列的默认值 |
| precision | 指定列精度 |
| scale | 指定列的数据范围 |
| not null | 指定列非空 |
| autoIncrement | 指定列自增的 |
| autoIncrementIncrement | 指定列自增的步长 |
| embedded | 嵌入字段 |
| embeddedPrefix | 为嵌入字段设置列名前缀 |
| autoCreateTime | 设置create时间为当前时间,如果类型为int,那么记录为unix秒,使用autoCreateTime:nano/milli来记录unix纳秒和毫秒。 |
| autoUpdateTime | 在create/update的时候设置为当前时间,标签值的作用与上一个标签相同。 |
| index | 创建索引,如果多个字段的此标签的值相同,那么会创建多字段的复合索引 |
| uniqueIndex | 与index作用相同,但是创建唯一索引 |
| check | 创建检查约束,例如:check:age > 13 |
| ← | 设置字段的写权限,<-:create是只能创建的字段,<-:update只能更新,<-:false没有写权限,<-可以创建与更新 |
| → | 设置读权限,->:false表示没有读权限 |
| - | 忽略此字段,-表示没有读写权限,-:migration没有迁移权限,-all没有读写迁移权限。 |
| comment | 在migrate的时候为字段添加注释 |
关联标签
在关系型数据库中,表与表之间是存在关联关系的,GORM提供了关联标签,方便对表之间的关联关系进行设置。
有如下关联标签:
| 标签名 | 描述 |
|---|---|
| foreignKey | 指定外键。 |
| references | 指定参考表的列名,该名称映射到联接表的外键 |
| polymorphic | 指定多态类型,例如model名 |
| polymorphicValue | 指定多态值,默认是表名 |
| many2many | 指定连接表名 |
| joinForeignKey | 指定映射到当前表的连接表的外键列名 |
| joinReferences | 指定映射到参考表的连接表的外键列名 |
| constraint | 关系约束,例如onUpdate,onDelete |
连接数据库
GORM官方支持 MySQL, PostgreSQL, SQLite, SQL Server。
mysql
导入对应的包
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)设置dsn来连接数据库
func main() {
// refer https://github.com/go-sql-driver/mysql#dsn-data-source-name for details
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{})
}Note
为了正确处理
time.Time,需要包含parseTime参数,同时为了全面支持utf8编码,将charset=utf8改为charset=utf8mb4
MYSQL驱动,支持在初始化的时候进行高级配置,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // 数据源的名称
DefaultStringSize: 256, // string字段的默认大小
DisableDatetimePrecision: true, // 禁用datetime精度,MYSQL5.6之前版本不支持
DontSupportRenameIndex: true, // 重命名索引先删除再创建,MYSQL5.7之前不支持
DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
SkipInitializeWithVersion: false, // 基于当前的MYSQL版本自动配置
}), &gorm.Config{})自定义驱动
GORM可以使用DriverName选项自定义MYSQL驱动,例如:
import (
_ "example.com/my_mysql_driver"
"gorm.io/gorm"
)
db, err := gorm.Open(mysql.New(mysql.Config{
DriverName: "my_mysql_driver",
DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name, refer https://github.com/go-sql-driver/mysql#dsn-data-source-name
}), &gorm.Config{})已存在的数据库连接
GORM可以使用已经存在的数据库连接初始化*gorm.DB
import (
"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
sqlDB, err := sql.Open("mysql", "mydb_dsn")
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: sqlDB,
}), &gorm.Config{})PostgreSQL
使用与mysql基本相同,无非就是使用的驱动不同
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
dsn := "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})也可以像mysql一样使用自定义驱动和利用已建立连接。
连接池
GORM使用database/sql来维持连接池。
sqlDB, err := db.DB()
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns sets the maximum number of open connections to the database.
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
sqlDB.SetConnMaxLifetime(time.Hour)tags: gorm