Go语言ORM框架GORM深度解析

Go语言ORM框架GORM深度解析

引言

GORM是Go语言中最流行的ORM(对象关系映射)框架,提供了强大的数据访问能力和优雅的API设计。本文将深入探讨GORM的核心功能、高级特性和最佳实践。

一、环境配置

1.1 安装GORM

go get gorm.io/gorm
go get gorm.io/driver/mysql

1.2 连接数据库

package main

import (
    "fmt"
    "log"
    "time"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

func main() {
    dsn := "user:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
    
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }

    // 获取底层sql.DB并设置连接池
    sqlDB, err := db.DB()
    if err != nil {
        log.Fatalf("Failed to get sql.DB: %v", err)
    }

    sqlDB.SetMaxOpenConns(100)
    sqlDB.SetMaxIdleConns(20)
    sqlDB.SetConnMaxLifetime(time.Hour)

    fmt.Println("Successfully connected to database")
}

二、模型定义

2.1 基础模型

import (
    "time"

    "gorm.io/gorm"
)

type User struct {
    gorm.Model
    Name     string     `gorm:"size:100;not null"`
    Email    string     `gorm:"size:255;unique;not null"`
    Age      int        `gorm:"default:0"`
    Birthday *time.Time `gorm:"type:date"`
    Active   bool       `gorm:"default:true"`
}

type Product struct {
    ID          uint      `gorm:"primaryKey"`
    Name        string    `gorm:"size:200;not null"`
    Price       float64   `gorm:"type:decimal(10,2)"`
    Stock       int       `gorm:"default:0"`
    CategoryID  uint
    CreatedAt   time.Time `gorm:"autoCreateTime"`
    UpdatedAt   time.Time `gorm:"autoUpdateTime"`
}

2.2 关联模型

type Category struct {
    ID       uint      `gorm:"primaryKey"`
    Name     string    `gorm:"size:100;not null"`
    Products []Product `gorm:"foreignKey:CategoryID"`
}

type Order struct {
    ID         uint        `gorm:"primaryKey"`
    UserID     uint
    User       User        `gorm:"foreignKey:UserID"`
    Items      []OrderItem `gorm:"foreignKey:OrderID"`
    TotalPrice float64     `gorm:"type:decimal(12,2)"`
    Status     string      `gorm:"size:20;default:'pending'"`
}

type OrderItem struct {
    ID        uint    `gorm:"primaryKey"`
    OrderID   uint
    ProductID uint
    Product   Product `gorm:"foreignKey:ProductID"`
    Quantity  int     `gorm:"default:1"`
    Price     float64 `gorm:"type:decimal(10,2)"`
}

三、CRUD操作

3.1 创建记录

func CreateUser(db *gorm.DB, user *User) error {
    return db.Create(user).Error
}

func CreateUsers(db *gorm.DB, users []*User) error {
    return db.Create(&users).Error
}

3.2 查询记录

func GetUserByID(db *gorm.DB, id uint) (*User, error) {
    var user User
    err := db.First(&user, id).Error
    if err != nil {
        return nil, err
    }
    return &user, nil
}

func GetUsersByAge(db *gorm.DB, minAge int) ([]*User, error) {
    var users []*User
    err := db.Where("age >= ?", minAge).Order("age DESC").Find(&users).Error
    if err != nil {
        return nil, err
    }
    return users, nil
}

func GetUserWithPreload(db *gorm.DB, id uint) (*User, error) {
    var user User
    err := db.Preload("Orders").Preload("Orders.Items").First(&user, id).Error
    if err != nil {
        return nil, err
    }
    return &user, nil
}

3.3 更新记录

func UpdateUser(db *gorm.DB, id uint, updates map[string]interface{}) error {
    return db.Model(&User{}).Where("id = ?", id).Updates(updates).Error
}

func UpdateUserSelective(db *gorm.DB, user *User) error {
    return db.Select("Name", "Email").Updates(user).Error
}

3.4 删除记录

func DeleteUser(db *gorm.DB, id uint) error {
    return db.Delete(&User{}, id).Error
}

func SoftDeleteUser(db *gorm.DB, id uint) error {
    return db.Delete(&User{}, id).Error
}

四、高级查询

4.1 条件查询

func AdvancedQuery(db *gorm.DB) ([]*User, error) {
    var users []*User

    // 复杂条件查询
    err := db.Where("age >= ? AND active = ?", 18, true).
        Or("name LIKE ?", "%John%").
        Not("email LIKE ?", "%@example.com").
        Order("created_at DESC").
        Limit(10).
        Offset(20).
        Find(&users).Error

    return users, err
}

4.2 原生SQL查询

func RawQuery(db *gorm.DB) ([]*User, error) {
    var users []*User
    err := db.Raw("SELECT * FROM users WHERE age > ?", 18).Scan(&users).Error
    return users, err
}

func RawQueryWithParams(db *gorm.DB, minAge, maxAge int) ([]map[string]interface{}, error) {
    var results []map[string]interface{}
    err := db.Raw(
        "SELECT name, COUNT(*) as count FROM users WHERE age BETWEEN ? AND ? GROUP BY name",
        minAge, maxAge,
    ).Scan(&results).Error
    return results, err
}

五、事务处理

func TransferMoney(db *gorm.DB, fromID, toID uint, amount float64) error {
    return db.Transaction(func(tx *gorm.DB) error {
        // 扣除余额
        if err := tx.Model(&Account{}).Where("id = ?", fromID).
            Update("balance", gorm.Expr("balance - ?", amount)).Error; err != nil {
            return err
        }

        // 增加余额
        if err := tx.Model(&Account{}).Where("id = ?", toID).
            Update("balance", gorm.Expr("balance + ?", amount)).Error; err != nil {
            return err
        }

        // 记录交易
        if err := tx.Create(&Transaction{
            FromID: fromID,
            ToID:   toID,
            Amount: amount,
        }).Error; err != nil {
            return err
        }

        return nil
    })
}

六、关联操作

6.1 一对多关系

func AddProductToCategory(db *gorm.DB, categoryID uint, product *Product) error {
    var category Category
    if err := db.First(&category, categoryID).Error; err != nil {
        return err
    }

    product.CategoryID = categoryID
    return db.Create(product).Error
}

func GetCategoryWithProducts(db *gorm.DB, categoryID uint) (*Category, error) {
    var category Category
    err := db.Preload("Products").First(&category, categoryID).Error
    return &category, err
}

6.2 多对多关系

type User struct {
    gorm.Model
    Roles []Role `gorm:"many2many:user_roles;"`
}

type Role struct {
    gorm.Model
    Name  string
    Users []User `gorm:"many2many:user_roles;"`
}

func AssignRole(db *gorm.DB, userID, roleID uint) error {
    var user User
    if err := db.First(&user, userID).Error; err != nil {
        return err
    }

    var role Role
    if err := db.First(&role, roleID).Error; err != nil {
        return err
    }

    return db.Model(&user).Association("Roles").Append(&role)
}

七、迁移

7.1 自动迁移

func AutoMigrate(db *gorm.DB) error {
    return db.AutoMigrate(
        &User{},
        &Product{},
        &Category{},
        &Order{},
        &OrderItem{},
    )
}

7.2 手动迁移

func ManualMigration(db *gorm.DB) error {
    // 添加新字段
    if err := db.Migrator().AddColumn(&User{}, "Phone"); err != nil {
        return err
    }

    // 修改字段类型
    if err := db.Migrator().AlterColumn(&User{}, "Age"); err != nil {
        return err
    }

    // 创建索引
    if err := db.Migrator().CreateIndex(&User{}, "idx_users_email"); err != nil {
        return err
    }

    return nil
}

八、性能优化

8.1 预编译语句

func UsePreparedStatement(db *gorm.DB) {
    db.Statement.Prepared = true
}

8.2 批量操作

func BatchInsert(db *gorm.DB, users []*User) error {
    return db.CreateInBatches(users, 100).Error
}

8.3 只读模式

func ReadOnlyTransaction(db *gorm.DB) error {
    return db.Transaction(func(tx *gorm.DB) error {
        tx.Statement.ForceSavePoint = true
        // 只读操作...
        return nil
    })
}

结语

GORM提供了丰富的功能和优雅的API,极大地简化了Go语言中的数据库操作。通过合理使用关联、事务和迁移功能,可以构建高效、可维护的数据库应用。希望本文的实践经验能帮助你更好地使用GORM进行开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值