- 开发无止境 -
Data: 2022-07-25 02:23:29Form: JournalClick: 14
Gorm官方文档地址:Gorm
go get -u gorm.io/gorm
gorm.io/driver/mysql
可以留意一下:gen
,和Gorm是一个作者。
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
GORM 提供的配置可以在初始化时使用.
type Config struct {
SkipDefaultTransaction bool
NamingStrategy schema.Namer
Logger logger.Interface
NowFunc func() time.Time
DryRun bool
PrepareStmt bool
DisableNestedTransaction bool
AllowGlobalUpdate bool
DisableAutomaticPing bool
DisableForeignKeyConstraintWhenMigrating bool
}
为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,在初始化时禁用它,会获得大约 30%+ 性能提升。
&gorm.Config{
SkipDefaultTransaction: true,
}
GORM 允许用户通过覆盖默认的NamingStrategy来更改命名约定。
// NamingStrategy tables, columns naming strategy
NamingStrategy schema.Namer
type Namer interface {
TableName(table string) string
SchemaName(table string) string
ColumnName(table, column string) string
JoinTableName(table string) string
RelationshipFKName(Relationship) string
CheckerName(table, column string) string
IndexName(table, column string) string
}
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // 表前缀, table for `User` would be `t_users`
SingularTable: true, // 表单数, table for `User` would be `user` with this option enabled
NoLowerCase: true, // skip the snake_casing of names(跳过蛇形命名,一般不用)
NameReplacer: strings.NewReplacer("CID", "Cid"), // use name replacer to change struct/field name before convert it to db name(一般不用)
},
})
实现gorm.logger.Interface接口以覆盖gorm的日志功能
// Logger
Logger logger.Interface
Gorm 有一个 默认 logger 实现,默认情况下,它会打印慢 SQL 和错误
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Silent, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: false, // 禁用彩色打印
},
)
// 全局模式
&gorm.Config{
Logger: newLogger,
}
// 新建会话模式
tx := db.Session(&Session{Logger: newLogger})
tx.First(&user)
tx.Model(&user).Update("Age", 18)
GORM 定义了这些日志级别:Silent、Error、Warn、Info
&gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
}
Debug 单个操作,将当前操作的 log 级别调整为 logger.Info
db.Debug().Where("name = ?", "jinzhu").First(&User{})
更改创建时间使用的函数
&gorm.Config{
NowFunc: func() time.Time {
return time.Now().Local()
},
}
批量插入的数量,
CreateBatchSize:1000
生成 SQL 但不执行,可以用于准备或测试生成的 SQL,
&gorm.Config{
DryRun: false,
}
PreparedStmt 在执行任何 SQL 时都会创建一个 prepared statement 并将其缓存,以提高后续的效率。
&gorm.Config{
// 禁止
PrepareStmt: false,
}
启用全局 update/delete。
在一个事务中使用 Transaction 方法,GORM 会使用 SavePoint(savedPointName),RollbackTo(savedPointName) 提供嵌套事务支持,可以通过 DisableNestedTransaction 选项关闭它。
在完成初始化后,GORM 会自动 ping 数据库以检查数据库的可用性,若要禁用该特性,可将其设置为 true。
在 AutoMigrate 或 CreateTable 时,GORM 会自动创建外键约束,若要禁用该特性,可将其设置为 true。
禁止该选项,实际开发中一般不用创建外键约束,外键只用代码约束。
DSN:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
var handler *gorm.DB
func init() {
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{
// 不使用默认的事务
SkipDefaultTransaction: false,
// 不建立实际的外键约束,约束靠代码实现
DisableForeignKeyConstraintWhenMigrating: true,
// 表名迁移为单数
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
// 日志
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Println(err)
} else {
fmt.Println(db)
}
}
// &{0xc0000dc630 <nil> 0 0xc0001a0380 1}
GORM 提供了 DB 方法,可用于从当前 *gorm.DB 返回一个通用的数据库接口 *sql.DB
// 获取通用数据库对象 sql.DB,然后使用其提供的功能
sqlDB, err := db.DB()
// Ping
sqlDB.Ping()
// Close
sqlDB.Close()
// 返回数据库统计信息
sqlDB.Stats()
// 获取通用数据库对象 sql.DB ,然后使用其提供的功能
sqlDB, err := db.DB()
// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)
使用 ID 作为主键
GORM 使用结构体名的 蛇形命名 作为表名。对于结构体 User,根据约定,其表名为 users
TableName 不支持动态变化,它会被缓存下来以便后续使用,可以使用 Table 方法临时指定表名
GORM 允许用户通过覆盖默认的命名策略更改默认的命名约定,命名策略被用于构建: TableName、ColumnName、JoinTableName、RelationshipFKName、CheckerName、IndexName。
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`
}
对于有 CreatedAt 字段的模型,创建记录时,如果该字段值为零值,则将该字段的值设为当前时间;对于有 UpdatedAt 字段的模型,更新记录时,将该字段的值设为当前时间。创建记录时,如果该字段值为零值,则将该字段的值设为当前时间;可以通过将 autoUpdateTime 标签置为 false 来禁用时间戳追踪
type User struct {
UpdatedAt time.Time `gorm:"autoUpdateTime:false"`
}
GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt,可以做结构体内嵌。
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type User struct {
gorm.Model
Name string
}
问题原文:
The docs of gorm https://gorm.io/docs/models.html present an example below.
The field Name and Email are described with string and *string. What is the main difference here?
Also how to provide the datatype for the images field storing a list of images link?
Should it be []string or []*string?
答案:
Go has default values for every primitive data types.
int -> 0, string -> “”, bool -> false likewise. So if you need to add null value, or load null value to a variable, it should be a pointer. Otherwise it is defaulted.
Default value of a pointer is nil in Go.
And complex data types such as slices, maps keep references. So their default value is nil. So, Images []string here images can be nil.
Below code with pointer types User1 and without pointer types User2 show the difference in default values.
package main
import (
"fmt"
"time"
)
type User1 struct {
Email *string
Images []string
Birthday *time.Time
}
type User2 struct {
Email string
Images []string
Birthday time.Time
}
func main() {
user1 := User1{}
user2 := User2{}
fmt.Printf("user1 := %+v \n", user1)
//Output : user1 := {Email:<nil> Images:[] Birthday:<nil>}
fmt.Printf("user2 := %+v \n", user2)
//Output : user2 := {Email: Images:[] Birthday:0001-01-01 00:00:00 +0000 UTC}
}
假设定义如下模型:
type Password struct {
gorm.Model
Name string
ManagerID *uint
// Team不会出现在表中
Team []Password `gorm:"foreignkey:ManagerID"`
}
执行新增时:
db.Create(&Password{Name:"p1"}
[11.708ms] [rows:1] INSERT INTO `password` (`created_at`,`updated_at`,`deleted_at`,`name`,`manager_id`) VALUES ('2023-03-29 20:17:20.553','2023-03-29 20:17:20.553',NULL,'p1',NULL)
tag 名大小写不敏感,但建议使用 camelCase 风格
标签名 | 说明 |
---|---|
column | 指定 db 列名 |
type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null 、size , autoIncrement … 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT |
serializer | 指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtime |
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 无读权限 |
- | 忽略该字段,- 表示无读写,-:migration 表示无迁移权限,-:all 表示无读写迁移权限 |
comment | 迁移时为字段添加注释 |
注意:使用 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"` // 仅创建(禁止从 db 读)
Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
}
type User struct {
CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
UpdatedAt int // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
Updated int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳纳秒数填充更新时间
Updated int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间
Created int64 `gorm:"autoCreateTime"` // 使用时间戳秒数填充创建时间
}
对于匿名字段,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
}
// 等效于
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}
db.AutoMigrate(&User{})
或
db.AutoMigrate(new(User))
db.AutoMigrate(&User{}, &Product{}, &Order{})
或
db.AutoMigrate(new(User),new(Product),new(Order))
// 创建表时添加后缀
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
type Migrator interface {
// AutoMigrate
AutoMigrate(dst ...interface{}) error
// Database
CurrentDatabase() string
FullDataTypeOf(*schema.Field) clause.Expr
// Tables
CreateTable(dst ...interface{}) error
DropTable(dst ...interface{}) error
HasTable(dst interface{}) bool
RenameTable(oldName, newName interface{}) error
GetTables() (tableList []string, err error)
// Columns
AddColumn(dst interface{}, field string) error
DropColumn(dst interface{}, field string) error
AlterColumn(dst interface{}, field string) error
MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error
HasColumn(dst interface{}, field string) bool
RenameColumn(dst interface{}, oldName, field string) error
ColumnTypes(dst interface{}) ([]ColumnType, error)
// Constraints
CreateConstraint(dst interface{}, name string) error
DropConstraint(dst interface{}, name string) error
HasConstraint(dst interface{}, name string) bool
// Indexes
CreateIndex(dst interface{}, name string) error
DropIndex(dst interface{}, name string) error
HasIndex(dst interface{}, name string) bool
RenameIndex(dst interface{}, oldName, newName string) error
}
获取Migrator对象
migrator := handler.Migrator()
返回当前使用的数据库名
migrator.CurrentDatabase()
表操作
// 为 `User` 创建表
db.Migrator().CreateTable(&User{})
// 将 "ENGINE=InnoDB" 添加到创建 `User` 的 SQL 里去
db.Set("gorm:table_options", "ENGINE=InnoDB").Migrator().CreateTable(&User{})
// 检查 `User` 对应的表是否存在
db.Migrator().HasTable(&User{})
db.Migrator().HasTable("users")
// 如果存在表则删除(删除时会忽略、删除外键约束)
db.Migrator().DropTable(&User{})
db.Migrator().DropTable("users")
// 重命名表
db.Migrator