安装与快速开始
GORM 是 Go 语言中最流行的 ORM 框架,提供全功能的对象关系映射。它内置了 CRUD 操作、关联关系、事务、钩子、迁移等能力,同时保留原生 SQL 的灵活性。
官网文档:GORM Guides GitHub:go-gorm/gorm
前置条件
- Go 版本:GORM v2 需要 Go 1.14 或更高版本(推荐 Go 1.18+)
- 数据库驱动:GORM 本身不包含数据库驱动,需额外安装对应驱动(见下文)
安装
# 安装 GORM 核心库
go get -u gorm.io/gorm
# 按需安装数据库驱动(以 MySQL 和 PostgreSQL 为例)
go get -u gorm.io/driver/mysql # MySQL
go get -u gorm.io/driver/postgres # PostgreSQL
go get -u gorm.io/driver/sqlite # SQLite
go get -u gorm.io/driver/sqlserver # SQL Server第一个程序
MySQL 版
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 构建 DSN(Data Source Name)
dsn := "user:pass@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 {
panic("failed to connect database")
}
// GORM 会自动维护一个连接池,无需手动关闭单次连接
// 如果需要关闭整个连接池(一般在程序退出时),通过 sqlDB 操作:
// sqlDB, _ := db.DB()
// defer sqlDB.Close()
// 自动迁移(开发环境)
db.AutoMigrate(&User{})
// CRUD 操作
db.Create(&User{Name: "张三", Age: 18})
var user User
db.First(&user, 1) // 按主键查找
db.First(&user, "name = ?", "张三") // 按条件查找
db.Model(&user).Update("Age", 20)
db.Delete(&user, 1)
}
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100"`
Age int
}PostgreSQL 版
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func main() {
dsn := "host=localhost user=postgres password=pass dbname=mydb port=5432 sslmode=disable TimeZone=Asia/Shanghai"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// ...
}MySQL DSN 参数速查
| 参数 | 说明 | 推荐值 |
|---|---|---|
charset |
字符集 | utf8mb4(支持 Emoji) |
parseTime |
将 DATE/DATETIME 转为 time.Time |
True(必须开启,否则 Scan 报错) |
loc |
时区 | Local 或 Asia%2FShanghai |
timeout |
连接超时 | 10s |
readTimeout |
读超时 | 30s |
writeTimeout |
写超时 | 30s |
示例 DSN:
user:pass@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s🚨 陷阱:
parseTime=True必须设置,否则查询DATETIME字段时Scan会报错unsupported Scan, storing driver.Value type []uint8 into type *time.Time。
PostgreSQL DSN 参数速查
| 参数 | 说明 | 推荐值 |
|---|---|---|
host |
主机地址 | localhost |
port |
端口 | 5432 |
user |
用户名 | — |
password |
密码 | — |
dbname |
数据库名 | — |
sslmode |
SSL 模式 | disable(本地)/require(生产) |
TimeZone |
会话时区 | Asia/Shanghai |
示例 DSN:
host=localhost user=postgres password=pass dbname=mydb port=5432 sslmode=disable TimeZone=Asia/Shanghaigorm.Config 核心配置
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
// 是否跳过默认事务(默认 false,每个写操作包裹在事务中)
SkipDefaultTransaction: false,
// 命名策略(表名、列名规则)
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // 表名前缀
SingularTable: true, // 使用单数表名(默认复数)
NoLowerCase: false, // 禁用小写转换
},
// 关闭外键约束(迁移时不创建物理外键)
DisableForeignKeyConstraintWhenMigrating: true,
// Logger 配置
Logger: logger.Default.LogMode(logger.Info),
// 预备语句缓存(提升性能)
PrepareStmt: true,
// 启用 DryRun 模式(只生成 SQL 不执行)
DryRun: false,
})| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
SkipDefaultTransaction |
bool |
false |
跳过默认事务,写操作不再自动包裹 |
NamingStrategy |
NamingStrategy |
— | 表名/列名/外键命名规则 |
DisableForeignKeyConstraintWhenMigrating |
bool |
false |
禁用物理外键约束创建 |
Logger |
logger.Interface |
logger.Default |
SQL 日志输出配置 |
PrepareStmt |
bool |
false |
启用预编译语句缓存 |
DryRun |
bool |
false |
只生成 SQL 不执行(调试用) |
NowFunc |
func() time.Time |
— | 自定义 time.Now()(时区测试用) |
💡 最佳实践:开发环境开启
Logger: logger.Default.LogMode(logger.Info)以查看所有 SQL;生产环境使用logger.Warn级别。启用PrepareStmt可提升约 10%~20% 的重复查询性能。
获取底层 *sql.DB
GORM 使用 database/sql 作为底层连接池,通过 db.DB() 获取:
sqlDB, err := db.DB()
// 连接池配置
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间
sqlDB.SetConnMaxIdleTime(10 * time.Minute) // 空闲连接最大存活时间(Go 1.15+)
// Ping 检查连接
if err := sqlDB.Ping(); err != nil {
log.Fatal(err)
}💡 最佳实践:生产环境务必设置
SetConnMaxLifetime,否则 MySQL 默认 8 小时断开空闲连接,GORM 不会自动重连。
基本项目结构推荐
project/
├── main.go // 入口:初始化 DB、启动服务
├── config/
│ └── config.go // 配置管理(DSN、连接池参数)
├── model/
│ ├── user.go // User 模型定义
│ └── order.go // Order 模型定义
├── dao/ // 数据访问层(或 repository/)
│ ├── user_dao.go
│ └── order_dao.go
├── service/ // 业务逻辑层
│ └── user_service.go
├── handler/ // HTTP 处理函数(若使用 Gin 等框架)
│ └── user_handler.go
└── go.mod💡 大型项目建议将 DB 实例封装在 DAO 层,Service 层只依赖接口,便于单元测试时 Mock。
常见连接错误速查
| 错误信息 | 原因 | 解决 |
|---|---|---|
dial tcp: connect: connection refused |
数据库未启动或端口错误 | 检查数据库服务和端口 |
Error 1045: Access denied |
用户名或密码错误 | 检查 DSN 中的 user/pass |
Error 1049: Unknown database |
数据库不存在 | 先创建数据库:CREATE DATABASE mydb |
Error 2002: Can't connect to local MySQL server |
MySQL 未运行或 socket 路径不对 | 启动 MySQL 或改用 TCP 连接 |
pq: password authentication failed |
PostgreSQL 密码错误 | 检查 pg_hba.conf 和密码 |
pq: database "xxx" does not exist |
PostgreSQL 数据库不存在 | 先创建数据库 |
SSL is not enabled on the server |
PostgreSQL sslmode 不匹配 | 设置 sslmode=disable |
invalid connection |
连接已断开(超出存活时间) | 设置 SetConnMaxLifetime |