intro
package mainimport ("gorm.io/gorm""gorm.io/driver/sqlite" // GORM 使用该驱动来连接和操作 SQLite 数据库。
)type Product struct {gorm.Model // 嵌入GORM 内置的模型结构,包含 ID、CreatedAt、UpdatedAt、DeletedAt 四个字段Code stringPrice uint
}func main() {db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) // 使用 SQLite 驱动打开名为 "test.db" 的数据库文件,并用默认配置初始化 GORMif err != nil {panic("failed to connect database")}// 迁移 schemadb.AutoMigrate(&Product{}) // 根据 Product 结构体自动创建或更新数据库中的表结构,确保数据表与结构体一致。// Create 在数据库中插入一条新记录db.Create(&Product{Code: "D42", Price: 100})// Readvar product Product // 用来存储查询结果db.First(&product, 1) // 根据整型主键查找db.First(&product, "code = ?", "D42") // 通过条件查找,查找 code 字段值为 D42 的记录// Update - 将 product 的 price 更新为 200db.Model(&product).Update("Price", 200)// Update - 更新多个字段// 使用结构体 Product 作为参数更新多个字段,此方式只会更新结构体中非零值的字段。db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段// 使用 map 更新字段,可以同时更新多个字段,不受非零值的限制db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})// Delete - 删除 productdb.Delete(&product, 1)
}
gorm.Config{}
:这是一个结构体字面量,用于创建一个gorm.Config
类型的实例,其中的各个字段都被赋予了默认的零值(如果没有显式指定)。&
运算符:在结构体字面量前加&
,表示取这个实例的地址,也就是生成一个指向gorm.Config
实例的指针。- 传递指针而非值,可以使函数
gorm.Open
修改或读取配置,同时也避免了结构体值的拷贝。
数据库迁移(schema migration)
- 定义:数据库迁移是指对数据库表结构进行创建、修改或删除的过程。
- 自动迁移:GORM 提供了
AutoMigrate
方法,可以根据你的 Go 结构体(模型)自动创建或更新数据库中的表结构。- 示例:
db.AutoMigrate(&User{})
会检查User
模型,并自动生成或调整表结构来匹配这个模型。
- 示例:
- 优点:
- 让你无需手动编写 SQL 语句来创建或修改表结构。
- 保证模型和数据库结构保持一致。
- 局限性:
- 对于复杂的结构变更(如大规模数据迁移、索引优化等),可能需要手动编写迁移脚本。
结构体标签和反射
-
结构体标签:了解 Go 中标签的语法和使用场景,特别是如何利用标签为字段添加元信息。
- 概念:在 Go 中,你可以在结构体字段后面加上反引号(```)内的标签,这些标签是一些字符串,用来存储额外的信息。
- 用途:
- 用于告诉 GORM 如何将这个字段映射到数据库中的列,比如指定列名、数据类型、约束条件等。
- 也常见于 JSON 序列化,告诉程序如何将字段转换为 JSON 格式。
type User struct {Name string `gorm:"column:user_name;type:varchar(100);not null" json:"name"`Email string `gorm:"unique;not null" json:"email"` } // Name 字段在数据库中将映射为 user_name 列,类型为 varchar(100) 且不能为空 // 同时在 JSON 中使用 name 作为键
-
反射机制:虽然不必深入,但需要了解反射是如何在运行时读取结构体标签并据此执行操作的。
-
反射是 Go 语言的一种特性,它允许程序在运行时检查变量的类型、结构以及标签等信息。
-
用途:
- GORM 就是通过反射读取结构体标签,从而了解如何把结构体字段和数据库表的列对应起来。
- 反射可以动态地获取类型信息,使得代码更灵活,但同时也会带来一定的性能开销。
import ("fmt""reflect" )type User struct {Name string `gorm:"column:user_name" json:"name"` }func main() {user := User{Name: "Alice"}t := reflect.TypeOf(user)field, _ := t.FieldByName("Name")fmt.Println("GORM标签:", field.Tag.Get("gorm"))fmt.Println("JSON标签:", field.Tag.Get("json")) }
这段代码利用反射获取了
User
结构体中Name
字段的gorm
和json
标签信息。
-
time.Time
的基本用法和常见操作
GORM 会自动处理时间戳字段。
获取当前时间:now := time.Now()// 使用 Format 方法将时间转换为字符串
formatted := now.Format("2006-01-02 15:04:05")// 使用 time.Parse 将字符串转换为 time.Time 类型
t, err := time.Parse("2006-01-02", "2025-04-06")// 比较时间:可以使用 Before、After、Equal 等方法比较两个时间的先后。
模型定义
模型定义
- 映射机制:GORM 将 Go 语言中的结构体(struct)映射为数据库中的表。定义模型就是编写对应的结构体。
- 字段类型:模型字段可以使用基本类型(如
uint
、string
、uint8
)、指针(如*string
、*time.Time
)以及特殊类型(如sql.NullString
、sql.NullTime
)来处理可空值。 - 自动管理的时间戳:如果定义了
CreatedAt
和UpdatedAt
字段,GORM 会在创建和更新记录时自动填充当前时间。 - 非导出字段:结构体中首字母小写的字段不会映射到数据库中。
内置模型(gorm.Model)
- GORM 提供了预定义结构体
gorm.Model
,包含了常用的字段:ID
(主键)CreatedAt
(创建时间)UpdatedAt
(更新时间)DeletedAt
(软删除字段,支持索引)
- 嵌入
gorm.Model
可以让你快速拥有一套标准的字段。
约定与自动行为
-
主键约定:默认使用名为
ID
的字段作为主键。 -
表名和列名:GORM 会自动将结构体名转换为 snake_case 的复数形式(例如
User
变为表名users
),而字段名也转换为 snake_case。 -
自动时间戳:除了自动填充
CreatedAt
与UpdatedAt
外,还支持通过标签(如autoCreateTime
、autoUpdateTime
)自定义时间精度(秒、毫秒、纳秒)。type User struct {CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充UpdatedAt int // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充Updated int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳纳秒数填充更新时间Updated int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间Created int64 `gorm:"autoCreateTime"` // 使用时间戳秒数填充创建时间 }
标签和字段配置
-
字段级控制:使用标签可以设置字段的数据库列名、数据类型、大小、默认值以及约束(如非空、唯一、索引等)。
type Product struct {ID uint `gorm:"primaryKey"` // 主键字段Code string `gorm:"column:product_code;type:varchar(100);not null;unique;index"` // 指定列名为 product_code,数据类型为 varchar(100),非空、唯一并创建索引Price uint `gorm:"default:0;not null"` // 默认值为 0,且不允许为空Description string `gorm:"type:text;default:'no description'"` // 指定数据类型为 text,并设置默认描述 }
- column:将
Code
字段映射到数据库的product_code
列。 - type:设置数据库中的数据类型。
- not null、unique、index:添加非空、唯一和索引约束。
- default:指定默认值。
- column:将
-
权限控制:标签还支持配置字段在 CRUD 操作中的读写权限(例如只创建、只更新或完全忽略)。
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 迁移会忽略该字段 }
-
理解箭头方向
-
<-
表示写入(写操作)数据从外部传入数据库时走
<-
。(意味着在写入操作(无论创建或更新)时允许该字段参与。) -
->
表示读取(读操作)数据从数据库中输出时走
->
。(意味着在读取操作时允许该字段参与。)
-
-
后缀部分(如
:create
、:update
、false
)用于进一步限制操作时机:<-[操作]
限定写入操作<-:create
:仅在创建时写入,更新时不允许写入。<-:update
:仅在更新时写入,创建时不允许写入。<-
:创建和更新都允许写入。<-:false
:禁止写入,相当于只读。
>[操作]
限定读取操作>
:只允许读取(写入禁止)。>:false
:禁止读取(但可能允许写入,具体配合<-
说明)。
-
组合写法
>;<-:create
:读取始终允许,但写入仅在创建时允许;更新时不能写。>:false;<-:create
:禁止从数据库中读取(>:false
),但在创建时可以写入。
-
忽略字段的写法
"-"
:完全忽略该字段,既不读也不写。"-:all"
:在所有操作(读、写、迁移)中都忽略。"-:migration"
:仅在迁移操作时忽略该字段。
-
-
嵌入结构体:通过匿名嵌入或使用
embedded
标签,可以将一个结构体的字段直接嵌入到父结构体中,并且可以用embeddedPrefix
添加前缀。type Address struct {City stringState string }type Customer struct {ID uint `gorm:"primaryKey"`Name string// 匿名嵌入 Address 结构体,其字段会直接映射到 Customer 表中Address// 使用 embedded 标签嵌入 Address 结构体,并添加前缀 contact_Contact Address `gorm:"embedded;embeddedPrefix:contact_"` }
高级选项
- 除了基础的映射和约定,GORM 允许使用序列化标签(
serializer
)来自定义数据的存储和读取方式,增强灵活性。 - 对于字段更新的细粒度控制,如仅更新非零值字段、使用 map 或结构体进行批量更新,均由标签和方法调用来实现。