特性:
- 功能齐全的ORM
- 关联(一个、有多个、属于、多对多、多态性、单表继承)
- 钩子(Hooks)(创建/保存/更新/删除/查找之前/之后)
- 使用Preload和join进行快速加载
- 事务,嵌套事务,保存点,回滚到保存点
- 上下文,准备语句模式,DryRun模式
- 批量插入,批量查找,查找映射
- SQL Builder, Upsert,锁定,优化器/索引/注释提示,命名参数,用SQL Expr 搜索/更新/创建
- 复合主键
- 自动迁移
- 日志
- 可扩展的,灵活的插件API:数据库解析器(多数据库,读/写拆分)/ Prometheus…
- 每个特性都可测试
- 开发友好
Go 使用MySQL数据库
Go中支持MySQL的驱动目前比较多,有如下几种,有些是支持database/sql标准,而有些是采用了自己的实现接口,常用的有如下几种:
https://github.com/go-sql-driver/mysql 支持database/sql,全部采用go写。
https://github.com/ziutek/mymysql 支持database/sql,也支持自定义的接口,全部采用go写。
主要以第一个驱动为例(我目前项目中也是采用它来驱动),也推荐大家采用它,主要理由:
这个驱动比较新,维护的比较好
完全支持database/sql接口
支持keepalive,保持长连接,虽然mymysql也支持keepalive,但不是线程安全的,这个从底层就支持了keepalive。
1、概览
go get -u gorm.io/gorm
go get -u 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)
}
2、声明模型(Declaring Models)
2.1 Declaring Models
模型是具有基本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
}
2.2 约定(Conventions)
GORM更喜欢约定而不是配置。默认情况下,GORM使用ID
作为主键,将结构名复数为snake_cases
作为表名,snake_case
作为列名,并使用CreatedAt
、UpdatedAt
跟踪创建/更新时间
如果您遵循GORM采用的约定,您将需要编写非常少的配置/代码。如果约定不符合您的需求,GORM允许您配置它们
2.3 gorm.Model
GORM defined a gorm.Model struct, which includes fields ID, CreatedAt, UpdatedAt, DeletedAt
// gorm.Model definition
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
可以将其嵌入到结构中以包含这些字段,参考嵌入式结构体
2.4 高级设置
2.4.1 字段级权限
当使用GORM进行CRUD时,导出的字段具有所有权限,并且GORM允许您使用标记更改字段级别的权限,因此您可以将字段设置为只读、只写、仅创建、仅更新或忽略 (read-only, write-only, create-only, update-only or ignored)
当使用GORM Migrator创建表时,忽略字段将不会被创建
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
}
2.4.2 Creating/Updating Time/Unix (Milli/Nano) Seconds Tracking
GORM使用CreatedAt
, UpdatedAt
来按照约定跟踪创建/更新时间,如果定义了字段,GORM将在创建/更新时设置当前时间
要使用不同名称的字段,您可以使用标记autoCreateTime
, autoUpdateTime
来配置这些字段
如果您希望保存 UNIX (milli/nano) 秒而不是时间,您可以简单地根据更改字段的数据类型 time.Time
到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
}
2.4.3 嵌入式结构
对于匿名字段(anonymous fields),GORM将把它的字段包含到它的父结构体中,例如:
type User struct {
gorm.Model
Name string
}
// equals
type User struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
}
对于一个普通的struct字段,你可以embedded
标签,例如:
type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// equals
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
}
2.4.4 字段标签
在声明模型(model
)时,标签是可选的,GORM支持以下标签:
标签是不区分大小写的,但是camelCase是首选的。
Tag Name | Description |
---|---|
column | column db name |
type | column data type, 首选使用兼容的通用型, e.g: bool , int , uint , float , string , time , bytes , 对所有数据库都适用, 可以和其他标签(tag)一起使用, like not null , size , autoIncrement … specified database data type like varbinary(8) also supported, when using specified database data type, it needs to be a full database data type, for example: MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT |
serializer | specifies serializer for how to serialize and deserialize data into db, e.g: serializer:json/gob/unixtime |
size | specifies column data size/length, e.g: size:256 |
primaryKey | specifies column as primary key |
unique | specifies column as unique |
default | specifies column default value |
precision | specifies column precision |
scale | specifies column scale |
not null | specifies column as NOT NULL |
autoIncrement | specifies column auto incrementable |
autoIncrementIncrement | auto increment step, 控制连续列值之间的间隔 |
embedded | embed the field |
embeddedPrefix | column name prefix for embedded fields |
autoCreateTime | track current time when creating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoCreateTime:nano |
autoUpdateTime | track current time when creating/updating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoUpdateTime:milli |
index | create index with options, use same name for multiple fields creates composite indexes, refer Indexes for details |
uniqueIndex | same as index , but create uniqued index |
check | creates check constraint, eg: check:age > 13 , refer Constraints |
<- | set field’s write permission, <-:create create-only field, <-:update update-only field, <-:false no write permission, <- create and update permission |
-> | set field’s read permission, ->:false no read permission |
- | ignore this field, - no read/write permission, -:migration no migrate permission, -:all no read/write/migrate permission |
comment | add comment for field when migration |
2.4.5 关联标签(Associations Tags)
GORM允许配置外键(foreign keys),约束(constraints),多对多(many2many)表通过标签的关联,查看关联部分的详细信息
3、连接数据库
GORM正式支持MySQL、PostgreSQL、SQLite、SQL Server、TiDB等数据库
3.1 MySQL
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
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{})
}
要正确处理
time.Time
,您需要包括parseTime
作为参数。(更多参数)
要完全支持UTF-8编码,需要将charset=utf8
更改为charset=utf8mb4
。有关详细解释,请参阅这篇文章
MySQL Driver提供了一些可以在初始化时使用的高级配置,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name
DefaultStringSize: 256, // string 字段的默认大小
DisableDatetimePrecision: true, // 禁用datetime精度, which not supported before MySQL 5.6
DontSupportRenameIndex: true, // drop & create when rename index, rename index not supported before MySQL 5.7, MariaDB
DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
SkipInitializeWithVersion: false, // 根据当前MySQL版本自动配置
}), &gorm.Config{})
3.1.1 定制 Driver
GORM允许使用DriverName
选项自定义MySQL驱动程序,例如:
import (
_ "example.com/my_mysql_driver"
"gorm.io/driver/mysql"
"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{})
3.1.2 已有数据库连接
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{})
3.2 连接池
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)
详细信息请参阅通用接口(Generic Interface)
3.3 Unsupported Databases
有些数据库可能与mysql
或postgres
dialect兼容,在这种情况下,您可以在这些数据库中使用dialect。