当前位置: 首页 > article >正文

Go 项目如何集成类似mybatisPlus插件呢?GORM走起!!

导读:

在 Go 项目中,虽然没有像 MyBatis Plus 这样特定的 ORM 插件,但可以使用功能相似的 Go ORM 框架,比如 GORM,它支持链式查询、自动迁移、预加载等功能,与 MyBatis Plus 有相似之处。通过一些插件或扩展,可以实现更丰富的功能,比如软删除、分页查询等。下面是 GORM 集成的一些步骤和相关插件的推荐,本文以之前集成的项目Go语言?IDEA能支持吗?增删查走起?_go idea-CSDN博客为模版在此基础上进行迭代。

目录

 一、引入GORM

二、GORM能力:

2.1分页查询:

2.2软删除

2.3分页拓展

 2.4链式查询

2.5自定义拓展

三、源码改造

3.1改造数据库连接类database.go

3.2改造中间件DBMiddleware

3.3 改造userDao.go

3.4 改造userService.go

3.5 改造router.go

3.6 改造main.go

四、启动

 五、异常情况:

5.1解决思路:

 六、复杂查询拓展:

6.1实体类Orders.go

 6.2新增OrderInfoDTO.go

6.3新增GetUserWithOrders方法:


 一、引入GORM

先在项目中引入 GORM:

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql  # 根据你的数据库类型选择驱动

二、GORM能力:

GORM 提供了一些类似 MyBatis Plus 的功能,比如:

  • 软删除:通过 gorm.Model 或自定义字段来实现软删除。
  • 条件查询:可以通过链式查询构建条件查询。
  • 分页插件:可以手动实现分页,或使用封装的分页插件。

2.1分页查询:

go 一遍集成GORM 的 LimitOffset 方法实现分页查询:

var users []User
db.Limit(10).Offset(20).Find(&users)  // 查询第 21-30 条数据

2.2软删除

例如:通过 gorm.Model 或自定义字段来实现软删除。

type User struct {
    gorm.Model
    Name string
    Age  int
}

db.Delete(&user) // 软删除

2.3分页拓展

可以手动实现分页,或使用封装的分页插件比如分页器 github.com/biezhi/gorm-paginator 

 2.4链式查询

GORM 支持链式调用来构建复杂的查询:

var users []User
db.Where("age > ?", 18).Find(&users)

2.5自定义拓展

如果需要类似 MyBatis Plus 的扩展功能,你可以通过自定义 GORM 的钩子函数(Hooks)或中间件,来实现数据插入、更新时自动添加字段等逻辑。GORM 提供了生命周期回调接口,可以在执行操作前后进行拦截和处理。

三、源码改造

3.1改造数据库连接类database.go

  • 引入 GORM:通过 gorm.io/gormgorm.io/driver/mysql 代替原来的 database/sql

  • 初始化数据库连接:使用 gorm.Open() 方法替代 sql.Open(),并且支持更多的配置参数,比如 charset=utf8mb4parseTime=True

  • GetDB 函数:返回 *gorm.DB 类型的数据库实例,供其他模块使用。

  • 这边删除了原先的func GetDB() *sql.DB { return DB } 方法

package config

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"log"
)

var DB *gorm.DB

// InitGormDatabase 初始化 GORM 数据库连接并返回 *gorm.DB
func InitGormDatabase() *gorm.DB {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		AppConfig.Database.User,
		AppConfig.Database.Password,
		AppConfig.Database.Host,
		AppConfig.Database.Port,
		AppConfig.Database.DBName,
	)

	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})

	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
		return nil
	}

	log.Println("GORM database successfully connected")
	return DB
}

3.2改造中间件DBMiddleware

这边说明一下,中间件的引入是刚搭建项目时想从java中的切面编程来实现跨功能关注点的处理。比如数据库的连接。但是呢go没有内置切面的支持。所以就引入了中间件模式,每次调用数据库操作都通过中间件来处理。下面是优化前的中间件:

package middleware

import (
	"database/sql"
	"go_tas/dao/database"
	"log"
)

type DBMiddleware struct {
	db *sql.DB
}

func NewDBMiddleware() *DBMiddleware {
	return &DBMiddleware{
		db: database.GetDB(),
	}
}

func (mw *DBMiddleware) ExecuteQuery(query string, args ...interface{}) (*sql.Rows, error) {
	rows, err := mw.db.Query(query, args...) // 使用中间件中保存的 db
	if err != nil {
		return nil, mw.logError("Failed to execute query", err)
	}
	return rows, nil
}

func (mw *DBMiddleware) ExecuteExec(query string, args ...interface{}) (int64, error) {
	result, err := mw.db.Exec(query, args...) // 使用中间件中保存的 db
	if err != nil {
		return 0, mw.logError("Failed to execute exec", err)
	}
	insertID, err := result.LastInsertId()
	if err != nil {
		return 0, mw.logError("Failed to get last insert ID", err)
	}
	return insertID, nil
}

func (mw *DBMiddleware) BeginTransaction() (*sql.Tx, error) {
	tx, err := mw.db.Begin() // 使用中间件中保存的 db
	if err != nil {
		return nil, mw.logError("Failed to begin transaction", err)
	}
	return tx, nil
}

func (mw *DBMiddleware) logError(message string, err error) error {
	log.Printf("%s: %v", message, err)
	return err
}

使用原生的 database/sql 改造为使用 GORM,可以直接使用 GORM 的功能来处理查询、事务等操作。GORM 已经封装了这些常见的操作,并且支持更简洁的 API。

下面是改造后的 middleware 模块,使用 GORM 替代 database/sql

  1. gorm.DB 替换 sql.DBDBMiddleware 中的 db 现在使用 *gorm.DB,所有的数据库操作通过 GORM 进行。

  2. ExecuteQuery 方法

    • 使用 GORM 的 Raw 方法执行原生 SQL 查询,并通过 Scan 将结果映射到传入的 model 结构体。
    • 原生 SQL 查询返回的结果可以映射到结构体切片中。
    var users []User
    middleware.ExecuteQuery(&users, "SELECT * FROM users WHERE age > ?", 18)
    

  3. ExecuteExec 方法

    • 使用 GORM 的 Exec 方法执行修改(插入、更新、删除)语句。
    • 返回执行后影响的行数,而不是插入 ID。
  4. 事务管理

    • 使用 GORM 的 Begin 方法开启事务,返回事务对象 *gorm.DB
    tx, err := middleware.BeginTransaction()
    if err != nil {
        // handle error
    }
    // 在事务中执行操作
    err = tx.Create(&user).Error
    if err != nil {
        tx.Rollback()
    } else {
        tx.Commit()
    }
    

    中间件现在可以使用 GORM 的功能,简化数据库操作并支持复杂的 ORM 功能。

package middleware

import (
    "go_tas/dao/database"
    "log"
    "gorm.io/gorm"
)

type DBMiddleware struct {
    db *gorm.DB
}

func NewDBMiddleware() *DBMiddleware {
    return &DBMiddleware{
        db: database.GetDB(), // 使用 GORM 的 DB 实例
    }
}

// ExecuteQuery 执行查询语句
func (mw *DBMiddleware) ExecuteQuery(model interface{}, query string, args ...interface{}) error {
    result := mw.db.Raw(query, args...).Scan(model) // 使用 GORM 的 Raw 方法
    if result.Error != nil {
        return mw.logError("Failed to execute query", result.Error)
    }
    return nil
}

// ExecuteExec 执行修改语句(插入、更新、删除)
func (mw *DBMiddleware) ExecuteExec(query string, args ...interface{}) (int64, error) {
    result := mw.db.Exec(query, args...) // 使用 GORM 的 Exec 方法
    if result.Error != nil {
        return 0, mw.logError("Failed to execute exec", result.Error)
    }
    return result.RowsAffected, nil // 返回影响的行数
}

// BeginTransaction 开启事务
func (mw *DBMiddleware) BeginTransaction() (*gorm.DB, error) {
    tx := mw.db.Begin() // 使用 GORM 的事务支持
    if tx.Error != nil {
        return nil, mw.logError("Failed to begin transaction", tx.Error)
    }
    return tx, nil
}

// logError 记录错误信息
func (mw *DBMiddleware) logError(message string, err error) error {
    log.Printf("%s: %v", message, err)
    return err
}

3.3 改造userDao.go

使用原生 SQL 改造为使用 GORM,可以直接利用 GORM 的模型操作方法来简化数据库操作。GORM 允许直接对结构体进行增删改查,而不需要手动编写 SQL 查询

package dao

import (
    "go_tas/entity"
    "gorm.io/gorm"
    "log"
)

type UserDAO struct {
    db *gorm.DB
}

// NewUserDAO 创建新的 UserDAO 实例
func NewUserDAO(db *gorm.DB) *UserDAO {
    return &UserDAO{db: db}
}

// GetAllUsers 获取所有用户
func (dao *UserDAO) GetAllUsers() ([]entity.User, error) {
    var users []entity.User

    // 使用 GORM 查询所有用户
    if err := dao.db.Find(&users).Error; err != nil {
        log.Printf("Failed to get users: %v", err)
        return nil, err
    }

    return users, nil
}

// CreateUser 创建新用户
func (dao *UserDAO) CreateUser(user *entity.User) (int64, error) {
    // 使用 GORM 创建用户
    if err := dao.db.Create(user).Error; err != nil {
        log.Printf("Failed to create user: %v", err)
        return 0, err
    }

    // 返回创建的用户 ID
    return int64(user.ID), nil
}

3.4 改造userService.go

使用 GORM 替代原生 SQL 操作来进行数据处理。可以将事务处理和数据库操作使用 GORM 的特性进行简化和改造

package service

import (
    "encoding/json"
    "go_tas/dao"
    "go_tas/entity"
    "go_tas/utils"
    "gorm.io/gorm"
    "net/http"
)

// GetUsers 获取所有用户
func GetUsers(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
    userDAO := dao.NewUserDAO(db)
    users, err := userDAO.GetAllUsers()
    if err != nil {
        utils.HandleError(w, http.StatusInternalServerError, "Failed to retrieve users", err)
        return
    }

    respondWithJSON(w, http.StatusOK, users)
}

// CreateUser 创建新用户
func CreateUser(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
    var user entity.User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        utils.HandleError(w, http.StatusBadRequest, "Invalid request payload", err)
        return
    }

    // 开始一个事务
    tx := db.Begin()
    if tx.Error != nil {
        utils.HandleError(w, http.StatusInternalServerError, "Failed to begin transaction", tx.Error)
        return
    }
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback() // 回滚事务
            utils.HandleError(w, http.StatusInternalServerError, "Internal server error", nil)
        }
    }()

    // 插入用户数据
    userDAO := dao.NewUserDAO(tx)
    if _, err := userDAO.CreateUser(&user); err != nil {
        tx.Rollback() // 回滚事务
        utils.HandleError(w, http.StatusInternalServerError, "Failed to create user", err)
        return
    }

    // 提交事务
    if err := tx.Commit().Error; err != nil {
        utils.HandleError(w, http.StatusInternalServerError, "Failed to commit transaction", err)
        return
    }

    respondWithJSON(w, http.StatusCreated, user)
}

// respondWithJSON 处理JSON响应
func respondWithJSON(w http.ResponseWriter, status int, payload interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    if err := json.NewEncoder(w).Encode(payload); err != nil {
        utils.HandleError(w, http.StatusInternalServerError, "Failed to encode response data", err)
    }
}

3.5 改造router.go

router 中的数据库操作从 dbMiddleware 改为使用 GORM 数据库连接,并清理代码逻辑

package router

import (
	"github.com/gorilla/mux"
	httpSwagger "github.com/swaggo/http-swagger"
	"go_tas/controller"
	"go_tas/middleware"
	"go_tas/service"
	"gorm.io/gorm"
	"net/http"
)

func InitRouter(authMiddleware *middleware.CustomAuthMiddleware, db *gorm.DB) *mux.Router {
	r := mux.NewRouter()
	
	// 添加身份验证中间件
	r.Use(authMiddleware.Middleware)
	
	// 使用 GORM 数据库连接
	r.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		service.GetUsers(w, r, db)
	}).Methods("GET")

	r.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		service.CreateUser(w, r, db)
	}).Methods("POST")
	
	// 设备控制接口
	r.HandleFunc("/tas/control", controller.Control).Methods("POST")
	r.HandleFunc("/tas/adminControl", controller.AdminControl).Methods("POST")
	
	// Swagger Handler
	r.PathPrefix("/swagger/").Handler(httpSwagger.WrapHandler)
	
	return r
}

3.6 改造main.go

main 函数中,可以将原本使用的 dbMiddleware 改为直接使用 GORM 初始化的数据库连接,以便与前面改造的代码保持一致

package main

import (
	"go_tas/config"
	"go_tas/logger"
	"go_tas/middleware"
	"go_tas/router"
	"log"
	"net/http"
)

func main() {
	// 初始化配置
	config.InitConfig()
	log.Println("Config initialized")

	// 初始化数据库,获取 GORM 数据库连接
	db := config.InitGormDatabase()
	if db == nil {
		log.Fatal("Failed to initialize database")
	}
	log.Println("Database initialized with GORM")

	// 初始化 Redis
	config.InitRedis()
	log.Println("Redis initialized")

	// 初始化日志
	logger.InitLogger()

	// 初始化认证中间件
	appCode := config.AppConfig.AppCode
	appSecret := config.AppConfig.AppSecret
	authMiddleware := middleware.NewCustomAuthMiddleware(appCode, appSecret)

	// 初始化路由并传递 GORM 数据库连接
	r := router.InitRouter(authMiddleware, db)
	log.Println("authMiddleware initialized")

	// 启动服务器
	log.Println("Starting server on :8080")
	log.Fatal(http.ListenAndServe(":8080", r))
}

四、启动

 五、异常情况:

如果提示Table 'go_data_base.users' doesn't exist这种情况可以查看一下实体类,原因可能是由于查询时表名不匹配造成的。GORM 默认会使用复数形式表名进行查询,因此它在查找 users 表,而你的实际表名是 user

5.1解决思路:

可以通过在 GORM 模型中设置表名

package entity

type User struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

// TableName 指定表名为 user
func (User) TableName() string {
	return "user"
}

 六、复杂查询拓展:

如果涉及多表关联查询则可使用Joins来进行SQL联表查询,下面举个栗子:

6.1实体类Orders.go

package entity

type Orders struct {
	ID          int    `json:"id"`
	UserId      int    `json:"user_id"`
	Amount      int    `json:"amount"`
	Description string `json:"description"`
}

func (Orders) TableName() string {
	return "orders"
}

 6.2新增OrderInfoDTO.go

package dto

type OrderInfoDTO struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	Email       string `json:"email"`
	Amount      int    `json:"amount"`
	Description string `json:"description"`
}

6.3新增GetUserWithOrders方法:

在userDao.go 新增一个GetUserWithOrders方法,Joins 用于执行 SQL 联表查Select("users.*, orders.amount") 指定返回的字段,你可以选择返回哪些表的字段Scan(&users) 将查询结果扫描到 users 切片中。

func (dao *UserDAO) GetUsersWithOrderInfo() ([]dto.OrderInfoDTO, error) {
	var userInfo []dto.OrderInfoDTO

	err := dao.db.Table("user").
		Joins("LEFT JOIN orders ON orders.user_id = user.id").
		Select("user.*, orders.*").
		Scan(&userInfo).Error
	if err != nil {
		log.Printf("Failed to get users with order info: %v", err)
		return nil, err
	}

	return userInfo, nil
}

最后将数据库的语句:

DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NULL DEFAULT NULL COMMENT '用户ID',
  `amount` int(10) NULL DEFAULT NULL COMMENT '支付金额/分',
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

如果需要源码的同学评论区把邮箱放置一下哈!


http://www.kler.cn/news/357596.html

相关文章:

  • vscode配置ssh远程连接服务器
  • 货币兑换计算器(RMB <=> 美元)
  • 炒股VS炒游戏装备,哪个更好做
  • 有关 C#多表查询学习
  • .NET无侵入式对象池解决方案
  • 免费字体二次贩卖;刮刮乐模拟器;小报童 | 生活周刊 #4
  • @KafkaListener注解
  • 使用docker搭建lnmp运行WordPress
  • 2011年国赛高教杯数学建模B题交巡警服务平台的设置与调度解题全过程文档及程序
  • 光纤光学的基本方程
  • 纯血鸿蒙!
  • BI 的前置计算
  • 2023年ICPC亚洲合肥赛区赛 C. Cyclic Substrings
  • 关于Android12以上的闪屏兼容
  • 深度学习速通系列:中文命名实体识别(NER)数据集大全(含链接)
  • Python 基础语法
  • 【C++】deque(空间适配器))
  • pip安装指定版本的tensorflow
  • ChatGPT Plus 升级全攻略
  • 基于SSM+微信小程序的打印室预约管理系统(打印2)