介紹
GORM 是 Go 語言中最流行的 ORM(對象關系映射)庫之一,基于數據庫操作的封裝,提供類似 Django ORM / SQLAlchemy 的開發體驗。
特性 | 描述 |
---|---|
支持多種數據庫 | MySQL、PostgreSQL、SQLite、SQL Server、ClickHouse 等 |
自動遷移 | 自動根據 struct 生成數據庫表結構 |
CRUD 操作簡潔 | 簡潔直觀的增刪查改接口 |
支持事務 | 內置事務管理 |
預加載 | 一行代碼加載關聯數據(Preload) |
鉤子函數 | 提供 BeforeSave、AfterCreate 等生命周期鉤子 |
軟刪除 | 提供內置軟刪除支持 |
自定義 SQL | 可執行原生 SQL 或復雜查詢 |
遷移、索引 | 支持復合索引、唯一索引等設置 |
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
配置數據庫
一般來說,關于數據庫的配置 我喜歡放在 infra/* 這個文件夾下
這里以 pg 為例
pg_connection.go
package infraimport ("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.AppConfigif 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,)// 配置 GORMgormConfig := &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 errorDB, 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
func main() {// 1. 加載配置config := utils.LoadConfig()// 2. 初始化 PostgreSQLinfra.InitPG()defer infra.ClosePG()
}
GetDB
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 時才從連接池中臨時借用連接,執行完立即歸還。
db := infra.GetDB()
db.Find(&users)
它的實際行為如下:
- db := GetDB():只是獲取一個 *gorm.DB 對象,不涉及任何連接。
- db.Find(&users):
- 這一步 GORM 內部會調用 sql.DB.Conn(ctx) 或 sql.DB.QueryContext(ctx);
- 從連接池借一個連接;
- 執行 SQL;
- 自動關閉 rows,釋放連接,連接歸還連接池 ?
rows, err := db.Raw("SELECT * FROM users").Rows()
defer rows.Close() // 如果忘記寫這句,連接就不會釋放!!
場景 | 是否占用連接 |
---|---|
GetDB() | ? 不占用連接 |
db.Find()、db.First() 等 | ? 使用連接 → 自動釋放 |
SSE/流式接口 + 查詢前置 | ? 查詢連接已釋放,SSE 是純內存流 |
使用 Rows() 沒有手動 Close() | ?? 會占用連接池 |