Gin 框架中的数据库集成和 ORM 如何使用?
Gin 框架中的数据库集成和 ORM 使用方法如下:1. 数据库连接配置1.1 使用 GORMimport ( "gorm.io/driver/mysql" "gorm.io/gorm")var db *gorm.DBfunc 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 使用 sqlximport ( "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")var db *sqlx.DBfunc 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 模型type 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 数据库迁移func migrateDB() error { return db.AutoMigrate(&User{}, &Post{}, &Comment{})}3. CRUD 操作3.1 创建记录func 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 查询记录func 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 更新记录func 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 删除记录func 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 关联查询type 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 条件查询func 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 基本事务func 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 数据库上下文中间件func 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 事务中间件func 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 框架中高效地集成和使用数据库。