如题,本文章记录如何基于gin、gorm实现一个简单的服务示例
模型类
很简单,不多说
package models
type User struct {
ID uint `gorm:"primary_key"`
Username string `gorm:"not null"`
Account string `gorm:"not null;unique"`
}
数据库配置
package database
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"log"
"user-center-go/models"
)
// 如果你要使用 MySQL 数据库,请修改下面的配置信息,并在main.go中改为调用InitMysqlDB
const (
USER = "root"
PASS = "123456"
HOST = "127.0.0.1"
PORT = "3306"
DBNAME = "db1"
)
var DB *gorm.DB
func InitMysqlDB() {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", USER, PASS, HOST, PORT, DBNAME)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 使用单数表名
},
})
if err != nil {
log.Fatal(err)
}
DB = db
migrateTables()
}
func InitSqliteDB() {
db, err := gorm.Open(sqlite.Open("database.db"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 使用单数表名
},
})
if err != nil {
log.Fatal(err)
}
DB = db
migrateTables()
insertInitialData()
}
func migrateTables() {
err := DB.AutoMigrate(&models.User{})
if err != nil {
log.Fatalf("Failed to migrate database tables: %v", err)
}
}
func insertInitialData() {
initialData := []models.User{
{ID: 1, Username: "Alice", Account: "10001"},
{ID: 2, Username: "Bob", Account: "10002"},
{ID: 3, Username: "Cathy", Account: "10003"},
// 添加更多初始数据...
}
for _, data := range initialData {
result := DB.Create(&data)
if result.Error != nil {
log.Fatal(result.Error)
}
}
}
上面代码是一个数据库初始化的包。它使用了 GORM 库来管理数据库连接和操作。
首先定义了一些常量,用于配置连接 MySQL 数据库的参数:
const (
USER = "root"
PASS = "123456"
HOST = "127.0.0.1"
PORT = "3306"
DBNAME = "db1"
)
然后定义了一个全局变量 DB,用于存储初始化后的数据库连接:
var DB *gorm.DB
接下来是两个初始化数据库函数 InitMysqlDB() 和 InitSqliteDB()。
InitMysqlDB() 函数使用 gorm.Open() 方法来连接 MySQL 数据库,并设置一些配置选项。如果连接出错,则输出错误信息并终止程序。然后调用 migrateTables() 函数来创建数据库表格。
InitSqliteDB() 函数与 InitMysqlDB() 类似,不同之处在于它连接的是 SQLite 数据库,并且在初始化完成后调用 insertInitialData() 函数插入一些初始数据。
migrateTables() 函数执行数据库迁移操作,使用 DB.AutoMigrate() 方法将 models.User{} 结构体映射到数据库表中。若迁移失败,则输出错误信息并终止程序。
insertInitialData() 函数用于向数据库插入一些初始数据,数据以 models.User 结构体的形式组织,并使用 DB.Create() 方法将其插入数据库表中。如果插入过程中出错,则输出错误信息并终止程序。
至此,这段代码完成了两个数据库连接的初始化、表格的迁移以及插入初始数据的操作,为后续的数据库操作提供了基础。
业务层
也挺简单的
package services
import (
"errors"
"gorm.io/gorm"
"user-center-go/database"
"user-center-go/models"
)
type UserService interface {
GetUserByID(id uint) (*models.User, error)
GetUserByUsername(username string) (*models.User, error)
}
type userService struct{}
func NewUserService() UserService {
return &userService{}
}
func (s *userService) GetUserByID(id uint) (*models.User, error) {
var user models.User
err := database.DB.First(&user, id).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil // 用户不存在
}
return nil, err
}
return &user, nil
}
func (s *userService) GetUserByUsername(username string) (*models.User, error) {
var user models.User
err := database.DB.Where("username = ?", username).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil // 用户不存在
}
return nil, err
}
return &user, nil
}
表现层
package handlers
import (
"github.com/gin-gonic/gin"
"net/http"
"strconv"
"user-center-go/models"
"user-center-go/services"
)
type UserHandler struct {
userService services.UserService
}
func NewUserHandler(userService services.UserService) *UserHandler {
return &UserHandler{
userService: userService,
}
}
func (h *UserHandler) GetUserByID(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}
user, err := h.userService.GetUserByID(uint(id))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if user == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.JSON(http.StatusOK, user)
}
// 其他接口类似...
这段代码定义了一个 handlers 包,其中包含了一些处理 HTTP 请求的处理器函数。
首先,它导入了一些必要的包:
github.com/gin-gonic/gin:用于构建和处理 HTTP 请求的 Gin 框架。net/http:用于设置 HTTP 响应状态码。strconv:用于将字符串转换为整数。user-center-go/models:用于导入用户模型结构体。user-center-go/services:用于导入用户服务接口。
然后定义了一个 UserHandler 结构体,其中有一个 userService 字段,表示用户服务接口的实例。
接下来是一个 NewUserHandler() 函数,用于创建一个 UserHandler 结构体的实例。该函数接收一个用户服务接口作为参数,并返回一个指向 UserHandler 结构体的指针。
然后是一个 GetUserByID() 方法,用于处理根据用户 ID 获取用户信息的请求。它从 Gin 上下文中获取 ID 参数,并将其转换为无符号的整数。如果转换出错,将返回一个带有错误信息的 HTTP 响应。否则,它调用用户服务接口的 GetUserByID() 方法来获取用户信息。如果出现错误,将返回一个带有错误信息的 HTTP 响应。如果用户不存在,将返回一个带有“User not found”错误信息的 HTTP 响应。最后,如果一切正常,它将以 JSON 格式返回用户信息的 HTTP 响应。
最后,这段代码还注释说明还有其他接口类似的处理函数。
main 函数
func main() {
database.InitSqliteDB()
userService := services.NewUserService()
userHandler := handlers.NewUserHandler(userService)
r := gin.Default()
v1 := r.Group("/api/v1")
{
v1.POST("/users", userHandler.CreateUser)
v1.GET("/users/:id", userHandler.GetUserByID)
// 其他路由...
}
if err := r.Run(":8080"); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
这段代码是一个 Go 语言的主函数 main()。它通过调用 database.InitSqliteDB() 来初始化 SQLite 数据库的连接和数据表,并创建了一个用户服务和用户处理器。
然后,它创建了一个基于 Gin 框架的 HTTP 路由引擎实例 r := gin.Default()。
接下来,它创建了一个新的路由组 /api/v1,其中包含两个路由:
v1.POST("/users", userHandler.CreateUser):当收到 POST 请求/api/v1/users时,调用userHandler.CreateUser方法来处理用户创建操作。v1.GET("/users/:id", userHandler.GetUserByID):当收到 GET 请求/api/v1/users/:id时,调用userHandler.GetUserByID方法来处理根据用户 ID 获取用户信息的操作。
你可以在该路由组中添加其他路由配置。
最后,启动 HTTP 服务器并监听在端口 :8080 上,如果启动失败,则输出错误信息并终止程序。