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.ModelName stringAge  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 configimport ("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 errorDB, 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 middlewareimport ("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...) // 使用中间件中保存的 dbif 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...) // 使用中间件中保存的 dbif 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() // 使用中间件中保存的 dbif 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 middlewareimport ("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 daoimport ("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}// 返回创建的用户 IDreturn int64(user.ID), nil
}

3.4 改造userService.go

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

package serviceimport ("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.Userif 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 routerimport ("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 Handlerr.PathPrefix("/swagger/").Handler(httpSwagger.WrapHandler)return r
}

3.6 改造main.go

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

package mainimport ("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")// 初始化 Redisconfig.InitRedis()log.Println("Redis initialized")// 初始化日志logger.InitLogger()// 初始化认证中间件appCode := config.AppConfig.AppCodeappSecret := config.AppConfig.AppSecretauthMiddleware := 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 entitytype 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 entitytype 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 dtotype 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.OrderInfoDTOerr := dao.db.Table("user").Joins("LEFT JOIN orders ON orders.user_id = user.id").Select("user.*, orders.*").Scan(&userInfo).Errorif 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.mzph.cn/news/881872.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang 中的强大 TUI 库 ——tview

在命令行界面下创建丰富的用户交互界面是许多开发者的需求,而 Golang 语言中有一个非常出色的 TUI(文本用户界面)库 ——tview。本文将详细介绍 tview 库,并与其他流行的 TUI 库进行对比,最后进行总结。 一、tview 库介…

【JavaEE】——TCP应答报文机制,超时重传机制

阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 一:TCP协议(面试重点重点) 1:报头长度 2:…

尚硅谷rabbitmq 2024 Federation配置 第60节答疑

rabbitmq联邦队列怎么做 要在 RabbitMQ 中设置联邦队列(Federated Queues),你需要遵循以下步骤。联邦队列允许你在不同的 RabbitMQ 实例之间共享队列,从而实现消息的分布式处理和高可用性。 ### 步骤 1:安装 RabbitMQ…

线性规划:优化决策的数学工具

文章目录 一、引言二、线性规划的基本概念1. 决策变量2. 目标函数3. 约束条件 三、线性规划的数学模型四、线性规划的求解方法1. 图解法2. 单纯形法3. 其他算法 五、线性规划的应用场景1. 生产计划2. 投资组合优化3. 运输问题4. 资源分配 六、总结 一、引言 线性规划&#xff…

Linux学习网络编程学习(TCP和UDP)

文章目录 网络编程主要函数介绍1、socket函数2、bind函数转换端口和IP形式的函数 3、listen函数4、accept函数网络模式(TCP&UDP)1、面向连接的TCP流模式2、UDP用户数据包模式 编写一个简单服务端编程5、connect函数编写一个简单客户端编程 超级客户端…

国产单片机及其特点

国产单片机在近年来取得了显著的发展,不仅在技术上不断突破,还在市场上占据了越来越重要的位置。 主要国产单片机品牌及特点 兆易创新(GD) 主要系列:GD32系列,基于ARM Cortex-M内核。特点:高性能…

PDT 数据集:首个基于无人机的高精密度树木病虫害目标检测数据集

2024-09-24,由中国山东计算机科学中心、北京大学等机构联合创建了Pests and Diseases Tree(PDT)数据集,目的解决农业领域中病虫害检测模型开发中专业数据集缺失的问题。通过集成公共数据和网络数据,进一步推出了Common…

编程题 7-18 二分法求多项式单根【PAT】

文章目录 题目输入格式输出格式输入样例输出样例 题解解题思路完整代码 编程练习题目集目录 题目 二分法求函数根的原理为&#xff1a;如果连续函数 f ( x ) f(x) f(x) 在区间 [ a , b ] [a,b] [a,b] 的两个端点取值异号&#xff0c;即 f ( a ) f ( b ) < 0 f(a)f(b)<…

前端笔记(二):svg图标;git

一、svg图标 原视频 注意&#xff1a;图片是使用的png但是图标是使用的svg&#xff1b; &#xff08;1&#xff09;配置svg ①main.js里引入icons目录 ②调用函数遍历使用svg ③配置symbolId前都加了icon-&#xff0c;所以引用时应该加icon- ④但是使用时没有加因为组件里…

2024.10.15 sql

刷题网站&#xff1a; 牛客网 select device_id as user_infos_example from user_profile where id < 2 select device_id, university from user_profile where university"北京大学" select device_id, gender, age, university from user_profile where ag…

Qt_打开其他软件(带参数)

文章内容: 当打开器他软件并要求其他软件做出响应动作时,我们可以通过传入参数来控制软件打开的方式。 打开软件: if(QString::compare(ui->btn_connect->text(), "断开连接") ==

MySQL学习(六):视图和存储过程以及函数

MySQL学习&#xff08;六&#xff09;&#xff1a;视图和存储过程以及函数 文章目录 MySQL学习&#xff08;六&#xff09;&#xff1a;视图和存储过程以及函数1. 视图1.1 视图的作用与特性1.2 视图的创建1.3 视图的查看与维护1.4 视图的修改与删除 2. 存储过程与函数2.1 存储过…

Windows 添加右键以管理员身份运行 PowerShell

在 Windows 系统中添加一个右键菜单选项&#xff0c;以便可以使用管理员权限打开 PowerShell&#xff0c;可以通过编辑注册表来实现。 打开注册表编辑器&#xff1a; 按 Win R 打开运行对话框。输入 regedit 并按回车&#xff0c;这将打开注册表编辑器。 导航到文件夹背景键&…

计算机毕业设计Django+Vue.js豆瓣图书推荐系统 图书评论情感分析 豆瓣图书可视化大屏 豆瓣图书爬虫 数据分析 图书大数据 大数据毕业设计 机器学习

《DjangoVue.js豆瓣图书推荐系统》开题报告 一、研究背景与意义 1. 研究背景 随着数字化时代的来临&#xff0c;图书资源日益丰富&#xff0c;用户面临着信息过载的问题。如何在海量图书中快速找到符合个人兴趣和需求的书籍成为了亟待解决的问题。传统的图书检索方式往往基于…

javaweb以html方式集成富文本编辑器TinyMce

前言&#xff1a; 单一的批量图片上传按钮&#xff0c;禁用tinymce编辑器&#xff0c;但是还可以操作图片编辑&#xff1b; 多元化格式的富文本编辑要求&#xff1b; 采用tinymce实现。 附&#xff1a; tinymce中文手册网站&#xff1a; http://tinymce.ax-z.cn/download-all.…

空间解析几何 3:空间点到线段和平面的距离【附MATLAB代码】

目录 空间中点到线段的距离 空间中点到平面的投影和距离 matlab代码 空间中点到线段的距离 空间中点到平面的投影和距离 matlab代码 function [dis,P2,t] point2Line (A1,B1,C1) %求空间一点到一线段的最短距离 %[dis,P2,Q2]pointSegmentDistance(A,B,C) %A B为线段首末端…

使用LlamaFactory进行模型微调

使用LlamaFactory进行模型微调 简介 论文地址&#xff1a;https://arxiv.org/pdf/2403.13372 仓库地址&#xff1a;https://github.com/hiyouga/LLaMA-Factory/tree/main 名词解释 1. 预训练 (Pre-training, PT) 预训练是指模型在大规模无监督数据集上进行初步训练的过程…

最强 OpenAI o1 逻辑推理正确率仅 50%!清华、智谱推出「大模型逻辑推理新基准」

9.11 和 9.9 哪个大&#xff1f; 这一连人类幼儿园儿童都能回答的问题&#xff0c;曾经&#xff08;至今&#xff09;难倒了众多大语言模型&#xff08;LLM&#xff09;。 然而&#xff0c;要想达到通用人工智能&#xff08;AGI&#xff09;的水平&#xff0c;LLM 不仅要完成“…

居民小区的有序充电策略及解决方案

0引言 全球经济增长导致化石能源过度消耗&#xff0c;加剧环境污染。电动汽车因其环保优势迅速崛起&#xff0c;预计到2030年中国电动汽车保有量将达到6000万辆。这有助于减少化石能源依赖&#xff0c;推广绿色出行&#xff0c;但也对电网稳定性构成挑战。因此&#xff0c;研究…

2.1 HTML5 - Canvas标签

文章目录 引言Canvas标签概述定义实例&#xff1a;创建画布 理解Canvas坐标系概述实例&#xff1a;获取Canvas坐标 获取Canvas环境上下文概述实例&#xff1a;获取Canvas上下文设置渐变色效果 结语 引言 大家好&#xff0c;今天我们要一起探索HTML5中一个非常有趣且强大的特性…