GORM集成 #
一、GORM安装 #
1.1 安装GORM #
bash
# 安装GORM
go get -u gorm.io/gorm
# 安装MySQL驱动
go get -u gorm.io/driver/mysql
# 安装PostgreSQL驱动
go get -u gorm.io/driver/postgres
# 安装SQLite驱动
go get -u gorm.io/driver/sqlite
1.2 基本配置 #
go
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func InitDB() (*gorm.DB, error) {
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
return gorm.Open(mysql.Open(dsn), &gorm.Config{
// 日志配置
Logger: logger.Default.LogMode(logger.Info),
// 跳过默认事务
SkipDefaultTransaction: true,
// 命名策略
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_",
SingularTable: true,
},
// 禁用外键约束
DisableForeignKeyConstraintWhenMigrating: true,
})
}
二、模型定义 #
2.1 基本模型 #
go
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Email string `gorm:"size:100;uniqueIndex" json:"email"`
Age int `gorm:"default:0" json:"age"`
Status string `gorm:"size:20;default:'active'" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
2.2 字段标签 #
go
type User struct {
// 主键
ID uint `gorm:"primaryKey"`
// 自增
ID uint `gorm:"primaryKey;autoIncrement"`
// 字段大小
Name string `gorm:"size:100"`
// 非空
Name string `gorm:"not null"`
// 默认值
Age int `gorm:"default:0"`
// 唯一索引
Email string `gorm:"uniqueIndex"`
// 普通索引
Name string `gorm:"index"`
// 复合索引
Name string `gorm:"index:idx_name_age"`
Age int `gorm:"index:idx_name_age"`
// 外键
UserID uint `gorm:"foreignKey:UserID"`
// 忽略字段
Ignore string `gorm:"-"`
// 列名
Name string `gorm:"column:user_name"`
// 类型
Bio string `gorm:"type:text"`
}
2.3 嵌入结构体 #
go
type BaseModel struct {
ID uint `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
type User struct {
BaseModel
Name string `json:"name"`
Email string `json:"email"`
}
// 嵌入但不作为单独模型
type Profile struct {
gorm.Model
UserID uint
Bio string
}
三、关联关系 #
3.1 一对一 #
go
type User struct {
ID uint
Name string
Profile Profile // 一对一
}
type Profile struct {
ID uint
UserID uint // 外键
Bio string
}
// 创建
user := User{
Name: "Alice",
Profile: Profile{
Bio: "Hello, I'm Alice",
},
}
DB.Create(&user)
// 预加载查询
var user User
DB.Preload("Profile").First(&user, 1)
3.2 一对多 #
go
type User struct {
ID uint
Name string
Posts []Post // 一对多
}
type Post struct {
ID uint
Title string
UserID uint // 外键
}
// 创建
user := User{
Name: "Alice",
Posts: []Post{
{Title: "Post 1"},
{Title: "Post 2"},
},
}
DB.Create(&user)
// 预加载查询
var user User
DB.Preload("Posts").First(&user, 1)
3.3 多对多 #
go
type User struct {
ID uint
Name string
Roles []Role `gorm:"many2many:user_roles;"` // 多对多
}
type Role struct {
ID uint
Name string
Users []User `gorm:"many2many:user_roles;"`
}
// 添加关联
user := User{Name: "Alice"}
role := Role{Name: "Admin"}
DB.Create(&user)
DB.Create(&role)
// 关联
DB.Model(&user).Association("Roles").Append(&role)
// 预加载查询
var user User
DB.Preload("Roles").First(&user, 1)
3.4 属于 #
go
type Post struct {
ID uint
Title string
UserID uint
User User // 属于
}
type User struct {
ID uint
Name string
}
// 创建
post := Post{
Title: "My Post",
User: User{Name: "Alice"},
}
DB.Create(&post)
// 预加载查询
var post Post
DB.Preload("User").First(&post, 1)
四、钩子函数 #
4.1 生命周期钩子 #
go
type User struct {
ID uint
Name string
CreatedAt time.Time
UpdatedAt time.Time
}
// 创建前
func (u *User) BeforeCreate(tx *gorm.DB) error {
fmt.Println("BeforeCreate")
return nil
}
// 创建后
func (u *User) AfterCreate(tx *gorm.DB) error {
fmt.Println("AfterCreate")
return nil
}
// 更新前
func (u *User) BeforeUpdate(tx *gorm.DB) error {
fmt.Println("BeforeUpdate")
return nil
}
// 更新后
func (u *User) AfterUpdate(tx *gorm.DB) error {
fmt.Println("AfterUpdate")
return nil
}
// 删除前
func (u *User) BeforeDelete(tx *gorm.DB) error {
fmt.Println("BeforeDelete")
return nil
}
// 删除后
func (u *User) AfterDelete(tx *gorm.DB) error {
fmt.Println("AfterDelete")
return nil
}
// 查询后
func (u *User) AfterFind(tx *gorm.DB) error {
fmt.Println("AfterFind")
return nil
}
4.2 钩子顺序 #
text
创建: BeforeSave → BeforeCreate → AfterCreate → AfterSave
更新: BeforeSave → BeforeUpdate → AfterUpdate → AfterSave
删除: BeforeDelete → AfterDelete
查询: AfterFind
五、事务处理 #
5.1 自动事务 #
go
func CreateUser() error {
return DB.Transaction(func(tx *gorm.DB) error {
// 创建用户
if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
return err
}
// 创建用户配置
if err := tx.Create(&Profile{UserID: 1, Bio: "Bio"}).Error; err != nil {
return err
}
return nil
})
}
5.2 手动事务 #
go
func CreateUser() error {
tx := DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Profile{UserID: 1, Bio: "Bio"}).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
5.3 SavePoint #
go
func CreateUser() error {
tx := DB.Begin()
if err := tx.Create(&User{Name: "Alice"}).Error; err != nil {
tx.Rollback()
return err
}
tx.SavePoint("sp1")
if err := tx.Create(&Profile{UserID: 1, Bio: "Bio"}).Error; err != nil {
tx.RollbackTo("sp1")
return tx.Commit().Error
}
return tx.Commit().Error
}
六、日志配置 #
6.1 日志级别 #
go
const (
Silent logger.LogLevel = iota + 1
Error
Warn
Info
)
// 配置日志级别
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
6.2 自定义日志 #
go
type CustomLogger struct {
logger.Interface
}
func (l *CustomLogger) LogMode(level logger.LogLevel) logger.Interface {
return l
}
func (l *CustomLogger) Info(ctx context.Context, msg string, data ...interface{}) {
log.Printf("[INFO] "+msg, data...)
}
func (l *CustomLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
log.Printf("[WARN] "+msg, data...)
}
func (l *CustomLogger) Error(ctx context.Context, msg string, data ...interface{}) {
log.Printf("[ERROR] "+msg, data...)
}
func (l *CustomLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
sql, rows := fc()
log.Printf("[SQL] %s | Rows: %d | Error: %v", sql, rows, err)
}
func InitDB() (*gorm.DB, error) {
return gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: &CustomLogger{},
})
}
七、性能优化 #
7.1 批量插入 #
go
// 批量插入
users := []User{
{Name: "Alice"},
{Name: "Bob"},
{Name: "Charlie"},
}
DB.CreateInBatches(&users, 100)
7.2 选择字段 #
go
// 只查询需要的字段
var users []User
DB.Select("id", "name").Find(&users)
// 排除字段
DB.Select("EXCLUDE", "password").Find(&users)
7.3 分页查询 #
go
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if page <= 0 {
page = 1
}
switch {
case pageSize > 100:
pageSize = 100
case pageSize <= 0:
pageSize = 10
}
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
// 使用
var users []User
var total int64
DB.Model(&User{}).Count(&total)
DB.Scopes(Paginate(1, 10)).Find(&users)
八、总结 #
8.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 模型定义 | 使用struct tag定义字段属性 |
| 关联关系 | 一对一、一对多、多对多 |
| 钩子函数 | 生命周期回调 |
| 事务处理 | 保证数据一致性 |
| 日志配置 | 自定义日志输出 |
8.2 最佳实践 #
| 实践 | 说明 |
|---|---|
| 模型设计 | 合理设计模型和关联 |
| 索引优化 | 为常用查询字段添加索引 |
| 批量操作 | 使用批量插入提高性能 |
| 事务使用 | 合理使用事务 |
8.3 下一步 #
现在你已经掌握了GORM集成,接下来让我们学习 CRUD操作,深入了解数据库操作!
最后更新:2026-03-28