跳到正文
返回文章

gorm配置数据库

JACIN10 分钟阅读Go 开发
AI 摘要展开 / 收起这份笔记面向 Go Web 项目中使用 GORM 接入数据库的基础配置场景,以 PostgreSQL 为例展示了将连接初始化放在 infra 目录下的组织方式。正文先概括 GORM 作为 ORM 的常见能力,包括多数据库…

这份笔记面向 Go Web 项目中使用 GORM 接入数据库的基础配置场景,以 PostgreSQL 为例展示了将连接初始化放在 infra 目录下的组织方式。正文先概括 GORM 作为 ORM 的常见能力,包括多数据库支持、自动迁移、事务、预加载、钩子、软删除和自定义 SQL,并给出安装 gorm 与数据库驱动的命令。配置部分围绕 InitPG 展开:从 utils.AppConfig 读取主机、用户、密码、库名和端口拼接 DSN,按开发环境开启 SQL Info 日志、生产环境静默日志,再通过 gorm.Open 建立连接并取得底层 sql.DB 设置连接池参数。文章还说明 main.go 中应在加载配置后调用 infra.InitPG,并用 defer infra.ClosePG 在应用退出时关闭连接,便于服务优雅释放资源。关于 GetDB,重点澄清它只是返回全局保存的 *gorm.DB 指针,不会重复建连,也不会负责释放连接;多个请求并发调用是安全的,因为 GORM 的实例本身并不长期持有物理连接。真正执行 Find、First 等查询时,GORM 会从连接池临时借用连接并在完成后归还,但使用 Raw().Rows() 这类手动遍历结果集的写法必须显式 Close,否则可能持续占用连接池。整体适合正在搭建 Gin/GORM 项目骨架、需要理解连接池生命周期和全局 DB 访问方式的后端开发者。

目录#

[[toc]]

介绍#

GORM 是 Go 语言中最流行的 ORM(对象关系映射)库之一,基于数据库操作的封装,提供类似 Django ORM / SQLAlchemy 的开发体验。

特性描述
支持多种数据库MySQL、PostgreSQL、SQLite、SQL Server、ClickHouse 等
自动迁移自动根据 struct 生成数据库表结构
CRUD 操作简洁简洁直观的增删查改接口
支持事务内置事务管理
预加载一行代码加载关联数据(Preload)
钩子函数提供 BeforeSave、AfterCreate 等生命周期钩子
软删除提供内置软删除支持
自定义 SQL可执行原生 SQL 或复杂查询
迁移、索引支持复合索引、唯一索引等设置
jsx
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

配置数据库#

一般来说,关于数据库的配置 我喜欢放在 infra/* 这个文件夹下

这里以 pg 为例

pg_connection.go

go
package infra

import (
	"fmt"
	"time"

	"gin-api-template/utils"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var DB *gorm.DB //定义一个全局变量 DB,用于保存数据库连接句柄。可供全局调用。

// InitPG 初始化 PostgreSQL 连接
func InitPG() {
	config := utils.AppConfig
	if config == nil {
		utils.LogError("Config not loaded")
		return
	}

	// 构建数据库连接字符串
	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Shanghai",
		config.DBHost,
		config.DBUser,
		config.DBPassword,
		config.DBName,
		config.DBPort,
	)

	// 配置 GORM
	gormConfig := &gorm.Config{}

	// 根据环境设置日志级别,开发模式下显示 SQL 日志,生产模式下静默。
	if utils.IsDevelopment() {
		gormConfig.Logger = logger.Default.LogMode(logger.Info)
		utils.LogInfo("PostgreSQL debug mode enabled")
	} else {
		gormConfig.Logger = logger.Default.LogMode(logger.Silent)
	}

	// 连接数据库
	var err error
	DB, err = gorm.Open(postgres.Open(dsn), gormConfig)
	if err != nil {
		utils.LogError("Failed to connect to PostgreSQL: " + err.Error())
		panic(err)
	}

	// 配置连接池
	sqlDB, err := DB.DB()
	if err != nil {
		utils.LogError("Failed to get PostgreSQL instance: " + err.Error())
		panic(err)
	}

	// 设置连接池参数
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(time.Hour) //  每个连接的最大生命周期(如过期回收)

	utils.LogInfo("PostgreSQL connected successfully")
}

// GetDB 获取数据库实例
func GetDB() *gorm.DB {
	return DB
}

// ClosePG 关闭数据库连接
func ClosePG() {
	if DB != nil {
		sqlDB, err := DB.DB()
		if err != nil {
			utils.LogError("Failed to get PostgreSQL instance for closing: " + err.Error())
			return
		}

		if err := sqlDB.Close(); err != nil {
			utils.LogError("Failed to close PostgreSQL: " + err.Error())
		} else {
			utils.LogInfo("PostgreSQL connection closed")
		}
	}
}

在 main.go

jsx
func main() {
	// 1. 加载配置
	config := utils.LoadConfig()

	// 2. 初始化 PostgreSQL
	infra.InitPG()
	defer infra.ClosePG()
}

GetDB#

go
func GetDB() *gorm.DB {
	return DB
}

这是一个全局访问数据库实例的 Getter 函数,它的作用很简单:

让你在项目中任何地方通过调用 infra.GetDB() 获取一个 *gorm.DB 实例,从而操作数据库。

  • ✅ 数据库连接在 InitPG() 初始化时创建
  • ❌ GetDB() 不会重复建立连接,它只是返回之前初始化好的连接指针
  • ❌ 它也不会自动释放连接
问题回答
GetDB() 会不会新建连接?不会,它只是返回初始化后的 *gorm.DB 实例。
GetDB() 会不会释放连接?不会,连接释放由 GORM 内部连接池自动管理或由 ClosePG() 完成。
如果不主动关闭会有问题吗?一般不会,因为连接池会回收,但优雅关闭应用时应调用 ClosePG()。
多个请求并发使用 GetDB() 安全么?是安全的,GORM 是并发安全的,底层连接池会复用连接。

GORM 的 *gorm.DB 实例是线程安全的,不会持有数据库连接本身,只有执行 SQL 时才从连接池中临时借用连接,执行完立即归还。

go
db := infra.GetDB()
db.Find(&users)

它的实际行为如下:

  1. db := GetDB():只是获取一个 *gorm.DB 对象,不涉及任何连接。
  2. db.Find(&users):
    • 这一步 GORM 内部会调用 sql.DB.Conn(ctx) 或 sql.DB.QueryContext(ctx);
    • 从连接池借一个连接;
    • 执行 SQL;
    • 自动关闭 rows,释放连接,连接归还连接池 ✅
go
rows, err := db.Raw("SELECT * FROM users").Rows()
defer rows.Close()  // 如果忘记写这句,连接就不会释放!!
场景是否占用连接
GetDB()❌ 不占用连接
db.Find()、db.First() 等✅ 使用连接 → 自动释放
SSE/流式接口 + 查询前置✅ 查询连接已释放,SSE 是纯内存流
使用 Rows() 没有手动 Close()⚠️ 会占用连接池

评论

还没有评论,来发第一个吧