#数据库
这里以目前流行的 pgsql为例子,这里我们以ORM库Gorm为例(不手撸SQL,所以抛弃jackc的pgx)
#安装依赖
# 环境变量插件包
go get github.com/joho/godotenv
# 数据库驱动和Gorm包
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres#代码示例
封装一个工具
utils/db-helper.go
package utils
import (
"fmt"
"os"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
// DB 定义一个全局变量,以便其他地方调用
var DB *gorm.DB
func InitDB() {
var err error
databaseUrl := os.Getenv("PGSQL_PARAMS")
println(databaseUrl)
// 注意:这里使用 = 而不是 :=,因为 DB 已经在外部声明了
DB, err = gorm.Open(postgres.Open(databaseUrl), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 强制使用单数表名
},
})
if err != nil {
panic("无法连接数据库: " + err.Error())
}
}
func CloseDB() {
sqlDB, err := DB.DB()
if err != nil {
fmt.Println("获取 sql.DB 失败:", err)
return
}
err = sqlDB.Close()
if err != nil {
fmt.Println("关闭数据库连接失败:", err)
} else {
fmt.Println("--- 数据库连接已安全关闭 ---")
}
}然后在主程序中注册它
main.go
func main() {
// 加载环境变量
err := godotenv.Load()
if err != nil {
log.Fatal("加载环境变量文件出错")
}
// 初始化连接池,程序结束时关闭连接池
utils.InitDB()
defer utils.CloseDB()
// 注册路由并启动服务
r := routers.InitRouter()
r.Run()
}service层里就可以使用了,比如crud:只要定义好模型,直接使用模型操作数据库,这都是 gorm 插件做的事情,它就是 orm库
model/article.go
package model
import "time"
type Article struct {
// 必须大写开头!后面反引号里的才是数据库真实的列名
Id int `gorm:"column:id" json:"id"`
Title string `gorm:"column:title" json:"title"`
Description string `gorm:"column:description" json:"description"`
TypeId string `gorm:"column:type_id" json:"typeId"`
CreateTime time.Time `gorm:"column:create_time" json:"createTime"`
Content string `gorm:"column:content" json:"content"`
}
// ArticleVO 展示模型 (VO),直接在下面定义,不用谢重复字段,用匿名嵌套
type ArticleVO struct {
Article // 组合 Article 的所有字段
TypeName string `json:"typeName" gorm:"column:type_name"` // 扩展字段
}
ackage service
import (
"blog-2026-server/model"
"blog-2026-server/utils"
)
type ArticleService struct{}
func (s *ArticleService) GetList(current, size int) (model.PageResult[model.ArticleVO], error) {
var count int64
var results []model.ArticleVO
// 1. 统计总数
if err := utils.DB.Model(&model.Article{}).Count(&count).Error; err != nil {
return model.PageResult[model.ArticleVO]{}, err
}
// 2. 只有有数据时才查询详情
if count > 0 {
err := utils.DB.Table("article a").
Select("a.*, t.name as type_name").
Joins("left join type t on a.type_id = t.id").
Order("a.create_time desc").
Offset((current - 1) * size).
Limit(size).
Scan(&results).Error
if err != nil {
return model.PageResult[model.ArticleVO]{}, err
}
}
// 3. 组装返回结果
return model.PageResult[model.ArticleVO]{
List: results,
Total: count,
Current: current,
Size: size,
HasMore: int64(current*size) < count,
}, nil
}
func (s *ArticleService) GetOne(id int) (model.ArticleVO, error) {
var result model.ArticleVO
err := utils.DB.Table("article a").
Select("a.*, t.name as type_name").
Joins("left join type t on a.type_id::int = t.id").
Where("a.id = ?", id).
Scan(&result).Error
return result, err
}
func (s *ArticleService) Save(article *model.Article) error {
// 自动生成摘要:取前 120 个字
article.Description = utils.MdGetSummary(article.Content)
// Save 成功后,GORM 会自动把数据库生成的 ID、CreatedAt 等回填到 article 指针指向的内存,我们只需要解引用,返回这个结构体的值即可
return utils.DB.Save(article).Error
}
func (s *ArticleService) Update(article *model.Article) error {
return utils.DB.Model(article).Updates(article).Error
}
func (s *ArticleService) Delete(id int) error {
return utils.DB.Delete(&model.Article{}, id).Error
}