Go从入门到精通(23)
一个简单web项目-使用数据库存储数据
前言
GORM 是 Go 语言中最流行的 ORM(对象关系映射)库之一,用于简化数据库操作。它提供了直观的 API,支持主流数据库(如 MySQL、PostgreSQL、SQLite 等),并具备自动迁移、关联查询、事务管理等功能。
安装依赖
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres
创建数据库模型
// app/repository/models/user.go
package models
import (
"time"
)
// User 用户模型
type User struct {
ID string `gorm:"primaryKey;size:36" json:"id"`
Username string `gorm:"unique;not null;size:50" json:"username"`
Password string `gorm:"not null" json:"-"` // 不返回密码
Email string `gorm:"unique;not null;size:100" json:"email"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
配置数据库连接
数据库安装与使用自行查阅其他资料,这里不再讲解
package repository
import (
"go-web-demo/app/repository/models"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log"
"os"
)
var DB *gorm.DB
func InitGormDB() error {
dsn := os.Getenv("DATABASE_DSN")
if dsn == "" {
// 默认使用 PostgreSQL
dsn = "host=localhost user=postgres password=password dbname=user_db port=5432 sslmode=disable TimeZone=Asia/Shanghai"
}
var err error
var dialect gorm.Dialector
// 根据环境变量选择数据库驱动
dbDriver := os.Getenv("DB_DRIVER")
switch dbDriver {
case "mysql":
dialect = mysql.Open(dsn)
default:
dialect = postgres.Open(dsn)
}
DB, err = gorm.Open(dialect, &gorm.Config{})
if err != nil {
log.Fatalf("无法连接数据库: %v", err)
return err
}
// 自动迁移模型
if err := DB.AutoMigrate(&models.User{}); err != nil {
log.Fatalf("数据库迁移失败: %v", err)
return err
}
log.Println("数据库连接成功")
return nil
}
修改用户处理函数使用数据库
// handlers/user_handlers.go
package handlers
import (
"encoding/json"
"net/http"
"strings"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"your-project/database"
"your-project/models"
)
// 全局变量替换为数据库连接
// var users = make(map[string]models.User)
// var nextUserID = 1
// RegisterHandler 注册新用户
func RegisterHandler(c *gin.Context) {
var request dto.RegisterRequest
// 绑定并验证请求
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 检查用户名是否已存在
var existingUser models.User
if err := repository.DB.Where("username = ?", request.Username).First(&existingUser).Error; err == nil {
c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"})
return
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库错误"})
return
}
// 哈希密码
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)
if err != nil {
logger.Sugar.Errorw("密码哈希失败", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})
return
}
// 创建新用户
user := models.User{
ID: utils.GenerateUniqueID(), // 实现一个生成唯一ID的函数
Username: request.Username,
Password: string(hashedPassword),
Email: request.Email,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// 保存到数据库
if err := repository.DB.Create(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户失败"})
return
}
// 生成令牌
token, err := utils.GenerateToken(user.ID)
if err != nil {
logger.Sugar.Errorw("生成令牌失败", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
return
}
c.JSON(http.StatusCreated, dto.TokenResponse{Token: token})
}
// 其他处理函数类似修改...
修改主函数初始化数据库
// main.go
func main() {
// 连接数据库
if err := repository.InitGormDB(); err != nil {
panic(err)
}
//.. 其他逻辑
}
添加辅助函数
// utils/utils.go
package utils
import (
"crypto/rand"
"encoding/hex"
"time"
"github.com/dgrijalva/jwt-go"
)
// 生成唯一ID
func GenerateUniqueID() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}
// 生成JWT令牌
func GenerateToken(userID string) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["id"] = userID
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
return token.SignedString([]byte("your-secret-key"))
}
配置环境变量
根据自己环境配置,下面为例子
DATABASE_DSN=host=localhost user=postgres password=password dbname=user_db port=5432 sslmode=disable
插件与扩展
GORM 支持通过插件增强功能,例如:
- gorm.io/plugin/dbresolver:读写分离、多数据库支持
- gorm.io/plugin/soft_delete:软删除(逻辑删除)
- gorm.io/plugin/optimisticlock:乐观锁实现
小结
上面的例子只是简单的说明gorm的用法,实际项目中使用可能会更加复杂,中大型项目可能会要求,数据库交互层(repository)独立,外部通过接口来调用,大家可自行实践。
gorm更多用法
更多的用法参考中文官网