Go语言对象关系映射ORM

目录gORMxormgORM安装GORM首先,确保你的环境中已经安装了Go和一个支持的数据库驱动。然后通过以下命令安装gorm:goget-ugorm.io/gorm基础概念Model:数据库中的表。Record:表中的行。Field:表中的列。Relat

目录


gORM

安装GORM

首先,确保你的环境中已经安装了Go和一个支持的数据库驱动。然后通过以下命令安装gorm:

go get -u gorm.io/gorm

基础概念

  • Model: 数据库中的表。
  • Record: 表中的行。
  • Field: 表中的列。
  • Relation: 模型之间的关联。

快速入门

连接数据库

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

func main() {
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }
}

创建模型 定义一个简单的用户模型:

type User struct {
    gorm.Model
    Name  string
    Email string
}

自动迁移

db.AutoMigrate(&User{})

创建记录

db.Create(&User{Name: "张三", Email: "zhangsan@example.com"})

查询记录

var user User
db.First(&user, 1) // 根据主键查询

关联

  • 一对一 (One-to-One)
  • 一对多 (One-to-Many)
  • 多对多 (Many-to-Many)

示例代码

type Profile struct {
    gorm.Model
    UserID uint
    User   User
}

db.Model(&Profile{}).Association("User").Set(&User{})

批量操作

users := []User{{Name: "李四"}, {Name: "王五"}}
db.Create(&users)

事务处理

tx := db.Begin()
tx.Create(&user)
if tx.Error != nil {
    tx.Rollback()
} else {
    tx.Commit()
}

一对一 (One-to-One)

例如,用户和其个人资料的关系:

type User struct {
    gorm.Model
    Name  string
    Email string
    ProfileID uint
    Profile Profile `gorm:"foreignKey:ProfileID"`
}

type Profile struct {
    gorm.Model
    Address string
    User    User `gorm:"foreignKey:ID"`
}

创建和读取关联数据

// 创建用户及其个人资料
profile := Profile{Address: "上海市"}
db.Create(&profile)

user := User{Name: "张三", Email: "zhangsan@example.com", ProfileID: profile.ID}
db.Create(&user)

// 读取用户及其个人资料
var user User
db.Preload("Profile").First(&user, 1)
fmt.Println(user.Name, user.Profile.Address)

一对多 (One-to-Many)

例如,用户和其文章的关系:

type User struct {
    gorm.Model
    Name  string
    Email string
    Posts []Post `gorm:"foreignKey:UserID"`
}

type Post struct {
    gorm.Model
    Title string
    UserID uint
    User  User `gorm:"foreignKey:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}

创建和读取关联数据

// 创建用户
user := User{Name: "张三", Email: "zhangsan@example.com"}
db.Create(&user)

// 创建文章并关联用户
post := Post{Title: "我的第一篇文章", UserID: user.ID}
db.Create(&post)

// 读取用户及其所有文章
var user User
db.Preload("Posts").First(&user, 1)
for _, post := range user.Posts {
    fmt.Println(post.Title)
}

多对多 (Many-to-Many)

例如,用户和其关注的用户的关系:

type User struct {
    gorm.Model
    Name string
    Follows []User `gorm:"many2many:follows;"`
}

// 创建用户
user1 := User{Name: "张三"}
user2 := User{Name: "李四"}
db.Create(&user1)
db.Create(&user2)

// 用户1关注用户2
db.Model(&user1).Association("Follows").Append(&user2)

// 读取用户及其关注的用户
var user User
db.Preload("Follows").First(&user, 1)
for _, follow := range user.Follows {
    fmt.Println(follow.Name)
}

批量操作

批量插入和更新数据可以显著提高性能。

批量插入

users := []User{
    {Name: "张三", Email: "zhangsan@example.com"},
    {Name: "李四", Email: "lisi@example.com"},
    {Name: "王五", Email: "wangwu@example.com"},
}
db.Create(&users)

批量更新

db.Model(&User{}).Where("age < ?", 30).Updates(map[string]interface{}{"age": 30})

事务处理

事务处理可以确保一系列操作要么全部成功,要么全部失败。

db.Transaction(func(tx *gorm.DB) error {
    var user User
    if err := tx.Where("name = ?", "张三").First(&user).Error; err != nil {
        return err
    }

    if err := tx.Model(&user).Update("age", 30).Error; err != nil {
        return err
    }

    return nil
})

性能优化

性能优化对于大型应用至关重要。

批处理 批量插入和批量更新可以减少数据库的网络开销。

db.CreateInBatches(users, 100) // 每次批量插入100条记录

索引 合理设计索引可以显著提高查询速度。

db.Model(&User{}).AddIndex("idx_name_email", "name", "email")

避免不必要的数据加载 使用预加载 (Preload) 可以避免 N+1 查询问题。

db.Preload("Posts").First(&user, 1)

错误处理

正确处理错误可以避免程序崩溃或数据不一致的问题。

result := db.Create(&user)
if result.Error != nil {
    log.Fatal(result.Error)
}

最佳实践

遵循一些最佳实践可以提高代码质量和可维护性。

使用结构体标签 使用结构体标签定义数据库字段可以提高代码的可读性和可维护性。

type User struct {
    gorm.Model
    Name  string `gorm:"not null"`
    Email string `gorm:"unique;not null"`
}

复杂查询 对于复杂的查询,考虑使用子查询或连接查询。

db.Table("users").
    Select("users.*, profiles.address").
    Joins("left join profiles on users.id = profiles.user_id").
    Where("users.age > ?", 18).
    Order("users.created_at desc").
    Find(&users)

更新依赖库 定期更新依赖库到最新版本可以获取最新的功能和修复已知的问题。

go get -u gorm.io/gorm

xorm

安装 xorm

首先,确保你的环境中已经安装了 Go 和一个支持的数据库驱动。然后通过以下命令安装 xorm:

go get -u github.com/go-xorm/xorm

基础概念

  • Engine: 数据库引擎实例。
  • Session: 数据库会话。
  • Model: 数据库中的表。
  • Record: 表中的行。
  • Field: 表中的列。

快速入门

连接数据库

import (
    "github.com/go-xorm/xorm"
    _ "github.com/go-sql-driver/mysql" // MySQL 驱动
)

func main() {
    engine, err := xorm.NewEngine("mysql", "root:password@/test?charset=utf8")
    if err != nil {
        panic(err)
    }
    defer engine.Close()

    // 显示 SQL 日志
    engine.ShowSQL(true)
}

创建模型 定义一个简单的用户模型:

type User struct {
    ID    int64
    Name  string
    Email string
}

// 表名映射
func (User) TableName() string {
    return "users"
}

自动迁移

engine.Sync2(new(User))

创建记录

user := User{Name: "张三", Email: "zhangsan@example.com"}
engine.Insert(&user)

查询记录

var user User
has, err := engine.Id(1).Get(&user)
if err != nil || !has {
    fmt.Println("查询失败")
} else {
    fmt.Println(user.Name, user.Email)
}

关联

  • 一对一 (One-to-One)
  • 一对多 (One-to-Many)
  • 多对多 (Many-to-Many)

一对一 (One-to-One)

例如,用户和其个人资料的关系:

type Profile struct {
    ID     int64
    UserID int64
    Address string
}

type User struct {
    ID    int64
    Name  string
    Email string
    Profile Profile
}

// 表名映射
func (Profile) TableName() string {
    return "profiles"
}

func (User) TableName() string {
    return "users"
}

创建和读取关联数据

profile := Profile{Address: "上海市"}
engine.Insert(&profile)

user := User{Name: "张三", Email: "zhangsan@example.com", Profile: profile}
engine.Insert(&user)

var user User
has, err := engine.Id(1).Get(&user)
if err != nil || !has {
    fmt.Println("查询失败")
} else {
    fmt.Println(user.Name, user.Profile.Address)
}

一对多 (One-to-Many)

例如,用户和其文章的关系:

type Post struct {
    ID    int64
    Title string
    UserID int64
}

type User struct {
    ID    int64
    Name  string
    Email string
    Posts []Post
}

// 表名映射
func (Post) TableName() string {
    return "posts"
}

func (User) TableName() string {
    return "users"
}

创建和读取关联数据

user := User{Name: "张三", Email: "zhangsan@example.com"}
engine.Insert(&user)

post := Post{Title: "我的第一篇文章", UserID: user.ID}
engine.Insert(&post)

var user User
has, err := engine.Id(1).Get(&user)
if err != nil || !has {
    fmt.Println("查询失败")
} else {
    for _, post := range user.Posts {
        fmt.Println(post.Title)
    }
}

多对多 (Many-to-Many)

例如,用户和其关注的用户的关系:

type User struct {
    ID    int64
    Name  string
    Email string
    Follows []User
}

// 表名映射
func (User) TableName() string {
    return "users"
}

创建和读取关联数据

user1 := User{Name: "张三", Email: "zhangsan@example.com"}
user2 := User{Name: "李四", Email: "lisi@example.com"}
engine.Insert(&user1)
engine.Insert(&user2)

engine.Table("users").Exec("INSERT INTO follows (follower_id, following_id) VALUES (?, ?)", user1.ID, user2.ID)

var user User
has, err := engine.Id(1).Get(&user)
if err != nil || !has {
    fmt.Println("查询失败")
} else {
    for _, follow := range user.Follows {
        fmt.Println(follow.Name)
    }
}

批量操作

批量插入和更新数据可以显著提高性能。

批量插入

users := []User{
    {Name: "张三", Email: "zhangsan@example.com"},
    {Name: "李四", Email: "lisi@example.com"},
    {Name: "王五", Email: "wangwu@example.com"},
}
engine.Insert(users)

批量更新

engine.Table("users").Where("age < ?", 30).Update(map[string]interface{}{"age": 30})

事务处理

事务处理可以确保一系列操作要么全部成功,要么全部失败。

session := engine.NewSession()
defer session.Close()

if err := session.Begin(); err != nil {
    panic(err)
}

if _, err := session.Id(1).Get(&user); err != nil {
    session.Rollback()
    panic(err)
}

if _, err := session.Model(&user).Update(map[string]interface{}{"age": 30}); err != nil {
    session.Rollback()
    panic(err)
}

if err := session.Commit(); err != nil {
    panic(err)
}

性能优化

性能优化对于大型应用至关重要。

批处理 批量插入和批量更新可以减少数据库的网络开销。

engine.InsertInBatches(users, 100) // 每次批量插入100条记录

索引 合理设计索引可以显著提高查询速度。

engine.Exec("ALTER TABLE users ADD INDEX idx_name_email (name, email)")

避免不必要的数据加载 使用预加载可以避免 N+1 查询问题。

var user User
has, err := engine.Id(1).Join("INNER", "profiles", "users.id = profiles.user_id").Get(&user)
if err != nil || !has {
    fmt.Println("查询失败")
} else {
    fmt.Println(user.Name, user.Profile.Address)
}

错误处理

正确处理错误可以避免程序崩溃或数据不一致的问题。

result, err := engine.Id(1).Get(&user)
if err != nil {
    log.Fatal(err)
} else if !result {
    fmt.Println("未找到记录")
}

最佳实践

遵循一些最佳实践可以提高代码质量和可维护性。

使用结构体标签 使用结构体标签定义数据库字段可以提高代码的可读性和可维护性。

type User struct {
    ID    int64  `xorm:"pk autoincr"`
    Name  string `xorm:"not null"`
    Email string `xorm:"unique not null"`
}

func (User) TableName() string {
    return "users"
}

复杂查询 对于复杂的查询,考虑使用子查询或连接查询。

var users []User
engine.Table("users").
    Select("users.*, profiles.address").
    Join("LEFT", "profiles", "users.id = profiles.user_id").
    Where("users.age > ?", 18).
    Desc("users.created_at").
    Find(&users)

更新依赖库 定期更新依赖库到最新版本可以获取最新的功能和修复已知的问题。

go get -u github.com/go-xorm/xorm
点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论
天涯学馆
天涯学馆
0x9d6d...50d5
资深大厂程序员,12年开发经验,致力于探索前沿技术!