GORM 使用指南
1. GORM 简介
1.1 什么是 GORM
GORM 是 Go 语言中最流行的对象关系映射(ORM)库,它提供了友好的 API,让开发者可以更加方便地操作数据库,而不需要编写复杂的 SQL 语句。
1.2 GORM 的优势
-
全功能 ORM
-
关联关系处理
-
钩子函数支持
-
预加载功能
-
事务支持
-
多数据库支持
-
自动迁移
-
灵活的查询构建器
1.3 对比原生 SQL
与直接使用原生 SQL 相比,GORM 具有以下优势:
-
代码更加简洁清晰
-
避免 SQL 注入风险
-
减少重复代码
-
自动处理类型转换
-
更好的可维护性
2. 安装与配置
2.1 安装 GORM
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql // MySQL驱动
// 或其他数据库驱动
// go get -u gorm.io/driver/postgres // PostgreSQL
// go get -u gorm.io/driver/sqlite // SQLite
// go get -u gorm.io/driver/sqlserver // SQL Server
2.2 项目配置
package main
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
func main() {
// 后续将在这里添加数据库连接代码
}
3. 数据库连接
3.1 MySQL 连接
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
3.2 其他数据库连接
PostgreSQL
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{})
SQLite
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
3.3 连接池配置
sqlDB, err := db.DB()
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间
sqlDB.SetConnMaxLifetime(time.Hour)
4. 模型定义
4.1 基本模型结构
type User struct {
ID uint `gorm:"primaryKey"` // 主键
Name string `gorm:"size:255;not null"` // 非空字符串字段
Email *string `gorm:"uniqueIndex"` // 唯一索引,指针类型表示可为空
Age int
MemberNumber sql.NullString // 使用sql.NullString处理可空字符串
ActivatedAt sql.NullTime // 可空时间字段
CreatedAt time.Time // 创建时间,自动填充
UpdatedAt time.Time // 更新时间,自动填充
DeletedAt gorm.DeletedAt `gorm:"index"` // 软删除时间,索引
}
GORM 支持多种方式定义可空字段:
-
使用指针类型(如 *string)
-
使用 database/sql 包中的 sql.NullXXX 类型
-
使用自定义类型并实现 Scanner 和 Valuer 接口
4.2 模型标签(Tag)详解
type Product struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Code string `gorm:"type:varchar(100);uniqueIndex;not null"`
Price uint `gorm:"default:0"`
ReleaseDate time.Time `gorm:"autoCreateTime"` // 创建时自动设置当前时间
ModifiedAt int64 `gorm:"autoUpdateTime:milli"` // 更新时自动设置毫秒时间戳
Data []byte `gorm:"serializer:json"` // 使用JSON序列化此字段
CreatedAt time.Time
}
常用标签说明:
-
primaryKey:将字段设置为主键
-
autoIncrement:自增
-
type:设置字段的数据库类型
-
size:设置字段的大小
-
default:设置字段的默认值
-
not null:设置字段不能为空
-
uniqueIndex:设置字段为唯一索引
-
index:设置字段为索引
-
embedded:嵌套字段
-
embeddedPrefix:嵌入字段的前缀
-
serializer:指定序列化方式,如 json/gob/unixtime
-
autoCreateTime:创建时自动填充时间
-
autoUpdateTime:更新时自动填充时间
-
column:指定数据库列名
-
<-:设置字段写入权限,如<-:create只创建,<-:update只更新,<-:false无写入权限
-
->:设置字段读取权限,如->:false无读取权限
-
-:忽略该字段,如-表示无读写权限,-:migration表示无迁移权限
4.3 约定与 gorm.Model
GORM 遵循一系列约定来简化开发:
-
主键:默认使用名为 ID 的字段作为主键
-
表名:结构体名称的蛇形复数形式(如 User 对应表名 users)
-
列名:字段名称的蛇形形式(如 CreatedAt 对应列名 created_at)
-
时间戳:自动追踪 CreatedAt 和 UpdatedAt 字段
GORM 提供了一个预定义结构体 gorm.Model,可以嵌入到模型中:
// 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
Age int
}
4.4 表名称定制
// 通过结构体方法重写表名
func (User) TableName() string {
return "my_users"
}
// 临时指定表名
db.Table("employees").Create(&user)
// 全局表名设置
db.Config.NamingStrategy = schema.NamingStrategy{
TablePrefix: "t_", // 表名前缀,`User` 的表名变为 `t_users`
SingularTable: true, // 使用单数表名,`User` 的表名变为 `t_user`
}
4.5 嵌入结构体
GORM 支持嵌入结构体,可以在一个结构体中嵌入另一个结构体的字段:
type Author struct {
Name string
Email string
}
// 匿名嵌入 - 字段将直接包含在Blog结构体中
type Blog struct {
ID int
Author // 匿名嵌入
Upvotes int32
}
// 命名嵌入 - 需要使用embedded标签
type Blog struct {
ID int
Author Author `gorm:"embedded"` // 嵌入所有字段
Upvotes int32
}
// 使用前缀
type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"` // 前缀为author_
Upvotes int32
}
// 等同于
type Blog struct {
ID int
AuthorName string
AuthorEmail string
Upvotes int32
}
4.6 字段级权限控制
GORM 提供了字段级别的权限控制,可以限制字段的读写权限:
type User struct {
Name string `gorm:"<-:create"` // 允许读和创建,不允许更新
Email string `gorm:"<-:update"` // 允许读和更新,不允许创建
Address string `gorm:"<-"` // 允许读和写(创建和更新)
Secret string `gorm:"<-:false"` // 允许读,禁止写
Password string `gorm:"->:false;<-"` // 允许写,禁止读(对敏感信息有用)
Role string `gorm:"-"` // 通过struct操作忽略此字段
Notes string `gorm:"-:all"` // 忽略读写和迁移
}
4.7 模型自动迁移
// 自动迁移
db.AutoMigrate(&User{}, &Product{})
自动迁移是 GORM 提供的一项功能,它可以自动将 Go 结构体映射为数据库表结构。具体来说,自动迁移会:
-
当表不存在时,创建表
-
当表结构与模型定义不匹配时,更新表结构(添加新字段、修改字段类型等)
-
添加缺少的索引和外键约束
这一功能主要解决了开发过程中表结构与代码模型保持同步的问题,特别适合于开发环境或个人项目,可以减少手动维护数据库结构的工作量。
自动迁移的局限性
需要注意的是,在现代互联网企业级开发环境中,自动迁移功能通常不会在生产环境使用,原因如下:
数据库变更风险控制:在企业环境中,数据库结构变更是高风险操作,通常需要:
-
正式的变更申请和审批流程
-
由专业 DBA 评估影响并执行操作
-
在低峰期执行,并有完整的回滚方案
权限控制:服务应用的数据库账号通常只有基本的 CRUD 权限,而没有修改表结构(DDL)的权限,这是出于安全考虑。
数据安全:自动迁移在某些情况下可能导致数据丢失(例如,当字段类型变更导致数据截断时)。
4.8 根据数据库反向生成模型
在实际开发中,我们经常会遇到已有数据库表需要生成 Go 模型的情况。GORM 官方提供了 gen 工具来支持这一功能。
安装 gen 工具
go get -u gorm.io/gen
使用 gen 工具从数据库生成模型
以下代码建议放到 cmd 目录下,执行 go run main.go 即可。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gen"
"gorm.io/gorm"
)
func main() {
// 连接数据库
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(fmt.Errorf("连接数据库失败: %w", err))
}
// 初始化生成器
g := gen.NewGenerator(gen.Config{
// 相对路径,会自动创建目录
OutPath: "./dao/query",
// 模式: QueryMode 只生成查询代码(不需要指定表)
// ModelMode 只生成模型代码(需要指定表)
// DefaultMode 同时生成模型和查询代码(需要指定表)
Mode: gen.WithDefaultQuery | gen.WithoutContext,
// 表字段可为null时,对应字段使用指针类型
FieldNullable: true,
// 表字段默认值,对应字段使用指针类型
FieldWithDefaultValue: true,
// 表字段有注释,对应字段增加注释
FieldWithIndexComment: true,
})
// 设置数据库连接
g.UseDB(db)
// 从数据库表生成所有模型
// g.GenerateAllTable()
// 或者只生成指定表的模型
// 第一个参数是表名,第二个参数是模型名称
g.GenerateModel("users", gen.FieldNew("id").FieldType(gen.FieldTypeUint))
g.GenerateModel("products")
g.GenerateModel("orders")
// 生成代码
g.Execute()
}
执行以上代码后,会在指定的 OutPath 目录生成对应的模型和查询代码。
生成文件示例
对于 users 表,会生成如下文件:
model_user.go - 包含模型定义
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"time"
)
const TableNameUser = "users"
// User mapped from table <users>
type User struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
Name string `gorm:"column:name;not null" json:"name"`
Email string `gorm:"column:email;not null;uniqueIndex:idx_email" json:"email"`
Age *int32 `gorm:"column:age" json:"age"`
CreatedAt time.Time `gorm:"column:created_at;not null" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at" json:"deleted_at"`
}
// TableName User's table name
func (*User) TableName() string {
return TableNameUser
}
query.go - 包含查询代码
自定义生成选项
gen 工具提供了丰富的自定义选项:
// 自定义字段类型
g.GenerateModel("users",
// 使用自定义类型
gen.FieldNew("id").FieldType(gen.FieldTypeUint),
// 指定 Go 结构体字段名
gen.FieldNew("created_at").FieldName("CreatedTime"),
// 添加字段标签
gen.FieldNew("updated_at").FieldTag(map[string]string{"json": "update_time"}),
)
// 忽略一些字段
g.GenerateModel("users", gen.FieldIgnore("deleted_at"))
// 表前缀处理
g.WithOpts(gen.WithTablePrefix("t_"))
反向生成的优势与注意事项
优势:
-
快速开发 - 无需手动编写结构体模型
-
精确匹配 - 自动适配数据库字段类型和约束
-
保持同步 - 可定期运行以与数据库结构保持同步
注意事项:
-
生成的代码为静态代码,数据库结构变更后需要重新生成
-
建议保存生成配置代码,以便后期调整和重新生成
-
对于复杂的关联关系可能需要手动调整
-
生成的模型通常需要后续增加业务相关代码
在实际项目开发中,通常会结合使用手动定义模型和反向生成模型两种方式,以获得最大的灵活性和效率。
5. 基本 CRUD 操作
5.1 创建记录
// 创建单条记录
user := User{Name: "张三", Age: 18, Email: "zhangsan@example.com"}
result := db.Create(&user)
// 创建多条记录
users := []User{
{Name: "李四", Age: 20, Email: "lisi@example.com"},
{Name: "王五", Age: 22, Email: "wangwu@example.com"},
}
result = db.Create(&users)
5.2 查询记录
查询单条记录
// 根据主键查询
var user User
db.First(&user, 1) // 查询ID为1的用户
db.First(&user, "id = ?", 1) // 条件查询
// 查询第一条记录
db.First(&user) // 根据主键升序查询第一条记录
// 查询最后一条记录
db.Last(&user) // 根据主键降序查询最后一条记录
// 查询任意一条记录
db.Take(&user)
查询多条记录
var users []User
// 查询所有记录
db.Find(&users)
// 条件查询
db.Where("age > ?", 18).Find(&users)
db.Where("name LIKE ?", "%张%").Find(&users)
指定查询字段
在查询数据时,如果只需要获取表中的部分字段,可以使用 Select 方法或者定义一个只包含所需字段的结构体。这样可以提高查询效率,减少不必要的数据传输(例如超长字段)。并且有的时候更安全,例如数据库中的密码或者手机号等敏感字段,可能不小心的暴露到了前端,这样就会导致安全问题。
使用 Select 方法指定字段
// 只查询name和age字段
var users []User
db.Select("name", "age").Find(&users)
// 也可以使用结构体字段名(推荐,更安全)
db.Select([]string{"Name", "Age"}).Find(&users)
// 如果查询结果想绑定到自定义结构体,可以这样做
type UserSummary struct {
Name string
Age int
}
var summaries []UserSummary
db.Model(&User{}).Select("name", "age").Find(&summaries)
使用特定结构体指定字段
定义一个只包含需要查询字段的结构体,GORM 在查询时会自动映射这些字段。
type UserInfo struct {
Name string
Email string
}
var userInfos []UserInfo
// GORM会根据UserInfo结构体的字段名去查询对应的列
// 例如,这里会执行 SELECT name, email FROM users;
db.Model(&User{}).Find(&userInfos)
// 配合条件查询
db.Model(&User{}).Where("age > ?", 18).Find(&userInfos)
这两种方式都可以有效地只选择需要的数据库列,根据实际情况和个人偏好选择即可。使用 Select 方法更为灵活,可以动态指定字段;而使用特定结构体则在代码可读性和类型安全方面有优势。
5.3 更新记录
// 更新单个字段
db.Model(&user).Update("name", "赵六")
// 更新多个字段
db.Model(&user).Updates(User{Name: "赵六", Age: 30})
// 或者使用map
db.Model(&user).Updates(map[string]interface{}{"name": "赵六", "age": 30})
// 更新选定字段
db.Model(&user).Select("name", "age").Updates(map[string]interface{}{
"name": "赵六", "age": 30, "email": "zhaoliu@example.com",
}) // 只会更新name和age字段
5.4 删除记录
// 删除单条记录
db.Delete(&user)
db.Delete(&user, 1) // 删除ID为1的记录
// 批量删除
db.Delete(&User{}, []int{1, 2, 3})
// 条件删除
db.Where("age > ?", 30).Delete(&User{})
5.5 查询条件
// 等于条件
db.Where("name = ?", "张三").Find(&users)
// 不等于条件
db.Where("name <> ?", "张三").Find(&users)
// IN条件
db.Where("name IN ?", []string{"张三", "李四"}).Find(&users)
// LIKE条件
db.Where("name LIKE ?", "%张%").Find(&users)
// AND条件
db.Where("name = ? AND age >= ?", "张三", 20).Find(&users)
// 时间条件
db.Where("created_at > ?", time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)).Find(&users)
// 结构体条件
db.Where(&User{Name: "张三", Age: 20}).Find(&users)
// 相当于 WHERE name = '张三' AND age = 20
// Map条件
db.Where(map[string]interface{}{"name": "张三", "age": 20}).Find(&users)
// 原生SQL
db.Raw("SELECT id, name, age FROM users WHERE name = ?", "张三").Scan(&users)
5.6 排序与分页
// 排序
db.Order("age desc, name").Find(&users)
// 分页
db.Limit(10).Offset(0).Find(&users) // 第一页,每页10条
db.Limit(10).Offset(10).Find(&users) // 第二页,每页10条
6. 关联关系
6.1 一对一关系
// 用户有一张信用卡
type User struct {
gorm.Model
Name string
CreditCard CreditCard // 一对一关系(拥有一个)
}
type CreditCard struct {
gorm.Model
Number string
UserID uint // 外键
}
// 查询用户并预加载信用卡信息
var user User
db.Preload("CreditCard").First(&user)
6.2 一对多关系
// 用户有多个订单
type User struct {
gorm.Model
Name string
Orders []Order // 一对多关系(拥有多个)
}
type Order struct {
gorm.Model
Amount float64
UserID uint // 外键
}
// 查询用户并预加载订单信息
var user User
db.Preload("Orders").First(&user)
6.3 多对多关系
// 用户可以有多个角色,一个角色可以属于多个用户
type User struct {
gorm.Model
Name string
Roles []Role `gorm:"many2many:user_roles;"`
}
type Role struct {
gorm.Model
Name string
Users []User `gorm:"many2many:user_roles;"`
}
// 查询用户并预加载角色信息
var user User
db.Preload("Roles").First(&user)
6.4 关联操作
// 添加关联
db.Model(&user).Association("Roles").Append(&role1, &role2)
// 替换关联
db.Model(&user).Association("Roles").Replace(&newRole1, &newRole2)
// 删除关联
db.Model(&user).Association("Roles").Delete(&role1, &role2)
// 清空关联
db.Model(&user).Association("Roles").Clear()
// 关联计数
count := db.Model(&user).Association("Roles").Count()
7. 事务与钩子
7.1 事务处理
// 方式一:手动事务
tx := db.Begin()
// 执行一些数据库操作
if err := tx.Create(&user).Error; err != nil {
tx.Rollback() // 发生错误时回滚
return err
}
if err := tx.Create(&order).Error; err != nil {
tx.Rollback() // 发生错误时回滚
return err
}
// 提交事务
return tx.Commit().Error
// 方式二:事务闭包
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err // 返回任何错误都会自动回滚
}
if err := tx.Create(&order).Error; err != nil {
return err
}
// 返回nil提交事务
return nil
})
7.2 钩子函数
// 示例:BeforeCreate钩子
func (u *User) BeforeCreate(tx *gorm.DB) error {
// 创建前执行的逻辑,如密码加密
u.Password = encrypt(u.Password)
return nil
}
// 其他常用钩子
// BeforeUpdate, AfterCreate, AfterUpdate, BeforeDelete, AfterDelete 等
7.3 Soft Delete(软删除)
// 模型定义包含软删除字段
type User struct {
gorm.Model // 已包含 DeletedAt 字段
Name string
}
// 或者手动定义
type User struct {
ID uint
Name string
DeletedAt gorm.DeletedAt `gorm:"index"`
}
// 软删除
db.Delete(&user)
// 查询时会自动排除软删除的记录
db.Find(&users) // 不包含被软删除的记录
// 强制查询包含已删除的记录
db.Unscoped().Find(&users) // 包含被软删除的记录
// 永久删除
db.Unscoped().Delete(&user)
8. 高级功能与最佳实践
8.1 原生 SQL
// 原生SQL查询
var users []User
db.Raw("SELECT * FROM users WHERE age > ?", 20).Scan(&users)
// 原生SQL执行
db.Exec("UPDATE users SET name = ? WHERE id = ?", "张三", 1)
// 命名参数
db.Raw("SELECT * FROM users WHERE name = @name", sql.Named("name", "张三")).Scan(&users)
8.2 数据库迁移
注意: 实际开发中,这样是不规范的。
// 自动迁移
db.AutoMigrate(&User{}, &Product{}, &Order{})
// 创建表
db.Migrator().CreateTable(&User{})
// 检查表是否存在
db.Migrator().HasTable(&User{})
// 删除表
db.Migrator().DropTable(&User{})
// 修改列
db.Migrator().AlterColumn(&User{}, "Name")
// 删除列
db.Migrator().DropColumn(&User{}, "Name")
// 添加索引
db.Migrator().CreateIndex(&User{}, "Name")
// 删除索引
db.Migrator().DropIndex(&User{}, "Name")
8.3 批量操作
// 批量插入
var users = []User{
{Name: "用户1"},
{Name: "用户2"},
{Name: "用户3"},
}
db.CreateInBatches(users, 100) // 每批100条
// 批量更新
db.Model(&User{}).Where("role = ?", "admin").Updates(User{Name: "管理员"})
8.4 性能优化
// 智能选择字段
db.Select("name", "age").Find(&users)
// 禁用默认事务
db.Session(&gorm.Session{SkipDefaultTransaction: true}).Find(&users)
// 预加载关联数据
db.Preload("Orders").Preload("CreditCard").Find(&users)
// 预加载关联数据时指定条件
db.Preload("Orders", "state = ?", "paid").Find(&users)
// 连接查询
db.Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Find(&users)
8.5 Logger 配置
// 设置日志配置
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 设置日志级别
})
// 自定义日志
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // 慢SQL阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound错误
Colorful: true, // 彩色打印
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
8.6 常见问题与解决方案
1. N\ +1查询问题
// 错误示例 - 会导致N+1查询问题
var users []User
db.Find(&users) // 查询所有用户
for _, user := range users {
var orders []Order
db.Where("user_id = ?", user.ID).Find(&orders) // 每个用户查询一次订单
}
// 正确示例 - 使用预加载解决N+1问题
var users []User
db.Preload("Orders").Find(&users) // 一次性加载所有用户及其订单
2. 构建复杂查询
// 使用作用域(Scopes)构建可复用的查询条件
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
return db.Where("amount > ?", 1000)
}
func PaidOrders(db *gorm.DB) *gorm.DB {
return db.Where("state = ?", "paid")
}
// 使用
var orders []Order
db.Scopes(AmountGreaterThan1000, PaidOrders).Find(&orders)
3. 处理大数据量
// 使用游标方式处理大量数据
rows, err := db.Model(&User{}).Rows()
defer rows.Close()
for rows.Next() {
var user User
db.ScanRows(rows, &user)
// 处理用户数据
}
8.7 实际项目示例
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
// 定义模型
type User struct {
gorm.Model
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex"`
Age int `gorm:"default:18"`
Birthday time.Time
CompanyID uint
Company Company
Addresses []Address
}
type Company struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
}
type Address struct {
gorm.Model
UserID uint
Address string `gorm:"size:200;not null"`
}
// 数据库连接函数
func connectDB() *gorm.DB {
// 配置MySQL连接参数
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// 自定义日志
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Info,
IgnoreRecordNotFoundError: true,
Colorful: true,
},
)
// 连接数据库
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic("failed to connect database")
}
// 配置连接池
sqlDB, err := db.DB()
if err != nil {
panic("failed to get database connection")
}
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
return db
}
func main() {
db := connectDB()
// 自动迁移
db.AutoMigrate(&User{}, &Company{}, &Address{})
// 创建公司
company := Company{Name: "测试公司"}
db.Create(&company)
// 创建用户
user := User{
Name: "张三",
Email: "zhangsan@example.com",
Age: 25,
Birthday: time.Date(1998, 1, 1, 0, 0, 0, 0, time.Local),
CompanyID: company.ID,
Addresses: []Address{
{Address: "北京市海淀区"},
{Address: "上海市浦东新区"},
},
}
// 事务处理
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err
}
// 模拟其他操作
fmt.Println("用户创建成功,ID:", user.ID)
return nil
})
if err != nil {
fmt.Println("事务执行失败:", err)
return
}
// 查询用户并预加载关联数据
var result User
db.Preload("Company").Preload("Addresses").First(&result, user.ID)
fmt.Printf("用户: %s, 公司: %s, 地址数量: %d\n",
result.Name, result.Company.Name, len(result.Addresses))
// 更新用户
db.Model(&result).Updates(User{Name: "李四", Age: 30})
// 关联查询示例
var users []User
db.Joins("Company").Where("companies.name LIKE ?", "%测试%").Find(&users)
fmt.Printf("关联查询到 %d 个用户\n", len(users))
}
9. 总结
GORM 作为 Go 语言中最流行的 ORM 库,提供了丰富的功能和优雅的 API,可以极大地提高数据库开发效率。本教程从基础到高级全面介绍了 GORM 的使用方法,包括:
-
基本连接配置
-
模型定义与字段标签
-
CRUD 操作
-
关联关系处理
-
事务与钩子
-
高级查询与优化
通过学习本教程,您应该能够在实际项目中熟练应用 GORM,构建出高效、稳定的数据库访问层。
喜欢的话,留下你的评论吧~