前言
在上一篇中,我们已经实现了一个可以接收 JSON 请求的 /register 接口。那我们既然可以正常拿到请求参数 我们是不是就可以进一步完善功能,将用户数据真正写入 MySQL 数据库。
项目结构更新
go
gin-learn-notes/
├── config/
│ └── database.go ✅ 新增:初始化数据库
├── model/
│ └── user.go ✅ 新增:用户数据模型
├── controller/
│ └── user.go ✅ 修改:写入数据库
├── main.go ✅ 修改:初始化 DB
第一步:安装 GORM 和 MySQL 驱动
bash
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
第二步:配置数据库连接
创建文件 config/database.go:
go
package config
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB // 全局数据库实例
func InitDB() {
dsn := "root:123456@tcp(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 表名不加 s,user 而不是 users
},
})
if err != nil {
panic("数据库连接失败:" + err.Error())
}
DB = db
fmt.Println(" 数据库连接成功")
}
⚠️ 记得提前在自己的本地 MySQL 中创建好
demo数据库。
关于 SingularTable: true
GORM 默认会把模型名转成复数作为表名:
- 结构体名为
User,默认表名是users - 加了
SingularTable: true后,表名就会是user(保持单数)
这是为了保持和我们手动建表时的命名一致,特别适合不想用复数表名的团队或开发者(比如我 😂)
第三步:定义数据模型
新建文件 model/user.go:
go
package model
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:50"`
Age int `gorm:"default:18"`
}
补充说明:GORM 字段标签解析 我们定义了用户模型结构体如下:
go
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:50"`
Age int `gorm:"default:18"`
}
GORM 通过结构体字段上的 gorm:"..." 标签来自定义字段的行为,这里每个字段都做了精简的配置:
| 字段 | 标签说明 | 含义 |
|---|---|---|
ID |
primaryKey |
设置为主键,GORM 会自动将其作为表的主键字段 |
Name |
size:50 |
设置该字段最大长度为 50(默认是 255) |
Age |
default:18 |
设置该字段在插入时默认值为 18 |
这些标签在 AutoMigrate 自动建表时,会影响最终生成的表结构。
第四步:在 main.go 中初始化数据库并自动建表
go
package main
import (
"gin-learn-notes/config"
"gin-learn-notes/model"
"gin-learn-notes/router"
)
func main() {
// 初始化数据库
config.InitDB()
// 自动建表
config.DB.AutoMigrate(&model.User{})
// 启动路由
r := router.InitRouter()
r.Run(":8080")
}
补充说明:为什么在 main.go 中自动建表?
我们上面在 main.go 中调用:
go
config.DB.AutoMigrate(&model.User{})
这样可以让 GORM 在项目启动时,自动根据结构体模型创建(或更新)数据库表结构,这对开发调试非常方便:
- 新增字段或模型后,无需手动写建表 SQL
- 本地开发时可以快速迭代数据库结构
但是正式环境建议谨慎使用!
虽然 AutoMigrate() 很方便,但在 正式环境 中通常不建议自动执行建表操作,原因是:
- 自动迁移可能会覆盖或误改已有表结构
- 不可控,无法精细管理字段变更或约束
- 正式环境更推荐使用手动 SQL 迁移 或版本化迁移工具 (如 golang-migrate)
这里我是为了便于学习,才选择在 main.go 中自动建表;将来上线时,要使用更严谨的方式管理数据库结构变更。
第五步:在注册接口中写入数据库
我们修改 controller/user.go:
go
package controller
import (
"github.com/gin-gonic/gin"
"gin-learn-notes/config"
"gin-learn-notes/model"
"net/http"
)
type RegisterRequest struct {
Name string `json:"name"`
Age int `json:"age"`
}
func Register(c *gin.Context) {
var req RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user := model.User{
Name: req.Name,
Age: req.Age,
}
if err := config.DB.Create(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "用户保存失败"})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "注册成功",
"user_id": user.ID,
})
}
上面代码中我们在控制器中做了这一段逻辑 👇:
go
user := model.User{
Name: req.Name,
Age: req.Age,
}
意思是:
- 将前端传来的参数(已经通过结构体绑定后的
req)封装为一个User模型对象 - 这个结构体和数据库表字段是一一对应的(通过 GORM 映射)
go
if err := config.DB.Create(&user).Error; err != nil {
...
}
这句是关键的数据库操作:
config.DB是我们前面初始化好的 GORM 实例Create(&user)表示插入一条新记录到 user 表中.Error是 GORM 返回的执行结果错误(如果有)
如果写入失败(例如数据库连接中断、字段类型不匹配等),我们就返回一个 500 错误响应:
go
c.JSON(http.StatusInternalServerError, gin.H{"error": "用户保存失败"})
如果写入成功,GORM 会自动为 user.ID 填充数据库生成的主键 ID,我们把它一并返回给前端:
go
c.JSON(http.StatusOK, gin.H{
"message": "注册成功",
"user_id": user.ID,
})
这段代码就实现了一个完整的用户注册入库流程 :
前端提交 JSON → 后端绑定参数 → 构造模型 → 写入数据库 → 返回响应。
后面我们还可以为这一步添加参数校验、重复检查、密码加密、事务处理等功能,逐步完善。
第六步:测试!
启动程序:
bash
go run main.go
使用 Postman 或 curl 发送 POST 请求:
json
POST /register
{
"name": "Alice",
"age": 20
}
数据库中就会新增一条用户记录!
这样操作后我们就已经学会了:
- 使用 GORM 连接 MySQL
- 创建数据模型并自动建表
- 在接口中插入数据
最后我们目前的结构目录就成了现在这样:
text
gin-learn-notes/
├── config/ # 配置模块
│ └── database.go # 数据库初始化
├── controller/ # 控制器层,处理路由逻辑
│ ├── hello.go
│ ├── index.go
│ └── user.go
├── model/ # 数据模型(对应数据表)
│ └── user.go
├── router/ # 路由注册
│ └── router.go
├── main.go # 程序入口,初始化数据库与启动服务
├── go.mod # Go Modules 管理文件
├── .gitignore
└── README.md
项目结构说明
config/:用于初始化各种全局配置(如数据库、后续可能的日志配置等)controller/:控制器层,处理具体业务逻辑,对外暴露路由接口model/:模型层,定义数据库表结构router/:负责路由统一注册,保持main.go简洁main.go:程序主入口,包含数据库初始化、建表、启动服务等
目前我们采用的是最基础的三层结构(控制器 → 模型 → 数据库),接下来可以继续拆出:
service/:业务逻辑服务层(推荐在第 4~5 篇加入)request/:请求参数结构体封装(便于加校验标签)middleware/:中间件,如 JWT、日志、跨域等
这样项目将更加清晰、可扩展。后面我们会逐步进行拆分封装。
最后
在本篇中,我们完成了用户注册接口的落地实现,并正式接入了数据库操作,主要包括:
- 安装并集成 GORM + MySQL 驱动
- 编写数据库初始化逻辑,并在启动时自动建表
- 定义用户数据模型,并使用结构体标签控制字段行为
- 将前端提交的数据写入数据库,并返回响应结果
至此,我们的 Gin 项目已经具备了从接收请求 → 处理参数 → 写入数据库 → 返回响应的基本能力,搭建起了一个初步的后端接口服务框架。
本篇对应代码提交记录
commit: 9f57513fdd089d9c8f7dad5d4a3c2577ccf24234
👉 GitHub 源码地址:github.com/luokakale-k...
下一篇我们将继续深入,学习使用 binding 标签配合 Gin 的内置校验器,实现请求参数的自动验证,提升接口的健壮性与开发体验。
同时,我们还将对请求参数结构体和响应结构体进行分离管理,让代码结构更加清晰、职责更明确,也为后续的统一返回格式和接口文档打好基础。