3.2goweb框架GORM

GORM 是 Go 语言中功能强大的 ORM(对象关系映射)框架,支持 MySQL、PostgreSQL、SQLite、SQL Server 等主流数据库。以下是 GORM 的核心概念和用法详解:


​一、基础入门​

1. 安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql  # 按需选择数据库驱动

所以我们看到安装的最新版本是 1.26.0.这个不是Gorm1.0的意思。是GORM2.0.参考官方2.0的文档。https://gorm.io/zh_CN/docs/

2. 连接数据库
import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)

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{})

Gorm 有一个 默认 logger 实现,默认情况下,它会打印慢 SQL 和错误

Logger 接受的选项不多,您可以在初始化时自定义它,例如:

newLogger := logger.New(
  log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
  logger.Config{
    SlowThreshold:              time.Second,   // Slow SQL threshold
    LogLevel:                   logger.Silent, // Log level
    IgnoreRecordNotFoundError: true,           // Ignore ErrRecordNotFound error for logger
    ParameterizedQueries:      true,           // Don't include params in the SQL log
    Colorful:                  false,          // Disable color
  },
)

// Globally mode
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
  Logger: newLogger,
})

// Continuous session mode
tx := db.Session(&Session{Logger: newLogger})
tx.First(&user)
tx.Model(&user).Update("Age", 18)
日志级别

GORM 定义了这些日志级别:SilentErrorWarnInfo


​二、模型定义​

1. 结构体与表映射
type User struct {
  gorm.Model        // 内置字段:ID, CreatedAt, UpdatedAt, DeletedAt
  Name       string `gorm:"size:255"`
  Age        int
  Email      string `gorm:"uniqueIndex size:255"` // 唯一索引
}
2. 自定义表名
func (User) TableName() string {
  return "custom_users" // 自定义表名
}
3.自动迁移

GORM 提供的自动迁移功能是指它能够根据定义的 Go 结构体(模型)自动创建、更新或删除数据库中的表结构,以此保持模型和数据库表结构的一致性。这一功能极大地简化了数据库表结构管理的流程。例如,当你新增一个字段到结构体中,自动迁移会尝试在数据库表中添加该字段;若删除了结构体中的某个字段,自动迁移也可能根据配置删除表中的相应列。

工作原理
  1. 解析模型定义:GORM 会解析你定义的 Go 结构体,识别其中的字段、类型、标签等信息。例如,对于以下 User 结构

type User struct {
  gorm.Model        // 内置字段:ID, CreatedAt, UpdatedAt, DeletedAt
  Name       string `gorm:"size:255"`
  Age        int
  Email      string `gorm:"uniqueIndex size:255"` // 唯一索引
}

func (User) TableName() string {
  return "custom_users" // 自定义表名
}

GORM 会分析出 User 表应该包含 idcreated_atupdated_atdeleted_at(来自 gorm.Model)、nameemail 和 age 这些字段,并且 email 字段需要有唯一约束。
2. 生成 SQL 语句:根据解析结果,GORM 生成相应的 SQL 语句。如果数据库中不存在 custom_users表,会生成 CREATE TABLE 语句来创建表;若表已存在,会生成 ALTER TABLE 语句来更新表结构。
3. 执行 SQL 语句:GORM 将生成的 SQL 语句发送到数据库执行,从而完成表结构的创建或更新。

使用示例
package main

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

// User 模型定义
type User struct {
  gorm.Model        // 内置字段:ID, CreatedAt, UpdatedAt, DeletedAt
  Name       string `gorm:"size:255"`
  Age        int
  Email      string `gorm:"uniqueIndex size:255"` // 唯一索引
}

func (User) TableName() string {
  return "custom_users" // 自定义表名
}

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("failed to connect database")
    }

    // 自动迁移 User 模型
    db.AutoMigrate(&User{})
}

输出结果:

在上述代码中,调用 db.AutoMigrate(&User{}) 会根据 User 结构体的定义在数据库中创建或更新 User 表。

注意事项
  • 数据丢失风险:自动迁移可能会导致数据丢失。例如,当你删除结构体中的某个字段时,自动迁移可能会删除数据库表中的相应列,该列的数据就会丢失。所以在生产环境中最好不要使用该功能。

​三、CRUD 操作​

1. 创建记录
// 创建单条
user := User{Name: "Alice", Age: 25}
db.Create(&user)

// 批量插入
users := []User{{Name: "Bob"}, {Name: "Charlie"}}
db.CreateInBatches(users, 100) // 每批100条
2. 查询数据
var users []User

// 查询全部
db.Find(&users)

// 条件查询
db.Where("age > ?", 18).Find(&users)
db.First(&user, 1)        // 查找ID=1的第一条记录
db.Last(&user)            // 最后一条记录

// 高级查询
db.Select("name, age").Where("age > ?", 20).Order("age desc").Limit(10).Find(&users)
3. 更新数据
// 更新单个字段
db.Model(&user).Update("Age", 30)

// 批量更新
db.Model(&User{}).Where("age < ?", 30).Update("Age", gorm.Expr("Age + ?", 1))
4. 删除数据
// 软删除(默认使用 DeletedAt 字段)
db.Delete(&user)

// 硬删除(物理删除)
db.Unscoped().Delete(&user)

​四、关联关系​

一对一关联

package main

import "gorm.io/gorm"

// User 用户模型
type User struct {
    gorm.Model
    Name    string
    Profile Profile
}

// Profile 个人资料模型
type Profile struct {
    gorm.Model
    UserID  uint
    Address string
}

在这个例子中,User 结构体包含一个 Profile 字段,Profile 结构体包含一个 UserID 字段作为外键,关联到 User 表。

迁移和操作示例
package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func ConnectDB() (*gorm.DB, error) {
    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 {
        return nil, err
    }
    return db, nil
}

func main() {
    db, err := ConnectDB()
    if err != nil {
        panic("failed to connect database")
    }

    // 自动迁移模型
    db.AutoMigrate(&User{}, &Profile{})

    // 创建用户和关联的个人资料
    user := User{
        Name: "John Doe",
        Profile: Profile{
            Address: "123 Main St",
        },
    }
    db.Create(&user)

    // 查询用户及其个人资料
    var retrievedUser User
    db.Preload("Profile").First(&retrievedUser, user.ID)
    fmt.Printf("User: %s, Address: %s\n", retrievedUser.Name, retrievedUser.Profile.Address)
}

在这个例子中,我们使用 Preload 方法预加载用户的个人资料。

一对多关联

一对多关联表示一个模型的一条记录可以关联到另一个模型的多条记录。例如,一个用户可以有多个帖子。

package main

import "gorm.io/gorm"

// User 用户模型
type User struct {
    gorm.Model
    Name  string
    Posts []Post
}

// Post 帖子模型
type Post struct {
    gorm.Model
    Title  string
    UserID uint
}

在这个例子中,User 结构体包含一个 Posts 切片,Post 结构体包含一个 UserID 字段作为外键,关联到 User 表。

迁移和操作示例
package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func ConnectDB() (*gorm.DB, error) {
    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 {
        return nil, err
    }
    return db, nil
}

func main() {
    db, err := ConnectDB()
    if err != nil {
        panic("failed to connect database")
    }

    // 自动迁移模型
    db.AutoMigrate(&User{}, &Post{})

    // 创建用户和关联的帖子
    user := User{
        Name: "John Doe",
        Posts: []Post{
            {Title: "First Post"},
            {Title: "Second Post"},
        },
    }
    db.Create(&user)

    // 查询用户及其帖子
    var retrievedUser User
    db.Preload("Posts").First(&retrievedUser, user.ID)
    fmt.Printf("User: %s, Posts count: %d\n", retrievedUser.Name, len(retrievedUser.Posts))
}

在这个例子中,我们同样使用 Preload 方法预加载用户的所有帖子。

多对多关联

多对多关联表示一个模型的一条记录可以关联到另一个模型的多条记录,反之亦然。例如,一个用户可以有多个角色,一个角色可以被多个用户拥有。

定义模型
package main

import "gorm.io/gorm"

// User 用户模型
type User struct {
    gorm.Model
    Name  string
    Roles []Role `gorm:"many2many:user_roles;"`
}

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

在这个例子中,User 结构体和 Role 结构体都包含一个切片,通过 many2many 标签指定关联表为 user_roles

迁移和操作示例
package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func ConnectDB() (*gorm.DB, error) {
    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 {
        return nil, err
    }
    return db, nil
}

func main() {
    db, err := ConnectDB()
    if err != nil {
        panic("failed to connect database")
    }

    // 自动迁移模型
    db.AutoMigrate(&User{}, &Role{})

    // 创建用户和角色
    user := User{Name: "John Doe"}
    role := Role{Name: "Admin"}
    db.Create(&user)
    db.Create(&role)

    // 关联用户和角色
    db.Model(&user).Association("Roles").Append(&role)

    // 查询用户及其角色
    var retrievedUser User
    db.Preload("Roles").First(&retrievedUser, user.ID)
    fmt.Printf("User: %s, Roles count: %d\n", retrievedUser.Name, len(retrievedUser.Roles))
}

我们使用 Association 方法将用户和角色关联起来,然后使用 Preload 方法预加载用户的所有角色。

  • 预加载:使用 Preload 方法可以在查询主模型时同时加载关联的模型数据,避免 N+1 查询问题。
  • 关联操作:对于多对多关联,可以使用 Association 方法进行关联的添加、删除等操作。
  • 迁移:在使用关联关系之前,需要确保模型已经通过 AutoMigrate 方法进行了迁移,以创建相应的表和外键约束。

  • ​循环引用​​:确保模型间没有相互嵌套导致无限递归。
  • ​性能优化​​:避免 SELECT *,明确指定需要的字段。
  • ​索引优化​​:在频繁查询的字段上添加索引(如 gorm:"index")。
### Go Gin框架GORM框架的集成及使用教程 #### 项目初始化 创建一个新的Go模块并安装必要的依赖包。通过命令行工具来设置环境,确保所有必需组件都已就绪。 ```bash go mod init myproject go get -u github.com/gin-gonic/gin go get -u gorm.io/gorm go get -u gorm.io/driver/mysql ``` 这些命令分别用于初始化新的Go模块、获取Gin框架及其所需的支持库以及获取GORM ORM库和支持MySQL数据库的操作[^4]。 #### 配置数据库连接 为了使应用程序能够访问数据库,在`main.go`文件中添加如下代码片段: ```go package main import ( "gorm.io/driver/mysql" "gorm.io/gorm" ) func InitDB() *gorm.DB { dsn := "username: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") } return db } ``` 此函数负责打开到指定MySQL实例的数据源名称(DSN),并通过它建立一个持久化的数据库会话对象[^3]。 #### 定义数据模型 接下来定义一些结构体表示要存储于关系型数据库中的实体类。这里以用户表为例展示基本语法: ```go type User struct { ID uint `json:"id"` Name string `json:"name" binding:"required,min=3,max=50"` Email string `json:"email" binding:"required,email"` Password string `json:"-" binding:"required,min=6"` // 不返回给客户端 } // 自动迁移模式 db.AutoMigrate(&User{}) ``` 这段代码展示了如何声明一个简单的用户记录映射至SQL表格的方式,并调用了自动迁移功能使得程序可以根据定义自动生成相应的DDL语句[^2]。 #### 实现RESTful API接口 利用Gin路由机制快速构建HTTP服务端点。下面是一个完整的例子说明怎样实现增删改查(CRUD)操作: ```go router.POST("/users", func(c *gin.Context) { var user User c.BindJSON(&user) result := db.Create(&user) if result.Error != nil { c.JSON(http.StatusBadRequest, gin.H{"error":result.Error}) return } c.JSON(http.StatusOK, user) }) router.GET("/users/:id", func(c *gin.Context){ id := c.Param("id") var user User if err := db.First(&user,id).Error;err!=nil{ c.AbortWithStatus(http.StatusNotFound) return }else{ c.JSON(http.StatusOK,user) } }) router.PUT("/users/:id", func(c *gin.Context){ id:=c.Param("id") var updateUser=User{} if err:=c.ShouldBindJSON(&updateUser);err!=nil{ c.JSON(http.StatusBadRequest,err) return } tx:=db.Model(&User{}).Where("id=?",id).Updates(updateUser) if tx.RowsAffected==0{ c.JSON(http.StatusNotFound,"not found") return } c.JSON(http.StatusOK,map[string]interface{}{ "message":"success", "rows_affected":tx.RowsAffected, }) }) router.DELETE("/users/:id", func(c *gin.Context){ id:=c.Param("id") tx:=db.Delete(&User{},id) if tx.RowsAffected==0{ c.JSON(http.StatusNotFound,nil) return } c.JSON(http.StatusOK,gimn.H{"message":"deleted"}) }) ``` 上述代码实现了四个标准路径处理器,它们对应着不同的HTTP请求方法(GET/POST/PUT/DELETE)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chxii

小小打赏,大大鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值