Go从入门到精通(23) - 一个简单web项目-使用数据库存储数据

发布于:2025-07-15 ⋅ 阅读:(21) ⋅ 点赞:(0)

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更多用法

更多的用法参考中文官网


网站公告

今日签到

点亮在社区的每一天
去签到