使用 GORM 连接数据库并实现增删改查操作| 豆包MarsCode AI刷题

139 阅读5分钟

在实际项目中,直接使用原生 SQL 虽然能满足需求,但随着业务复杂度的增加,手写 SQL 会让代码变得冗长且难以维护。后来,我选择了 GORM,这是一款功能强大的 Go ORM 库,它的便捷性和灵活性让我感受到了数据库操作的新思路。

初识 GORM:连接数据库

GORM 的安装过程并不复杂,我选择使用 MySQL 驱动作为例子。安装完成后,通过简单的几行代码便能连接数据库:

package main

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

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 {
		log.Fatalf("连接数据库失败: %v", err)
	}

	log.Println("数据库连接成功")
}

一开始,我遇到的问题是连接配置过于分散,比如用户名、密码等敏感信息直接写在代码中。这显然不够安全也不利于维护。于是,我采用 .env 文件存储这些信息,并通过 os.Getenv 读取它们,这让代码更加清晰和易于移植。

此外,生产环境中我发现一个重要问题——数据库连接池的管理。在 GORM 中,可以通过设置最大连接数和连接存活时间来避免连接耗尽:

sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)

定义模型:表与结构体的映射

接下来,我需要定义一个模型,它对应数据库中的某个表。例如,在用户管理的场景下,一个简单的 User 模型如下:

type User struct {
	ID       uint   `gorm:"primaryKey"`
	Name     string `gorm:"size:100;not null"`
	Email    string `gorm:"unique;not null"`
	Password string `gorm:"size:255;not null"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

这个结构体中的字段和数据库表的列一一对应,我还利用 GORM 提供的标签对字段进行约束。例如,Email 字段添加了唯一性约束,Name 字段限制了长度。 在设计模型时,我意识到一个问题:如果表结构修改了,比如新增一个字段,代码中的模型也需要同步调整。这提醒我,在多人协作项目中,需要保持数据库和模型定义的一致性,可以借助 GORM 的迁移功能(AutoMigrate)快速同步表结构:

db.AutoMigrate(&User{})

但这里需要注意,自动迁移功能虽然方便,但也可能导致表结构的意外修改,特别是在生产环境中使用时需要格外小心。


实现增删改查功能

在实现创建用户时,我使用了 GORM 提供的 Create 方法:

func CreateUser(db *gorm.DB, user User) error {
	result := db.Create(&user)
	if result.Error != nil {
		return result.Error
	}
	return nil
}

这个方法让我感受到 ORM 的便利,省去了手写 INSERT 语句的麻烦。不过需要注意的是,在插入数据前,建议对数据进行校验,例如检查 Email 格式是否正确。这种前置校验能够减少数据库的压力。


实现查询用户功能时,我选择使用 First 方法通过主键查找用户:

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

在使用过程中,我发现 GORM 提供了丰富的查询方法,例如通过 Where 条件查询多个记录:

var users []User
db.Where("name = ?", "Alice").Find(&users)

虽然 ORM 查询方法很强大,但在实际项目中,当遇到复杂查询(如多表关联)时,我发现直接写原生 SQL 反而更清晰,这也是 GORM 的一个灵活点——它允许直接执行 SQL 语句。


对于更新用户的场景,我使用了 ModelUpdate 方法:

func UpdateUserEmail(db *gorm.DB, id uint, newEmail string) error {
	result := db.Model(&User{}).Where("id = ?", id).Update("email", newEmail)
	if result.Error != nil {
		return result.Error
	}
	return nil
}

通过这种方式,可以避免更新整个记录,仅修改指定字段。这里让我感受到 GORM 对性能的优化考虑。不过在生产环境中,我会更倾向于使用事务操作,确保更新的原子性,避免因中途失败导致数据不一致。


删除记录的实现相对简单:

func DeleteUserByID(db *gorm.DB, id uint) error {
	result := db.Delete(&User{}, id)
	if result.Error != nil {
		return result.Error
	}
	return nil
}

值得一提的是,GORM 默认支持软删除,通过在模型中添加 DeletedAt 字段,删除操作只会标记记录为删除,而非真正移除。这对需要数据追溯的场景非常有用。但如果不需要软删除功能,可以通过额外配置禁用它。


思考与总结

在使用 GORM 的过程中,我不仅感受到了 ORM 工具带来的开发效率提升,也意识到了一些潜在的挑战:

  1. 适用场景
    GORM 非常适合中小型项目或对性能要求不那么极端的场景。它能快速完成常见的数据库操作,降低开发门槛。但对于需要高度优化的复杂查询场景,原生 SQL 可能更具优势。

  2. 团队协作
    在团队开发中,统一的代码规范非常重要。例如,模型设计和数据库迁移策略需要事先讨论清楚,以免因为模型与表结构不一致而引发问题。

  3. 学习曲线
    GORM 的功能丰富,但也意味着需要一定的学习成本。尤其是像事务管理、多表关联这样的高级功能,需要开发者具备一定的数据库知识。

总之,GORM 是一把利器,它让我从手写 SQL 的繁琐中解放出来,更专注于业务逻辑的实现。但要真正用好它,仍需要根据实际需求做权衡,避免过度依赖带来的性能问题。希望我的实践经验能对你有所帮助!