Gin 框架中的数据库集成和 ORM 使用方法如下:
1. 数据库连接配置
1.1 使用 GORM
goimport ( "gorm.io/driver/mysql" "gorm.io/gorm" ) var db *gorm.DB func initDB() error { dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" var err error db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) if err != nil { return err } // 配置连接池 sqlDB, err := db.DB() if err != nil { return err } sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) return nil }
1.2 使用 sqlx
goimport ( "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql" ) var db *sqlx.DB func initDB() error { var err error db, err = sqlx.Connect("mysql", "user:password@tcp(127.0.0.1:3306)/dbname") if err != nil { return err } db.SetMaxOpenConns(100) db.SetMaxIdleConns(10) db.SetConnMaxLifetime(time.Hour) return nil }
2. 模型定义
2.1 GORM 模型
gotype User struct { ID uint `gorm:"primaryKey" json:"id"` Username string `gorm:"uniqueIndex;size:50;not null" json:"username"` Email string `gorm:"uniqueIndex;size:100;not null" json:"email"` Password string `gorm:"size:255;not null" json:"-"` Age int `gorm:"not null" json:"age"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` } func (User) TableName() string { return "users" }
2.2 数据库迁移
gofunc migrateDB() error { return db.AutoMigrate(&User{}, &Post{}, &Comment{}) }
3. CRUD 操作
3.1 创建记录
gofunc createUser(c *gin.Context) { var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } // 密码加密 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) if err != nil { c.JSON(500, gin.H{"error": "Failed to hash password"}) return } user.Password = string(hashedPassword) if err := db.Create(&user).Error; err != nil { c.JSON(500, gin.H{"error": "Failed to create user"}) return } c.JSON(201, user) }
3.2 查询记录
gofunc getUser(c *gin.Context) { id := c.Param("id") var user User if err := db.First(&user, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(404, gin.H{"error": "User not found"}) return } c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, user) } func listUsers(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) var users []User var total int64 if err := db.Model(&User{}).Count(&total).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } offset := (page - 1) * pageSize if err := db.Offset(offset).Limit(pageSize).Find(&users).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{ "data": users, "total": total, "page": page, "page_size": pageSize, }) }
3.3 更新记录
gofunc updateUser(c *gin.Context) { id := c.Param("id") var user User if err := db.First(&user, id).Error; err != nil { c.JSON(404, gin.H{"error": "User not found"}) return } var updateData map[string]interface{} if err := c.ShouldBindJSON(&updateData); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } if err := db.Model(&user).Updates(updateData).Error; err != nil { c.JSON(500, gin.H{"error": "Failed to update user"}) return } c.JSON(200, user) }
3.4 删除记录
gofunc deleteUser(c *gin.Context) { id := c.Param("id") if err := db.Delete(&User{}, id).Error; err != nil { c.JSON(500, gin.H{"error": "Failed to delete user"}) return } c.JSON(200, gin.H{"message": "User deleted successfully"}) }
4. 复杂查询
4.1 关联查询
gotype Post struct { ID uint `gorm:"primaryKey" json:"id"` Title string `gorm:"size:200;not null" json:"title"` Content string `gorm:"type:text" json:"content"` UserID uint `gorm:"not null" json:"user_id"` User User `gorm:"foreignKey:UserID" json:"user,omitempty"` Comments []Comment `gorm:"foreignKey:PostID" json:"comments,omitempty"` CreatedAt time.Time `json:"created_at"` } func getPostWithUser(c *gin.Context) { id := c.Param("id") var post Post if err := db.Preload("User").Preload("Comments").First(&post, id).Error; err != nil { c.JSON(404, gin.H{"error": "Post not found"}) return } c.JSON(200, post) }
4.2 条件查询
gofunc searchUsers(c *gin.Context) { keyword := c.Query("keyword") minAge := c.DefaultQuery("min_age", "0") var users []User query := db.Model(&User{}) if keyword != "" { query = query.Where("username LIKE ? OR email LIKE ?", "%"+keyword+"%", "%"+keyword+"%") } if minAge != "0" { query = query.Where("age >= ?", minAge) } if err := query.Find(&users).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, users) }
5. 事务处理
5.1 基本事务
gofunc transferFunds(c *gin.Context) { var transfer struct { FromID uint `json:"from_id" binding:"required"` ToID uint `json:"to_id" binding:"required"` Amount int `json:"amount" binding:"required,gt=0"` } if err := c.ShouldBindJSON(&transfer); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } // 开始事务 tx := db.Begin() // 检查余额 var fromUser User if err := tx.First(&fromUser, transfer.FromID).Error; err != nil { tx.Rollback() c.JSON(404, gin.H{"error": "User not found"}) return } if fromUser.Balance < transfer.Amount { tx.Rollback() c.JSON(400, gin.H{"error": "Insufficient balance"}) return } // 转账 if err := tx.Model(&fromUser).Update("balance", gorm.Expr("balance - ?", transfer.Amount)).Error; err != nil { tx.Rollback() c.JSON(500, gin.H{"error": "Failed to deduct balance"}) return } if err := tx.Model(&User{}).Where("id = ?", transfer.ToID).Update("balance", gorm.Expr("balance + ?", transfer.Amount)).Error; err != nil { tx.Rollback() c.JSON(500, gin.H{"error": "Failed to add balance"}) return } // 提交事务 if err := tx.Commit().Error; err != nil { c.JSON(500, gin.H{"error": "Failed to commit transaction"}) return } c.JSON(200, gin.H{"message": "Transfer successful"}) }
6. 数据库中间件
6.1 数据库上下文中间件
gofunc dbMiddleware(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { c.Set("db", db) c.Next() } } // 使用示例 func handlerWithDB(c *gin.Context) { db := c.MustGet("db").(*gorm.DB) // 使用 db 进行数据库操作 }
6.2 事务中间件
gofunc transactionMiddleware(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { tx := db.Begin() c.Set("tx", tx) defer func() { if r := recover(); r != nil { tx.Rollback() panic(r) } }() c.Next() // 如果没有错误,提交事务 if len(c.Errors) == 0 { tx.Commit() } else { tx.Rollback() } } }
7. 最佳实践
-
连接池配置
- 根据应用负载调整连接池大小
- 设置合理的连接超时时间
- 监控连接池使用情况
-
查询优化
- 使用索引加速查询
- 避免 N+1 查询问题
- 合理使用预加载
- 分页查询大数据集
-
事务管理
- 保持事务简短
- 正确处理事务错误
- 使用事务中间件简化代码
-
数据验证
- 在数据库层和业务层都进行验证
- 使用 GORM 的验证标签
- 自定义验证规则
-
错误处理
- 区分不同类型的数据库错误
- 提供友好的错误信息
- 记录详细的错误日志
-
安全性
- 使用参数化查询防止 SQL 注入
- 加密敏感字段
- 实现软删除
- 定期备份数据库
通过以上方法,可以在 Gin 框架中高效地集成和使用数据库。